summaryrefslogtreecommitdiffstats
path: root/doc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 14:53:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 14:53:22 +0000
commit52c021ee0b0c6ad2128ed550c694aad0d11d4c3f (patch)
tree83cf8627b94336cf4bee7479b9749263bbfd3a06 /doc
parentInitial commit. (diff)
downloadisc-kea-52c021ee0b0c6ad2128ed550c694aad0d11d4c3f.tar.xz
isc-kea-52c021ee0b0c6ad2128ed550c694aad0d11d4c3f.zip
Adding upstream version 2.5.7.upstream/2.5.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'doc')
-rw-r--r--doc/Makefile.am129
-rw-r--r--doc/Makefile.in888
-rw-r--r--doc/devel/Doxyfile2850
-rw-r--r--doc/devel/Doxyfile-xml7
-rw-r--r--doc/devel/Makefile.am25
-rw-r--r--doc/devel/Makefile.in566
-rw-r--r--doc/devel/README5
-rw-r--r--doc/devel/bison.dox450
-rw-r--r--doc/devel/config-backend.dox121
-rw-r--r--doc/devel/congestion-handling.dox452
-rw-r--r--doc/devel/contribute.dox16
-rw-r--r--doc/devel/cross-compile.dox226
-rw-r--r--doc/devel/debug.dox26
-rw-r--r--doc/devel/doc.dox86
-rw-r--r--doc/devel/fuzz.dox303
-rw-r--r--doc/devel/mainpage.dox160
-rw-r--r--doc/devel/performance.dox15
-rw-r--r--doc/devel/qa.dox52
-rw-r--r--doc/devel/terminology.dox28
-rw-r--r--doc/devel/unit-tests.dox613
-rw-r--r--doc/examples/agent/comments.json58
-rw-r--r--doc/examples/agent/https.json32
-rw-r--r--doc/examples/agent/rbac.json90
-rw-r--r--doc/examples/agent/simple.json150
-rw-r--r--doc/examples/ddns/all-keys-netconf.json183
-rw-r--r--doc/examples/ddns/all-keys.json183
-rw-r--r--doc/examples/ddns/comments.json64
-rw-r--r--doc/examples/ddns/gss-tsig.json127
-rw-r--r--doc/examples/ddns/sample1.json172
-rw-r--r--doc/examples/ddns/template.json113
-rw-r--r--doc/examples/https/httpd2/kea-httpd2.conf129
-rw-r--r--doc/examples/https/nginx/kea-nginx.conf88
-rw-r--r--doc/examples/https/shell/kea-stunnel.conf46
-rw-r--r--doc/examples/kea4/advanced.json208
-rw-r--r--doc/examples/kea4/all-keys-netconf.json1245
-rw-r--r--doc/examples/kea4/all-keys.json1277
-rw-r--r--doc/examples/kea4/all-options.json1889
-rw-r--r--doc/examples/kea4/backends.json107
-rw-r--r--doc/examples/kea4/classify.json147
-rw-r--r--doc/examples/kea4/classify2.json180
-rw-r--r--doc/examples/kea4/comments.json113
-rw-r--r--doc/examples/kea4/config-backend.json91
-rw-r--r--doc/examples/kea4/dhcpv4-over-dhcpv6.json48
-rw-r--r--doc/examples/kea4/global-reservations.json178
-rw-r--r--doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json284
-rw-r--r--doc/examples/kea4/ha-load-balancing-server2-mt.json267
-rw-r--r--doc/examples/kea4/hooks-radius.json224
-rw-r--r--doc/examples/kea4/hooks.json50
-rw-r--r--doc/examples/kea4/leases-expiration.json76
-rw-r--r--doc/examples/kea4/multiple-options.json187
-rw-r--r--doc/examples/kea4/mysql-reservations.json103
-rw-r--r--doc/examples/kea4/pgsql-reservations.json101
-rw-r--r--doc/examples/kea4/reservations.json183
-rw-r--r--doc/examples/kea4/several-subnets.json86
-rw-r--r--doc/examples/kea4/shared-network.json164
-rw-r--r--doc/examples/kea4/single-subnet.json60
-rw-r--r--doc/examples/kea4/vendor-specific.json96
-rw-r--r--doc/examples/kea4/vivso.json90
-rw-r--r--doc/examples/kea4/with-ddns.json85
-rw-r--r--doc/examples/kea6/advanced.json189
-rw-r--r--doc/examples/kea6/all-keys-netconf.json1215
-rw-r--r--doc/examples/kea6/all-keys.json1247
-rw-r--r--doc/examples/kea6/all-options.json2179
-rw-r--r--doc/examples/kea6/backends.json109
-rw-r--r--doc/examples/kea6/classify.json113
-rw-r--r--doc/examples/kea6/classify2.json150
-rw-r--r--doc/examples/kea6/comments.json123
-rw-r--r--doc/examples/kea6/config-backend.json92
-rw-r--r--doc/examples/kea6/dhcpv4-over-dhcpv6.json58
-rw-r--r--doc/examples/kea6/duid.json80
-rw-r--r--doc/examples/kea6/global-reservations.json174
-rw-r--r--doc/examples/kea6/ha-hot-standby-server1-with-tls.json169
-rw-r--r--doc/examples/kea6/ha-hot-standby-server2.json160
-rw-r--r--doc/examples/kea6/hooks.json58
-rw-r--r--doc/examples/kea6/iPXE.json68
-rw-r--r--doc/examples/kea6/leases-expiration.json85
-rw-r--r--doc/examples/kea6/multiple-options.json184
-rw-r--r--doc/examples/kea6/mysql-reservations.json101
-rw-r--r--doc/examples/kea6/pgsql-reservations.json98
-rw-r--r--doc/examples/kea6/reservations.json171
-rw-r--r--doc/examples/kea6/several-subnets.json61
-rw-r--r--doc/examples/kea6/shared-network.json153
-rw-r--r--doc/examples/kea6/simple.json63
-rw-r--r--doc/examples/kea6/softwire46.json90
-rw-r--r--doc/examples/kea6/stateless.json29
-rw-r--r--doc/examples/kea6/tee-times.json73
-rw-r--r--doc/examples/kea6/with-ddns.json89
-rw-r--r--doc/examples/netconf/comments.json36
-rw-r--r--doc/examples/netconf/kea-dhcp6-operations/boot.json8
-rw-r--r--doc/examples/netconf/kea-dhcp6-operations/logging.xml26
-rw-r--r--doc/examples/netconf/kea-dhcp6-operations/netconf.json31
-rw-r--r--doc/examples/netconf/kea-dhcp6-operations/startup.xml18
-rw-r--r--doc/examples/netconf/kea-dhcp6-operations/twopools.xml23
-rw-r--r--doc/examples/netconf/kea-dhcp6-operations/twosubnets.xml27
-rw-r--r--doc/examples/netconf/simple-dhcp4.json119
-rw-r--r--doc/examples/netconf/simple-dhcp6.json121
-rw-r--r--doc/examples/template-ha-mt-tls/info.md87
-rw-r--r--doc/examples/template-ha-mt-tls/kea-ca-1.conf90
-rw-r--r--doc/examples/template-ha-mt-tls/kea-ca-2.conf90
-rw-r--r--doc/examples/template-ha-mt-tls/kea-dhcp4-1.conf238
-rw-r--r--doc/examples/template-ha-mt-tls/kea-dhcp4-2.conf238
-rw-r--r--doc/examples/template-power-user-home/info.md124
-rw-r--r--doc/examples/template-power-user-home/kea-ca-1.conf66
-rw-r--r--doc/examples/template-power-user-home/kea-ca-2.conf66
-rw-r--r--doc/examples/template-power-user-home/kea-dhcp4-1.conf227
-rw-r--r--doc/examples/template-power-user-home/kea-dhcp4-2.conf227
-rw-r--r--doc/images/kea-logo-100x70.pngbin0 -> 9978 bytes
-rw-r--r--doc/sphinx/Makefile.am257
-rw-r--r--doc/sphinx/Makefile.in1015
-rw-r--r--doc/sphinx/_build/man/kea-admin.8185
-rw-r--r--doc/sphinx/_build/man/kea-ctrl-agent.8113
-rw-r--r--doc/sphinx/_build/man/kea-dhcp-ddns.8113
-rw-r--r--doc/sphinx/_build/man/kea-dhcp4.8128
-rw-r--r--doc/sphinx/_build/man/kea-dhcp6.8128
-rw-r--r--doc/sphinx/_build/man/kea-lfc.8144
-rw-r--r--doc/sphinx/_build/man/kea-netconf.8108
-rw-r--r--doc/sphinx/_build/man/kea-shell.8140
-rw-r--r--doc/sphinx/_build/man/keactrl.8143
-rw-r--r--doc/sphinx/_build/man/perfdhcp.8596
-rw-r--r--doc/sphinx/api-files.txt197
-rwxr-xr-xdoc/sphinx/api2doc.py209
-rw-r--r--doc/sphinx/arm/acknowledgments.rst29
-rw-r--r--doc/sphinx/arm/admin.rst678
-rw-r--r--doc/sphinx/arm/agent.rst314
-rw-r--r--doc/sphinx/arm/classify.rst1265
-rw-r--r--doc/sphinx/arm/config-backend.rst327
-rw-r--r--doc/sphinx/arm/config-templates.rst74
-rw-r--r--doc/sphinx/arm/config.rst339
-rw-r--r--doc/sphinx/arm/congestion-handling.rst129
-rw-r--r--doc/sphinx/arm/ctrl-channel.rst906
-rw-r--r--doc/sphinx/arm/database-connectivity.rst104
-rw-r--r--doc/sphinx/arm/ddns.rst1027
-rw-r--r--doc/sphinx/arm/dhcp4-srv.rst8455
-rw-r--r--doc/sphinx/arm/dhcp6-srv.rst8184
-rw-r--r--doc/sphinx/arm/ext-gss-tsig.rst1303
-rw-r--r--doc/sphinx/arm/ext-netconf.rst1159
-rw-r--r--doc/sphinx/arm/ext-radius.rst559
-rw-r--r--doc/sphinx/arm/hammer.rst137
-rw-r--r--doc/sphinx/arm/hooks-bootp.rst91
-rw-r--r--doc/sphinx/arm/hooks-cb-cmds.rst2248
-rw-r--r--doc/sphinx/arm/hooks-cb-mysql.rst18
-rw-r--r--doc/sphinx/arm/hooks-cb-pgsql.rst18
-rw-r--r--doc/sphinx/arm/hooks-class-cmds.rst255
-rw-r--r--doc/sphinx/arm/hooks-ddns-tuning.rst217
-rw-r--r--doc/sphinx/arm/hooks-flex-id.rst286
-rw-r--r--doc/sphinx/arm/hooks-flex-option.rst196
-rw-r--r--doc/sphinx/arm/hooks-gss-tsig.rst15
-rw-r--r--doc/sphinx/arm/hooks-ha.rst2662
-rw-r--r--doc/sphinx/arm/hooks-host-cache.rst324
-rw-r--r--doc/sphinx/arm/hooks-host-cmds.rst1212
-rw-r--r--doc/sphinx/arm/hooks-lease-cmds.rst1073
-rw-r--r--doc/sphinx/arm/hooks-lease-query.rst675
-rw-r--r--doc/sphinx/arm/hooks-legal-log.rst1094
-rw-r--r--doc/sphinx/arm/hooks-limits.rst211
-rw-r--r--doc/sphinx/arm/hooks-perfmon.rst39
-rw-r--r--doc/sphinx/arm/hooks-ping-check.rst169
-rw-r--r--doc/sphinx/arm/hooks-radius.rst16
-rw-r--r--doc/sphinx/arm/hooks-rbac.rst587
-rw-r--r--doc/sphinx/arm/hooks-run-script.rst626
-rw-r--r--doc/sphinx/arm/hooks-stat-cmds.rst252
-rw-r--r--doc/sphinx/arm/hooks-subnet-cmds.rst1326
-rw-r--r--doc/sphinx/arm/hooks-user-chk.rst83
-rw-r--r--doc/sphinx/arm/hooks.rst626
-rw-r--r--doc/sphinx/arm/install.rst633
-rw-r--r--doc/sphinx/arm/integrations.rst11
-rw-r--r--doc/sphinx/arm/intro.rst64
-rw-r--r--doc/sphinx/arm/keactrl.rst364
-rw-r--r--doc/sphinx/arm/lease-expiration.rst328
-rw-r--r--doc/sphinx/arm/lfc.rst73
-rw-r--r--doc/sphinx/arm/logging.rst1077
-rw-r--r--doc/sphinx/arm/quickstart.rst382
-rw-r--r--doc/sphinx/arm/rst_arm_sources.mk54
-rw-r--r--doc/sphinx/arm/security.rst445
-rw-r--r--doc/sphinx/arm/shell.rst165
-rw-r--r--doc/sphinx/arm/stats.rst1043
-rw-r--r--doc/sphinx/arm/stork.rst50
-rw-r--r--doc/sphinx/conf.py307
-rw-r--r--doc/sphinx/grammar/grammar-ca-parser.rst253
-rw-r--r--doc/sphinx/grammar/grammar-d2-parser.rst310
-rw-r--r--doc/sphinx/grammar/grammar-dhcp4-parser.rst1056
-rw-r--r--doc/sphinx/grammar/grammar-dhcp6-parser.rst1098
-rw-r--r--doc/sphinx/grammar/grammar-netconf-parser.rst221
-rw-r--r--doc/sphinx/grammar/grammar.rst44
-rw-r--r--doc/sphinx/index.rst62
-rw-r--r--doc/sphinx/man/kea-admin.8.rst169
-rw-r--r--doc/sphinx/man/kea-ctrl-agent.8.rst101
-rw-r--r--doc/sphinx/man/kea-dhcp-ddns.8.rst101
-rw-r--r--doc/sphinx/man/kea-dhcp4.8.rst116
-rw-r--r--doc/sphinx/man/kea-dhcp6.8.rst116
-rw-r--r--doc/sphinx/man/kea-lfc.8.rst132
-rw-r--r--doc/sphinx/man/kea-netconf.8.rst96
-rw-r--r--doc/sphinx/man/kea-shell.8.rst129
-rw-r--r--doc/sphinx/man/keactrl.8.rst127
-rw-r--r--doc/sphinx/man/man8s.mk10
-rw-r--r--doc/sphinx/man/perfdhcp.8.rst564
-rw-r--r--doc/sphinx/man/rst_man_sources.mk10
-rw-r--r--doc/sphinx/manpages.rst28
-rw-r--r--doc/sphinx/mes-files.txt29
-rwxr-xr-xdoc/sphinx/mes2doc.py135
-rw-r--r--doc/sphinx/mes_files.mk29
-rw-r--r--doc/sphinx/static/kea-imageonly-100bw.pngbin0 -> 9560 bytes
-rw-r--r--doc/sphinx/static/kea-logo-100x70.pngbin0 -> 9978 bytes
-rw-r--r--doc/sphinx/static/kea-logo-200.pngbin0 -> 18795 bytes
-rw-r--r--doc/sphinx/static/kea.css26
-rw-r--r--doc/sphinx/uml/appendRequestedOptions.pngbin0 -> 50399 bytes
-rw-r--r--doc/sphinx/uml/appendRequestedOptions.svg112
-rw-r--r--doc/sphinx/uml/appendRequestedOptions.uml31
-rw-r--r--doc/sphinx/uml/appendRequestedVendorOptions.pngbin0 -> 93787 bytes
-rw-r--r--doc/sphinx/uml/appendRequestedVendorOptions.svg196
-rw-r--r--doc/sphinx/uml/appendRequestedVendorOptions.uml53
-rw-r--r--doc/sphinx/uml/assign-lease4.pngbin0 -> 112935 bytes
-rw-r--r--doc/sphinx/uml/assign-lease4.svg238
-rw-r--r--doc/sphinx/uml/assign-lease4.uml64
-rw-r--r--doc/sphinx/uml/buildCfgOptionList.pngbin0 -> 87919 bytes
-rw-r--r--doc/sphinx/uml/buildCfgOptionList.svg178
-rw-r--r--doc/sphinx/uml/buildCfgOptionList.uml52
-rw-r--r--doc/sphinx/uml/currentHost4.pngbin0 -> 146644 bytes
-rw-r--r--doc/sphinx/uml/currentHost4.svg301
-rw-r--r--doc/sphinx/uml/currentHost4.uml83
-rw-r--r--doc/sphinx/uml/lease-states.pngbin0 -> 51470 bytes
-rw-r--r--doc/sphinx/uml/lease-states.svg116
-rw-r--r--doc/sphinx/uml/lease-states.uml38
-rw-r--r--doc/sphinx/uml/main-loop.pngbin0 -> 44502 bytes
-rw-r--r--doc/sphinx/uml/main-loop.svg162
-rw-r--r--doc/sphinx/uml/main-loop.uml60
-rw-r--r--doc/sphinx/uml/option-data-priority.atxt53
-rw-r--r--doc/sphinx/uml/packet4.pngbin0 -> 220586 bytes
-rw-r--r--doc/sphinx/uml/packet4.svg355
-rw-r--r--doc/sphinx/uml/packet4.uml92
-rw-r--r--doc/sphinx/uml/priority-of-lease-lifetimes-and-dhcpv4-fields.atxt38
-rw-r--r--doc/sphinx/uml/radius.pngbin0 -> 156032 bytes
-rw-r--r--doc/sphinx/uml/radius.svg15
-rw-r--r--doc/sphinx/uml/radius.uml91
-rw-r--r--doc/sphinx/uml/recognizing-same-client.pngbin0 -> 86635 bytes
-rw-r--r--doc/sphinx/uml/recognizing-same-client.svg220
-rw-r--r--doc/sphinx/uml/recognizing-same-client.uml113
-rw-r--r--doc/sphinx/uml/request4-lease.pngbin0 -> 278529 bytes
-rw-r--r--doc/sphinx/uml/request4-lease.svg437
-rw-r--r--doc/sphinx/uml/request4-lease.uml118
-rw-r--r--doc/sphinx/uml/request4.pngbin0 -> 89991 bytes
-rw-r--r--doc/sphinx/uml/request4.svg213
-rw-r--r--doc/sphinx/uml/request4.uml59
-rw-r--r--doc/sphinx/uml/requestLease4.pngbin0 -> 248999 bytes
-rw-r--r--doc/sphinx/uml/requestLease4.svg540
-rw-r--r--doc/sphinx/uml/requestLease4.uml150
-rw-r--r--doc/sphinx/uml/select4.pngbin0 -> 179451 bytes
-rw-r--r--doc/sphinx/uml/select4.svg345
-rw-r--r--doc/sphinx/uml/select4.uml85
-rw-r--r--doc/sphinx/uml/tkey.atxt13
-rw-r--r--doc/sphinx/uml/tkey.pngbin0 -> 11097 bytes
-rw-r--r--doc/sphinx/uml/tkey.svg24
-rw-r--r--doc/sphinx/uml/tkey.uml11
-rw-r--r--doc/sphinx/uml/update.atxt13
-rw-r--r--doc/sphinx/uml/update.pngbin0 -> 13781 bytes
-rw-r--r--doc/sphinx/uml/update.svg24
-rw-r--r--doc/sphinx/uml/update.uml11
-rw-r--r--doc/sphinx/umls.rst159
257 files changed, 82840 insertions, 0 deletions
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..18b3b59
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,129 @@
+SUBDIRS = sphinx devel
+
+EXTRA_DIST = images/kea-logo-100x70.png
+
+nobase_dist_doc_DATA = examples/agent/comments.json
+nobase_dist_doc_DATA += examples/agent/https.json
+nobase_dist_doc_DATA += examples/agent/rbac.json
+nobase_dist_doc_DATA += examples/agent/simple.json
+nobase_dist_doc_DATA += examples/ddns/all-keys.json
+nobase_dist_doc_DATA += examples/ddns/all-keys-netconf.json
+nobase_dist_doc_DATA += examples/ddns/comments.json
+nobase_dist_doc_DATA += examples/ddns/gss-tsig.json
+nobase_dist_doc_DATA += examples/ddns/sample1.json
+nobase_dist_doc_DATA += examples/ddns/template.json
+nobase_dist_doc_DATA += examples/https/httpd2/kea-httpd2.conf
+nobase_dist_doc_DATA += examples/https/nginx/kea-nginx.conf
+nobase_dist_doc_DATA += examples/https/shell/kea-stunnel.conf
+nobase_dist_doc_DATA += examples/kea4/advanced.json
+nobase_dist_doc_DATA += examples/kea4/all-keys.json
+nobase_dist_doc_DATA += examples/kea4/all-keys-netconf.json
+nobase_dist_doc_DATA += examples/kea4/all-options.json
+nobase_dist_doc_DATA += examples/kea4/backends.json
+nobase_dist_doc_DATA += examples/kea4/classify.json
+nobase_dist_doc_DATA += examples/kea4/classify2.json
+nobase_dist_doc_DATA += examples/kea4/comments.json
+nobase_dist_doc_DATA += examples/kea4/config-backend.json
+nobase_dist_doc_DATA += examples/kea4/dhcpv4-over-dhcpv6.json
+nobase_dist_doc_DATA += examples/kea4/global-reservations.json
+nobase_dist_doc_DATA += examples/kea4/ha-load-balancing-server1-mt-with-tls.json
+nobase_dist_doc_DATA += examples/kea4/ha-load-balancing-server2-mt.json
+nobase_dist_doc_DATA += examples/kea4/hooks.json
+nobase_dist_doc_DATA += examples/kea4/hooks-radius.json
+nobase_dist_doc_DATA += examples/kea4/leases-expiration.json
+nobase_dist_doc_DATA += examples/kea4/multiple-options.json
+nobase_dist_doc_DATA += examples/kea4/mysql-reservations.json
+nobase_dist_doc_DATA += examples/kea4/pgsql-reservations.json
+nobase_dist_doc_DATA += examples/kea4/reservations.json
+nobase_dist_doc_DATA += examples/kea4/several-subnets.json
+nobase_dist_doc_DATA += examples/kea4/shared-network.json
+nobase_dist_doc_DATA += examples/kea4/single-subnet.json
+nobase_dist_doc_DATA += examples/kea4/with-ddns.json
+nobase_dist_doc_DATA += examples/kea4/vivso.json
+nobase_dist_doc_DATA += examples/kea4/vendor-specific.json
+nobase_dist_doc_DATA += examples/kea6/advanced.json
+nobase_dist_doc_DATA += examples/kea6/all-keys.json
+nobase_dist_doc_DATA += examples/kea6/all-keys-netconf.json
+nobase_dist_doc_DATA += examples/kea6/all-options.json
+nobase_dist_doc_DATA += examples/kea6/backends.json
+nobase_dist_doc_DATA += examples/kea6/classify.json
+nobase_dist_doc_DATA += examples/kea6/classify2.json
+nobase_dist_doc_DATA += examples/kea6/comments.json
+nobase_dist_doc_DATA += examples/kea6/config-backend.json
+nobase_dist_doc_DATA += examples/kea6/dhcpv4-over-dhcpv6.json
+nobase_dist_doc_DATA += examples/kea6/duid.json
+nobase_dist_doc_DATA += examples/kea6/global-reservations.json
+nobase_dist_doc_DATA += examples/kea6/ha-hot-standby-server1-with-tls.json
+nobase_dist_doc_DATA += examples/kea6/ha-hot-standby-server2.json
+nobase_dist_doc_DATA += examples/kea6/hooks.json
+nobase_dist_doc_DATA += examples/kea6/iPXE.json
+nobase_dist_doc_DATA += examples/kea6/leases-expiration.json
+nobase_dist_doc_DATA += examples/kea6/multiple-options.json
+nobase_dist_doc_DATA += examples/kea6/mysql-reservations.json
+nobase_dist_doc_DATA += examples/kea6/pgsql-reservations.json
+nobase_dist_doc_DATA += examples/kea6/reservations.json
+nobase_dist_doc_DATA += examples/kea6/several-subnets.json
+nobase_dist_doc_DATA += examples/kea6/shared-network.json
+nobase_dist_doc_DATA += examples/kea6/simple.json
+nobase_dist_doc_DATA += examples/kea6/softwire46.json
+nobase_dist_doc_DATA += examples/kea6/stateless.json
+nobase_dist_doc_DATA += examples/kea6/tee-times.json
+nobase_dist_doc_DATA += examples/kea6/with-ddns.json
+nobase_dist_doc_DATA += examples/netconf/kea-dhcp6-operations/boot.json
+nobase_dist_doc_DATA += examples/netconf/kea-dhcp6-operations/logging.xml
+nobase_dist_doc_DATA += examples/netconf/kea-dhcp6-operations/netconf.json
+nobase_dist_doc_DATA += examples/netconf/kea-dhcp6-operations/startup.xml
+nobase_dist_doc_DATA += examples/netconf/kea-dhcp6-operations/twopools.xml
+nobase_dist_doc_DATA += examples/netconf/kea-dhcp6-operations/twosubnets.xml
+nobase_dist_doc_DATA += examples/netconf/comments.json
+nobase_dist_doc_DATA += examples/netconf/simple-dhcp4.json
+nobase_dist_doc_DATA += examples/netconf/simple-dhcp6.json
+
+nobase_dist_doc_DATA += examples/template-power-user-home/info.md
+nobase_dist_doc_DATA += examples/template-power-user-home/kea-ca-1.conf
+nobase_dist_doc_DATA += examples/template-power-user-home/kea-ca-2.conf
+nobase_dist_doc_DATA += examples/template-power-user-home/kea-dhcp4-1.conf
+nobase_dist_doc_DATA += examples/template-power-user-home/kea-dhcp4-2.conf
+
+nobase_dist_doc_DATA += examples/template-ha-mt-tls/info.md
+nobase_dist_doc_DATA += examples/template-ha-mt-tls/kea-ca-1.conf
+nobase_dist_doc_DATA += examples/template-ha-mt-tls/kea-ca-2.conf
+nobase_dist_doc_DATA += examples/template-ha-mt-tls/kea-dhcp4-1.conf
+nobase_dist_doc_DATA += examples/template-ha-mt-tls/kea-dhcp4-2.conf
+
+# If there's any new parameter added for any grammar in one of the daemons, someone should go through
+# this procedure:
+#
+# 1. autoreconf -i && ./configure --enable-generate-docs --enable-generate-parser
+# 2. cd doc
+# 3. make grammar
+# 4. make -C sphinx html
+# 5. Inspect the html output and make sure it's ok.
+# 6. Review changes in doc/sphinx/grammar/*
+# 7. Check in changed files in doc/sphinx/grammar/*
+#
+# Make sure you commit only relevant changes, skip the timestamp only updates.
+grammar:
+if GENERATE_DOCS
+if GENERATE_PARSER
+ mkdir -p $(abs_top_srcdir)/doc/sphinx/grammar
+ $(abs_top_srcdir)/tools/extract_bnf.sh $(abs_top_srcdir)/src/bin/dhcp4/dhcp4_parser \
+ --markdown ':ref:`dhcp4`' > $(abs_top_srcdir)/doc/sphinx/grammar/grammar-dhcp4-parser.rst
+ $(abs_top_srcdir)/tools/extract_bnf.sh $(abs_top_srcdir)/src/bin/dhcp6/dhcp6_parser \
+ --markdown ':ref:`dhcp6`' > $(abs_top_srcdir)/doc/sphinx/grammar/grammar-dhcp6-parser.rst
+ $(abs_top_srcdir)/tools/extract_bnf.sh $(abs_top_srcdir)/src/bin/d2/d2_parser \
+ --markdown ':ref:`dhcp-ddns-server`' > $(abs_top_srcdir)/doc/sphinx/grammar/grammar-d2-parser.rst
+ $(abs_top_srcdir)/tools/extract_bnf.sh $(abs_top_srcdir)/src/bin/agent/agent_parser \
+ --markdown ':ref:`kea-ctrl-agent`' > $(abs_top_srcdir)/doc/sphinx/grammar/grammar-ca-parser.rst
+ $(abs_top_srcdir)/tools/extract_bnf.sh $(abs_top_srcdir)/src/bin/netconf/netconf_parser \
+ --markdown ':ref:`netconf`' > $(abs_top_srcdir)/doc/sphinx/grammar/grammar-netconf-parser.rst
+else
+ @echo "ERROR: You need to enable both docs (--enable-generate-docs) and parser (--enable-generate-parser)"
+ @echo "ERROR: to regenerate grammar documentation."
+ false
+endif
+else
+ @echo "ERROR: You need to enable both docs (--enable-generate-docs) and parser (--enable-generate-parser)"
+ @echo "ERROR: to regenerate grammar documentation."
+ false
+endif
diff --git a/doc/Makefile.in b/doc/Makefile.in
new file mode 100644
index 0000000..d7d855e
--- /dev/null
+++ b/doc/Makefile.in
@@ -0,0 +1,888 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = doc
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \
+ $(top_srcdir)/m4macros/ax_cpp14.m4 \
+ $(top_srcdir)/m4macros/ax_cpp20.m4 \
+ $(top_srcdir)/m4macros/ax_crypto.m4 \
+ $(top_srcdir)/m4macros/ax_find_library.m4 \
+ $(top_srcdir)/m4macros/ax_gssapi.m4 \
+ $(top_srcdir)/m4macros/ax_gtest.m4 \
+ $(top_srcdir)/m4macros/ax_isc_rpath.m4 \
+ $(top_srcdir)/m4macros/ax_netconf.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(nobase_dist_doc_DATA) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(docdir)"
+DATA = $(nobase_dist_doc_DATA)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_GSSAPI_CONFIGURE_FLAG = @DISTCHECK_GSSAPI_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG = @DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG@
+DISTCHECK_LIBYANG_CONFIGURE_FLAG = @DISTCHECK_LIBYANG_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_MYSQL_CONFIGURE_FLAG = @DISTCHECK_MYSQL_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PGSQL_CONFIGURE_FLAG = @DISTCHECK_PGSQL_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@
+DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG = @DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG@
+DISTCHECK_SYSREPO_CONFIGURE_FLAG = @DISTCHECK_SYSREPO_CONFIGURE_FLAG@
+DLLTOOL = @DLLTOOL@
+DPKG = @DPKG@
+DPKGQUERY = @DPKGQUERY@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+GENHTML = @GENHTML@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GTEST_CONFIG = @GTEST_CONFIG@
+GTEST_INCLUDES = @GTEST_INCLUDES@
+GTEST_LDADD = @GTEST_LDADD@
+GTEST_LDFLAGS = @GTEST_LDFLAGS@
+GTEST_SOURCE = @GTEST_SOURCE@
+HAVE_NETCONF = @HAVE_NETCONF@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KEA_CXXFLAGS = @KEA_CXXFLAGS@
+KEA_SRCID = @KEA_SRCID@
+KRB5_CONFIG = @KRB5_CONFIG@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBYANGCPP_CPPFLAGS = @LIBYANGCPP_CPPFLAGS@
+LIBYANGCPP_INCLUDEDIR = @LIBYANGCPP_INCLUDEDIR@
+LIBYANGCPP_LIBS = @LIBYANGCPP_LIBS@
+LIBYANGCPP_PREFIX = @LIBYANGCPP_PREFIX@
+LIBYANGCPP_VERSION = @LIBYANGCPP_VERSION@
+LIBYANG_CPPFLAGS = @LIBYANG_CPPFLAGS@
+LIBYANG_INCLUDEDIR = @LIBYANG_INCLUDEDIR@
+LIBYANG_LIBS = @LIBYANG_LIBS@
+LIBYANG_PREFIX = @LIBYANG_PREFIX@
+LIBYANG_VERSION = @LIBYANG_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@
+LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LIBS = @MYSQL_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PDFLATEX = @PDFLATEX@
+PERL = @PERL@
+PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PKGPYTHONDIR = @PKGPYTHONDIR@
+PKG_CONFIG = @PKG_CONFIG@
+PLANTUML = @PLANTUML@
+PREMIUM_DIR = @PREMIUM_DIR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SEP = @SEP@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@
+SR_PLUGINS_PATH = @SR_PLUGINS_PATH@
+SR_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_CPPFLAGS = @SYSREPOCPP_CPPFLAGS@
+SYSREPOCPP_INCLUDEDIR = @SYSREPOCPP_INCLUDEDIR@
+SYSREPOCPP_LIBS = @SYSREPOCPP_LIBS@
+SYSREPOCPP_PREFIX = @SYSREPOCPP_PREFIX@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+SYSREPO_PREFIX = @SYSREPO_PREFIX@
+SYSREPO_VERSION = @SYSREPO_VERSION@
+USE_LCOV = @USE_LCOV@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@
+XMLLINT = @XMLLINT@
+YACC = @YACC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = sphinx devel
+EXTRA_DIST = images/kea-logo-100x70.png
+nobase_dist_doc_DATA = examples/agent/comments.json \
+ examples/agent/https.json examples/agent/rbac.json \
+ examples/agent/simple.json examples/ddns/all-keys.json \
+ examples/ddns/all-keys-netconf.json \
+ examples/ddns/comments.json examples/ddns/gss-tsig.json \
+ examples/ddns/sample1.json examples/ddns/template.json \
+ examples/https/httpd2/kea-httpd2.conf \
+ examples/https/nginx/kea-nginx.conf \
+ examples/https/shell/kea-stunnel.conf \
+ examples/kea4/advanced.json examples/kea4/all-keys.json \
+ examples/kea4/all-keys-netconf.json \
+ examples/kea4/all-options.json examples/kea4/backends.json \
+ examples/kea4/classify.json examples/kea4/classify2.json \
+ examples/kea4/comments.json examples/kea4/config-backend.json \
+ examples/kea4/dhcpv4-over-dhcpv6.json \
+ examples/kea4/global-reservations.json \
+ examples/kea4/ha-load-balancing-server1-mt-with-tls.json \
+ examples/kea4/ha-load-balancing-server2-mt.json \
+ examples/kea4/hooks.json examples/kea4/hooks-radius.json \
+ examples/kea4/leases-expiration.json \
+ examples/kea4/multiple-options.json \
+ examples/kea4/mysql-reservations.json \
+ examples/kea4/pgsql-reservations.json \
+ examples/kea4/reservations.json \
+ examples/kea4/several-subnets.json \
+ examples/kea4/shared-network.json \
+ examples/kea4/single-subnet.json examples/kea4/with-ddns.json \
+ examples/kea4/vivso.json examples/kea4/vendor-specific.json \
+ examples/kea6/advanced.json examples/kea6/all-keys.json \
+ examples/kea6/all-keys-netconf.json \
+ examples/kea6/all-options.json examples/kea6/backends.json \
+ examples/kea6/classify.json examples/kea6/classify2.json \
+ examples/kea6/comments.json examples/kea6/config-backend.json \
+ examples/kea6/dhcpv4-over-dhcpv6.json examples/kea6/duid.json \
+ examples/kea6/global-reservations.json \
+ examples/kea6/ha-hot-standby-server1-with-tls.json \
+ examples/kea6/ha-hot-standby-server2.json \
+ examples/kea6/hooks.json examples/kea6/iPXE.json \
+ examples/kea6/leases-expiration.json \
+ examples/kea6/multiple-options.json \
+ examples/kea6/mysql-reservations.json \
+ examples/kea6/pgsql-reservations.json \
+ examples/kea6/reservations.json \
+ examples/kea6/several-subnets.json \
+ examples/kea6/shared-network.json examples/kea6/simple.json \
+ examples/kea6/softwire46.json examples/kea6/stateless.json \
+ examples/kea6/tee-times.json examples/kea6/with-ddns.json \
+ examples/netconf/kea-dhcp6-operations/boot.json \
+ examples/netconf/kea-dhcp6-operations/logging.xml \
+ examples/netconf/kea-dhcp6-operations/netconf.json \
+ examples/netconf/kea-dhcp6-operations/startup.xml \
+ examples/netconf/kea-dhcp6-operations/twopools.xml \
+ examples/netconf/kea-dhcp6-operations/twosubnets.xml \
+ examples/netconf/comments.json \
+ examples/netconf/simple-dhcp4.json \
+ examples/netconf/simple-dhcp6.json \
+ examples/template-power-user-home/info.md \
+ examples/template-power-user-home/kea-ca-1.conf \
+ examples/template-power-user-home/kea-ca-2.conf \
+ examples/template-power-user-home/kea-dhcp4-1.conf \
+ examples/template-power-user-home/kea-dhcp4-2.conf \
+ examples/template-ha-mt-tls/info.md \
+ examples/template-ha-mt-tls/kea-ca-1.conf \
+ examples/template-ha-mt-tls/kea-ca-2.conf \
+ examples/template-ha-mt-tls/kea-dhcp4-1.conf \
+ examples/template-ha-mt-tls/kea-dhcp4-2.conf
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign doc/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-nobase_dist_docDATA: $(nobase_dist_doc_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(nobase_dist_doc_DATA)'; test -n "$(docdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \
+ fi; \
+ $(am__nobase_list) | while read dir files; do \
+ xfiles=; for file in $$files; do \
+ if test -f "$$file"; then xfiles="$$xfiles $$file"; \
+ else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \
+ test -z "$$xfiles" || { \
+ test "x$$dir" = x. || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(docdir)/$$dir'"; \
+ $(MKDIR_P) "$(DESTDIR)$(docdir)/$$dir"; }; \
+ echo " $(INSTALL_DATA) $$xfiles '$(DESTDIR)$(docdir)/$$dir'"; \
+ $(INSTALL_DATA) $$xfiles "$(DESTDIR)$(docdir)/$$dir" || exit $$?; }; \
+ done
+
+uninstall-nobase_dist_docDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(nobase_dist_doc_DATA)'; test -n "$(docdir)" || list=; \
+ $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \
+ dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir)
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(DATA)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(docdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-nobase_dist_docDATA
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-nobase_dist_docDATA
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+ check-am clean clean-generic clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-nobase_dist_docDATA install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-nobase_dist_docDATA
+
+.PRECIOUS: Makefile
+
+
+# If there's any new parameter added for any grammar in one of the daemons, someone should go through
+# this procedure:
+#
+# 1. autoreconf -i && ./configure --enable-generate-docs --enable-generate-parser
+# 2. cd doc
+# 3. make grammar
+# 4. make -C sphinx html
+# 5. Inspect the html output and make sure it's ok.
+# 6. Review changes in doc/sphinx/grammar/*
+# 7. Check in changed files in doc/sphinx/grammar/*
+#
+# Make sure you commit only relevant changes, skip the timestamp only updates.
+grammar:
+@GENERATE_DOCS_TRUE@@GENERATE_PARSER_TRUE@ mkdir -p $(abs_top_srcdir)/doc/sphinx/grammar
+@GENERATE_DOCS_TRUE@@GENERATE_PARSER_TRUE@ $(abs_top_srcdir)/tools/extract_bnf.sh $(abs_top_srcdir)/src/bin/dhcp4/dhcp4_parser \
+@GENERATE_DOCS_TRUE@@GENERATE_PARSER_TRUE@ --markdown ':ref:`dhcp4`' > $(abs_top_srcdir)/doc/sphinx/grammar/grammar-dhcp4-parser.rst
+@GENERATE_DOCS_TRUE@@GENERATE_PARSER_TRUE@ $(abs_top_srcdir)/tools/extract_bnf.sh $(abs_top_srcdir)/src/bin/dhcp6/dhcp6_parser \
+@GENERATE_DOCS_TRUE@@GENERATE_PARSER_TRUE@ --markdown ':ref:`dhcp6`' > $(abs_top_srcdir)/doc/sphinx/grammar/grammar-dhcp6-parser.rst
+@GENERATE_DOCS_TRUE@@GENERATE_PARSER_TRUE@ $(abs_top_srcdir)/tools/extract_bnf.sh $(abs_top_srcdir)/src/bin/d2/d2_parser \
+@GENERATE_DOCS_TRUE@@GENERATE_PARSER_TRUE@ --markdown ':ref:`dhcp-ddns-server`' > $(abs_top_srcdir)/doc/sphinx/grammar/grammar-d2-parser.rst
+@GENERATE_DOCS_TRUE@@GENERATE_PARSER_TRUE@ $(abs_top_srcdir)/tools/extract_bnf.sh $(abs_top_srcdir)/src/bin/agent/agent_parser \
+@GENERATE_DOCS_TRUE@@GENERATE_PARSER_TRUE@ --markdown ':ref:`kea-ctrl-agent`' > $(abs_top_srcdir)/doc/sphinx/grammar/grammar-ca-parser.rst
+@GENERATE_DOCS_TRUE@@GENERATE_PARSER_TRUE@ $(abs_top_srcdir)/tools/extract_bnf.sh $(abs_top_srcdir)/src/bin/netconf/netconf_parser \
+@GENERATE_DOCS_TRUE@@GENERATE_PARSER_TRUE@ --markdown ':ref:`netconf`' > $(abs_top_srcdir)/doc/sphinx/grammar/grammar-netconf-parser.rst
+@GENERATE_DOCS_TRUE@@GENERATE_PARSER_FALSE@ @echo "ERROR: You need to enable both docs (--enable-generate-docs) and parser (--enable-generate-parser)"
+@GENERATE_DOCS_TRUE@@GENERATE_PARSER_FALSE@ @echo "ERROR: to regenerate grammar documentation."
+@GENERATE_DOCS_TRUE@@GENERATE_PARSER_FALSE@ false
+@GENERATE_DOCS_FALSE@ @echo "ERROR: You need to enable both docs (--enable-generate-docs) and parser (--enable-generate-parser)"
+@GENERATE_DOCS_FALSE@ @echo "ERROR: to regenerate grammar documentation."
+@GENERATE_DOCS_FALSE@ false
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/doc/devel/Doxyfile b/doc/devel/Doxyfile
new file mode 100644
index 0000000..967d656
--- /dev/null
+++ b/doc/devel/Doxyfile
@@ -0,0 +1,2850 @@
+# Doxyfile 1.10.0
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+#
+# Note:
+#
+# Use doxygen to compare the used configuration file with the template
+# configuration file:
+# doxygen -x [configFile]
+# Use doxygen to compare the used configuration file with the template
+# configuration file without replacing the environment variables or CMake type
+# replacement variables:
+# doxygen -x_noenv [configFile]
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = Kea
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO = ../images/kea-logo-100x70.png
+
+# With the PROJECT_ICON tag one can specify an icon that is included in the tabs
+# when the HTML document is shown. Doxygen will copy the logo to the output
+# directory.
+
+PROJECT_ICON =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = html
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
+# sub-directories (in 2 levels) under the output directory of each output format
+# and will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to
+# control the number of sub-directories.
+# The default value is: NO.
+
+CREATE_SUBDIRS = YES
+
+# Controls the number of sub-directories that will be created when
+# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every
+# level increment doubles the number of directories, resulting in 4096
+# directories at level 8 which is the default and also the maximum value. The
+# sub-directories are organized in 2 levels, the first level always has a fixed
+# number of 16 directories.
+# Minimum value: 0, maximum value: 8, default value: 8.
+# This tag requires that the tag CREATE_SUBDIRS is set to YES.
+
+CREATE_SUBDIRS_LEVEL = 8
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,
+# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English
+# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,
+# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with
+# English messages), Korean, Korean-en (Korean with English messages), Latvian,
+# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,
+# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,
+# Swedish, Turkish, Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = NO
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
+# such as
+# /***************
+# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
+# Javadoc-style will behave just like regular comments and it will not be
+# interpreted by doxygen.
+# The default value is: NO.
+
+JAVADOC_BANNER = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# By default Python docstrings are displayed as preformatted text and doxygen's
+# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
+# doxygen's special commands can be used and the contents of the docstring
+# documentation blocks is shown as doxygen documentation.
+# The default value is: YES.
+
+PYTHON_DOCSTRING = YES
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:^^"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". Note that you cannot put \n's in the value part of an alias
+# to insert newlines (in the resulting output). You can put ^^ in the value part
+# of an alias to insert a newline as if a physical newline was in the original
+# file. When you need a literal { or } or , in the value part of an alias you
+# have to escape them by means of a backslash (\), this can lead to conflicts
+# with the commands \{ and \} for these it is advised to use the version @{ and
+# @} or use a double escape (\\{ and \\})
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files). For instance to make doxygen treat .inc files
+# as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen. When specifying no_extension you should add
+# * to the FILE_PATTERNS.
+#
+# Note see also the list of default file extension mappings.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 5.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 5
+
+# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
+# generate identifiers for the Markdown headings. Note: Every identifier is
+# unique.
+# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a
+# sequence number starting at 0 and GITHUB use the lower case version of title
+# with any whitespace replaced by '-' and punctuation characters removed.
+# The default value is: DOXYGEN.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+MARKDOWN_ID_STYLE = DOXYGEN
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = YES
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use
+# during processing. When set to 0 doxygen will based this on the number of
+# cores available in the system. You can set it explicitly to a value larger
+# than 0 to get more control over the balance between CPU load and processing
+# speed. At this moment only the input processing can be done using multiple
+# threads. Since this is still an experimental feature the default is set to 1,
+# which effectively disables parallel processing. Please report any issues you
+# encounter. Generating dot graphs in parallel is controlled by the
+# DOT_NUM_THREADS setting.
+# Minimum value: 0, maximum value: 32, default value: 1.
+
+NUM_PROC_THREADS = 1
+
+# If the TIMESTAMP tag is set different from NO then each generated page will
+# contain the date or date and time when the page was generated. Setting this to
+# NO can help when comparing the output of multiple runs.
+# Possible values are: YES, NO, DATETIME and DATE.
+# The default value is: NO.
+
+TIMESTAMP = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
+# methods of a class will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIV_VIRTUAL = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If this flag is set to YES, the name of an unnamed parameter in a declaration
+# will be determined by the corresponding definition. By default unnamed
+# parameters remain unnamed in the output.
+# The default value is: YES.
+
+RESOLVE_UNNAMED_PARAMS = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# will also hide undocumented C++ concepts if enabled. This option has no effect
+# if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# declarations. If set to NO, these declarations will be included in the
+# documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
+# able to match the capabilities of the underlying filesystem. In case the
+# filesystem is case sensitive (i.e. it supports files in the same directory
+# whose names only differ in casing), the option must be set to YES to properly
+# deal with such files in case they appear in the input. For filesystems that
+# are not case sensitive the option should be set to NO to properly deal with
+# output files written for symbols that only differ in casing, such as for two
+# classes, one named CLASS and the other named Class, and to also support
+# references to files without having to specify the exact matching casing. On
+# Windows (including Cygwin) and MacOS, users should typically set this option
+# to NO, whereas on Linux or other Unix flavors it should typically be set to
+# YES.
+# Possible values are: SYSTEM, NO and YES.
+# The default value is: SYSTEM.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
+# will show which file needs to be included to use the class.
+# The default value is: YES.
+
+SHOW_HEADERFILE = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = YES
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = YES
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = YES
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file. See also section "Changing the
+# layout of pages" for information.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as documenting some parameters in
+# a documented function twice, or documenting parameters that don't exist or
+# using markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
+# function parameter documentation. If set to NO, doxygen will accept that some
+# parameters have no documentation without warning.
+# The default value is: YES.
+
+WARN_IF_INCOMPLETE_DOC = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong parameter
+# documentation, but not about the absence of documentation. If EXTRACT_ALL is
+# set to YES then this flag will automatically be disabled. See also
+# WARN_IF_INCOMPLETE_DOC
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
+# undocumented enumeration values. If set to NO, doxygen will accept
+# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: NO.
+
+WARN_IF_UNDOC_ENUM_VAL = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
+# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
+# at the end of the doxygen process doxygen will return with a non-zero status.
+# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
+# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
+# write the warning messages in between other messages but write them at the end
+# of a run, in case a WARN_LOGFILE is defined the warning messages will be
+# besides being in the defined file also be shown at the end of a run, unless
+# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
+# the behavior will remain as with the setting FAIL_ON_WARNINGS.
+# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# See also: WARN_LINE_FORMAT
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# In the $text part of the WARN_FORMAT command it is possible that a reference
+# to a more specific place is given. To make it easier to jump to this place
+# (outside of doxygen) the user can define a custom "cut" / "paste" string.
+# Example:
+# WARN_LINE_FORMAT = "'vi $file +$line'"
+# See also: WARN_FORMAT
+# The default value is: at line $line of file $file.
+
+WARN_LINE_FORMAT = "at line $line of file $file"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr). In case the file specified cannot be opened for writing the
+# warning and error messages are written to standard error. When as file - is
+# specified the warning and error messages are written to standard output
+# (stdout).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = ../../src/bin/agent \
+ ../../src/bin/d2 \
+ ../../src/bin/dhcp4 \
+ ../../src/bin/dhcp6 \
+ ../../src/bin/lfc \
+ ../../src/bin/netconf \
+ ../../src/bin/perfdhcp \
+ ../../src/hooks/dhcp/bootp \
+ ../../src/hooks/dhcp/flex_option \
+ ../../src/hooks/dhcp/high_availability \
+ ../../src/hooks/dhcp/lease_cmds \
+ ../../src/hooks/dhcp/perfmon \
+ ../../src/hooks/dhcp/stat_cmds \
+ ../../src/hooks/dhcp/user_chk \
+ ../../src/lib/asiodns \
+ ../../src/lib/asiolink \
+ ../../src/lib/cc \
+ ../../src/lib/config \
+ ../../src/lib/config_backend \
+ ../../src/lib/cryptolink \
+ ../../src/lib/d2srv \
+ ../../src/lib/database \
+ ../../src/lib/dhcp \
+ ../../src/lib/dhcp_ddns \
+ ../../src/lib/dhcpsrv \
+ ../../src/lib/dhcpsrv/parsers \
+ ../../src/lib/dns \
+ ../../src/lib/eval \
+ ../../src/lib/exceptions \
+ ../../src/lib/hooks \
+ ../../src/lib/http \
+ ../../src/lib/log \
+ ../../src/lib/log/compiler \
+ ../../src/lib/log/interprocess \
+ ../../src/lib/mysql \
+ ../../src/lib/pgsql \
+ ../../src/lib/process \
+ ../../src/lib/process/cfgrpt \
+ ../../src/lib/stats \
+ ../../src/lib/tcp \
+ ../../src/lib/testutils \
+ ../../src/lib/util \
+ ../../src/lib/util/encode \
+ ../../src/lib/util/io \
+ ../../src/lib/util/unittests \
+ ../../src/lib/yang \
+ .
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see:
+# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
+# See also: INPUT_FILE_ENCODING
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
+# character encoding on a per file pattern basis. Doxygen will compare the file
+# name with each pattern and apply the encoding instead of the default
+# INPUT_ENCODING) if there is a match. The character encodings are a list of the
+# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
+# "INPUT_ENCODING" for further information on supported encodings.
+
+INPUT_FILE_ENCODING =
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# Note the list of default checked file patterns might differ from the list of
+# default file extension mappings.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,
+# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl,
+# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d,
+# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to
+# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.h \
+ *.hpp \
+ *.dox
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE = ../../src/lib/dns/master_lexer.cc \
+ ../../src/lib/dns/rdataclass.cc \
+ ../../src/lib/eval/lexer.cc
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS = */*-placeholder.* \
+ */cpp/*.py
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# ANamespace::AClass, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH = ../images \
+ ../../src/lib/hooks/images \
+ ../../src/bin/d2/images \
+ ../../src/lib/dhcpsrv/images
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that doxygen will use the data processed and written to standard output
+# for further processing, therefore nothing else, like debug statements or used
+# commands (so in case of a Windows batch file always use @echo OFF), should be
+# written to standard output.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+# The Fortran standard specifies that for fixed formatted Fortran code all
+# characters from position 72 are to be considered as comment. A common
+# extension is to allow longer lines before the automatic comment starts. The
+# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
+# be processed before the automatic comment starts.
+# Minimum value: 7, maximum value: 10000, default value: 72.
+
+FORTRAN_COMMENT_AFTER = 72
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# multi-line macros, enums or list initialized variables directly into the
+# documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# entity all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
+# that should be ignored while generating the index headers. The IGNORE_PREFIX
+# tag works for classes, function and member names. The entity will be placed in
+# the alphabetical list under the first letter of the entity name that remains
+# after removing the prefix.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = ../html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# Note: Since the styling of scrollbars can currently not be overruled in
+# Webkit/Chromium, the styling will be left out of the default doxygen.css if
+# one or more extra stylesheets have been specified. So if scrollbar
+# customization is desired it has to be added explicitly. For an example see the
+# documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
+# should be rendered with a dark or light theme.
+# Possible values are: LIGHT always generate light mode output, DARK always
+# generate dark mode output, AUTO_LIGHT automatically set the mode according to
+# the user preference, use light mode if no preference is set (the default),
+# AUTO_DARK automatically set the mode according to the user preference, use
+# dark mode if no preference is set and TOGGLE allow to user to switch between
+# light and dark mode via a button.
+# The default value is: AUTO_LIGHT.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE = AUTO_LIGHT
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a color-wheel, see
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use gray-scales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via JavaScript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have JavaScript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = YES
+
+# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
+# dynamically folded and expanded in the generated HTML source code.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_CODE_FOLDING = YES
+
+# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in
+# the top right corner of code and text fragments that allows the user to copy
+# its content to the clipboard. Note this only works if supported by the browser
+# and the web page is served via a secure context (see:
+# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file:
+# protocol.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COPY_CLIPBOARD = YES
+
+# Doxygen stores a couple of settings persistently in the browser (via e.g.
+# cookies). By default these settings apply to all HTML pages generated by
+# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store
+# the settings under a project specific key, such that the user preferences will
+# be stored separately.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_PROJECT_COOKIE =
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see:
+# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
+# create a documentation set, doxygen will generate a Makefile in the HTML
+# output directory. Running make will produce the docset in that directory and
+# running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag determines the URL of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDURL =
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# on Windows. In the beginning of 2021 Microsoft took the original page, with
+# a.o. the download links, offline the HTML help workshop was already many years
+# in maintenance mode). You can download the HTML help workshop from the web
+# archives at Installation executable (see:
+# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
+# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the main .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# The SITEMAP_URL tag is used to specify the full URL of the place where the
+# generated documentation will be placed on the server by the user during the
+# deployment of the documentation. The generated sitemap is called sitemap.xml
+# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
+# is specified no sitemap is generated. For information about the sitemap
+# protocol see https://www.sitemaps.org
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SITEMAP_URL =
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location (absolute path
+# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
+# run qhelpgenerator on the generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine tune the look of the index (see "Fine-tuning the output"). As an
+# example, the default style sheet generated by doxygen has an example that
+# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
+# Since the tree basically has the same information as the tab index, you could
+# consider setting DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = YES
+
+# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
+# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
+# area (value NO) or if it should extend to the full height of the window (value
+# YES). Setting this to YES gives a layout similar to
+# https://docs.readthedocs.io with more room for contents, but less room for the
+# project logo, title, and description. If either GENERATE_TREEVIEW or
+# DISABLE_INDEX is set to NO, this option has no effect.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FULL_SIDEBAR = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 180
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email
+# addresses.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+OBFUSCATE_EMAILS = YES
+
+# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
+# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
+# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
+# the HTML output. These images will generally look nicer at scaled resolutions.
+# Possible values are: png (the default) and svg (looks nicer but requires the
+# pdf2svg or inkscape tool).
+# The default value is: png.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FORMULA_FORMAT = png
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
+
+FORMULA_MACROFILE =
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
+# Note that the different versions of MathJax have different requirements with
+# regards to the different settings, so it is possible that also other MathJax
+# settings have to be changed when switching between the different MathJax
+# versions.
+# Possible values are: MathJax_2 and MathJax_3.
+# The default value is: MathJax_2.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_VERSION = MathJax_2
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. For more details about the output format see MathJax
+# version 2 (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
+# (see:
+# http://docs.mathjax.org/en/latest/web/components/output.html).
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility. This is the name for Mathjax version 2, for MathJax version 3
+# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
+# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
+# is the name for Mathjax version 3, for MathJax version 2 this will be
+# translated into HTML-CSS) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from https://www.mathjax.org before deployment. The default value is:
+# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
+# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH =
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# for MathJax version 2 (see
+# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# For example for MathJax version 3 (see
+# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
+# MATHJAX_EXTENSIONS = ams
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using JavaScript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see:
+# https://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see:
+# https://xapian.org/). See the section "External Indexing and Searching" for
+# details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX. In case there is no backslash (\) as first character
+# it will be automatically added in the LaTeX code.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
+# the generated LaTeX document. The header should contain everything until the
+# first chapter. If it is left blank doxygen will generate a standard header. It
+# is highly recommended to start with a default header using
+# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
+# and then modify the file new_header.tex. See also section "Doxygen usage" for
+# information on how to generate the default header that doxygen normally uses.
+#
+# Note: Only use a user-defined header if you know what you are doing!
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. The following
+# commands have a special meaning inside the header (and footer): For a
+# description of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
+# the generated LaTeX document. The footer should contain everything after the
+# last chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer. See also section "Doxygen
+# usage" for information on how to generate the default footer that doxygen
+# normally uses. Note: Only use a user-defined footer if you know what you are
+# doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
+# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
+# files. Set this option to YES, to get a higher quality PDF documentation.
+#
+# See also section LATEX_CMD_NAME for selecting the engine.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = NO
+
+# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.
+# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
+# mode nothing is printed on the terminal, errors are scrolled as if <return> is
+# hit at every error; missing files that TeX tries to input or request from
+# keyboard input (\read on a not open input stream) cause the job to abort,
+# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
+# but there is no possibility of user interaction just like in batch mode,
+# SCROLL In scroll mode, TeX will stop only for missing files to input or if
+# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
+# each error, asking for user intervention.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# configuration file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = NO
+
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_NS_MEMB_FILE_SCOPE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to Sqlite3 output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
+# database with symbols found by doxygen stored in tables.
+# The default value is: NO.
+
+GENERATE_SQLITE3 = NO
+
+# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
+# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
+# in front of it.
+# The default directory is: sqlite3.
+# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
+
+SQLITE3_OUTPUT = sqlite3
+
+# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db
+# database file will be recreated with each doxygen run. If set to NO, doxygen
+# will warn if a database file is already found and not modify it.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
+
+SQLITE3_RECREATE_DB = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = YES
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of
+# RECURSIVE has no effect here.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces
+# will be listed in the class and namespace index. If set to NO, only the
+# inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the topic index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to diagram generator tools
+#---------------------------------------------------------------------------
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
+# subgraphs. When you want a differently looking font in the dot files that
+# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
+# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
+# Edge and Graph Attributes specification</a> You need to make sure dot is able
+# to find the font, which can be done by putting it in a standard location or by
+# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font. Default graphviz fontsize is 14.
+# The default value is: fontname=Helvetica,fontsize=10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
+
+# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
+# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
+# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
+# arrows shapes.</a>
+# The default value is: labelfontname=Helvetica,labelfontsize=10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
+
+# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
+# around nodes set 'shape=plain' or 'shape=plaintext' <a
+# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
+# The default value is: shape=box,height=0.2,width=0.4.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
+
+# You can set the path where dot can find font specified with fontname in
+# DOT_COMMON_ATTR and others dot attributes.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
+# generate a graph for each documented class showing the direct and indirect
+# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
+# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
+# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
+# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
+# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
+# relations will be shown as texts / links. Explicit enabling an inheritance
+# graph or choosing a different representation for an inheritance graph of a
+# specific class, can be accomplished by means of the command \inheritancegraph.
+# Disabling an inheritance graph can be accomplished by means of the command
+# \hideinheritancegraph.
+# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
+# The default value is: YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes. Explicit enabling a collaboration graph,
+# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
+# command \collaborationgraph. Disabling a collaboration graph can be
+# accomplished by means of the command \hidecollaborationgraph.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = NO
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies. Explicit enabling a group
+# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means
+# of the command \groupgraph. Disabling a directory graph can be accomplished by
+# means of the command \hidegroupgraph. See also the chapter Grouping in the
+# manual.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
+# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
+# tag is set to YES, doxygen will add type and arguments for attributes and
+# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
+# will not generate fields with class member information in the UML graphs. The
+# class diagrams will look similar to the default class diagrams but using UML
+# notation for the relationships.
+# Possible values are: NO, YES and NONE.
+# The default value is: NO.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+DOT_UML_DETAILS = NO
+
+# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
+# to display on a single line. If the actual line length exceeds this threshold
+# significantly it will be wrapped across multiple lines. Some heuristics are
+# applied to avoid ugly line breaks.
+# Minimum value: 0, maximum value: 1000, default value: 17.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_WRAP_THRESHOLD = 17
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
+# can be accomplished by means of the command \includegraph. Disabling an
+# include graph can be accomplished by means of the command \hideincludegraph.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set
+# to NO, can be accomplished by means of the command \includedbygraph. Disabling
+# an included by graph can be accomplished by means of the command
+# \hideincludedbygraph.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = YES
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories. Explicit enabling a directory graph, when
+# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
+# \directorygraph. Disabling a directory graph can be accomplished by means of
+# the command \hidedirectorygraph.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels
+# of child directories generated in directory dependency graphs by dot.
+# Minimum value: 1, maximum value: 25, default value: 1.
+# This tag requires that the tag DIRECTORY_GRAPH is set to YES.
+
+DIR_GRAPH_MAX_DEPTH = 1
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# https://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file or to the filename of jar file
+# to be used. If left blank, it is assumed PlantUML is not used or called during
+# a preprocessing step. Doxygen will generate a warning when it encounters a
+# \startuml command in this case and will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 400
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal
+# graphical representation for inheritance and collaboration diagrams is used.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
+# files that are used to generate the various graphs.
+#
+# Note: This setting is not only used for dot files but also for msc temporary
+# files.
+# The default value is: YES.
+
+DOT_CLEANUP = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
+# use a built-in version of mscgen tool to produce the charts. Alternatively,
+# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
+# specifying prog as the value, doxygen will call the tool as prog -T
+# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
+# output file formats "png", "eps", "svg", and "ismap".
+
+MSCGEN_TOOL =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
diff --git a/doc/devel/Doxyfile-xml b/doc/devel/Doxyfile-xml
new file mode 100644
index 0000000..ae5be8a
--- /dev/null
+++ b/doc/devel/Doxyfile-xml
@@ -0,0 +1,7 @@
+# This is a doxygen configuration for generating XML output as well as HTML.
+#
+# Inherit everything from our default Doxyfile except GENERATE_XML, which
+# will be reset to YES
+
+@INCLUDE = Doxyfile
+GENERATE_XML = YES
diff --git a/doc/devel/Makefile.am b/doc/devel/Makefile.am
new file mode 100644
index 0000000..30d819b
--- /dev/null
+++ b/doc/devel/Makefile.am
@@ -0,0 +1,25 @@
+EXTRA_DIST =
+EXTRA_DIST += Doxyfile Doxyfile-xml
+EXTRA_DIST += bison.dox
+EXTRA_DIST += config-backend.dox
+EXTRA_DIST += congestion-handling.dox
+EXTRA_DIST += contribute.dox
+EXTRA_DIST += cross-compile.dox
+EXTRA_DIST += debug.dox
+EXTRA_DIST += doc.dox
+EXTRA_DIST += fuzz.dox
+EXTRA_DIST += mainpage.dox
+EXTRA_DIST += qa.dox
+EXTRA_DIST += terminology.dox
+EXTRA_DIST += unit-tests.dox
+EXTRA_DIST += performance.dox
+
+all: # do nothing, used only by developers manually
+
+devel:
+ mkdir -p $(builddir)/html
+ (cat $(srcdir)/Doxyfile; echo PROJECT_NUMBER=$(PACKAGE_VERSION)) | doxygen - > $(builddir)/html/doxygen.log 2> $(builddir)/html/doxygen-error.log
+ echo `grep -i ": warning:" $(builddir)/html/doxygen-error.log | wc -l` warnings/errors detected.
+
+clean-local:
+ rm -rf html
diff --git a/doc/devel/Makefile.in b/doc/devel/Makefile.in
new file mode 100644
index 0000000..98cf008
--- /dev/null
+++ b/doc/devel/Makefile.in
@@ -0,0 +1,566 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = doc/devel
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \
+ $(top_srcdir)/m4macros/ax_cpp14.m4 \
+ $(top_srcdir)/m4macros/ax_cpp20.m4 \
+ $(top_srcdir)/m4macros/ax_crypto.m4 \
+ $(top_srcdir)/m4macros/ax_find_library.m4 \
+ $(top_srcdir)/m4macros/ax_gssapi.m4 \
+ $(top_srcdir)/m4macros/ax_gtest.m4 \
+ $(top_srcdir)/m4macros/ax_isc_rpath.m4 \
+ $(top_srcdir)/m4macros/ax_netconf.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in README
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_GSSAPI_CONFIGURE_FLAG = @DISTCHECK_GSSAPI_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG = @DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG@
+DISTCHECK_LIBYANG_CONFIGURE_FLAG = @DISTCHECK_LIBYANG_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_MYSQL_CONFIGURE_FLAG = @DISTCHECK_MYSQL_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PGSQL_CONFIGURE_FLAG = @DISTCHECK_PGSQL_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@
+DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG = @DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG@
+DISTCHECK_SYSREPO_CONFIGURE_FLAG = @DISTCHECK_SYSREPO_CONFIGURE_FLAG@
+DLLTOOL = @DLLTOOL@
+DPKG = @DPKG@
+DPKGQUERY = @DPKGQUERY@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+GENHTML = @GENHTML@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GTEST_CONFIG = @GTEST_CONFIG@
+GTEST_INCLUDES = @GTEST_INCLUDES@
+GTEST_LDADD = @GTEST_LDADD@
+GTEST_LDFLAGS = @GTEST_LDFLAGS@
+GTEST_SOURCE = @GTEST_SOURCE@
+HAVE_NETCONF = @HAVE_NETCONF@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KEA_CXXFLAGS = @KEA_CXXFLAGS@
+KEA_SRCID = @KEA_SRCID@
+KRB5_CONFIG = @KRB5_CONFIG@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBYANGCPP_CPPFLAGS = @LIBYANGCPP_CPPFLAGS@
+LIBYANGCPP_INCLUDEDIR = @LIBYANGCPP_INCLUDEDIR@
+LIBYANGCPP_LIBS = @LIBYANGCPP_LIBS@
+LIBYANGCPP_PREFIX = @LIBYANGCPP_PREFIX@
+LIBYANGCPP_VERSION = @LIBYANGCPP_VERSION@
+LIBYANG_CPPFLAGS = @LIBYANG_CPPFLAGS@
+LIBYANG_INCLUDEDIR = @LIBYANG_INCLUDEDIR@
+LIBYANG_LIBS = @LIBYANG_LIBS@
+LIBYANG_PREFIX = @LIBYANG_PREFIX@
+LIBYANG_VERSION = @LIBYANG_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@
+LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LIBS = @MYSQL_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PDFLATEX = @PDFLATEX@
+PERL = @PERL@
+PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PKGPYTHONDIR = @PKGPYTHONDIR@
+PKG_CONFIG = @PKG_CONFIG@
+PLANTUML = @PLANTUML@
+PREMIUM_DIR = @PREMIUM_DIR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SEP = @SEP@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@
+SR_PLUGINS_PATH = @SR_PLUGINS_PATH@
+SR_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_CPPFLAGS = @SYSREPOCPP_CPPFLAGS@
+SYSREPOCPP_INCLUDEDIR = @SYSREPOCPP_INCLUDEDIR@
+SYSREPOCPP_LIBS = @SYSREPOCPP_LIBS@
+SYSREPOCPP_PREFIX = @SYSREPOCPP_PREFIX@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+SYSREPO_PREFIX = @SYSREPO_PREFIX@
+SYSREPO_VERSION = @SYSREPO_VERSION@
+USE_LCOV = @USE_LCOV@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@
+XMLLINT = @XMLLINT@
+YACC = @YACC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = Doxyfile Doxyfile-xml bison.dox config-backend.dox \
+ congestion-handling.dox contribute.dox cross-compile.dox \
+ debug.dox doc.dox fuzz.dox mainpage.dox qa.dox terminology.dox \
+ unit-tests.dox performance.dox
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/devel/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign doc/devel/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-local mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ clean-local cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+all: # do nothing, used only by developers manually
+
+devel:
+ mkdir -p $(builddir)/html
+ (cat $(srcdir)/Doxyfile; echo PROJECT_NUMBER=$(PACKAGE_VERSION)) | doxygen - > $(builddir)/html/doxygen.log 2> $(builddir)/html/doxygen-error.log
+ echo `grep -i ": warning:" $(builddir)/html/doxygen-error.log | wc -l` warnings/errors detected.
+
+clean-local:
+ rm -rf html
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/doc/devel/README b/doc/devel/README
new file mode 100644
index 0000000..4cb53d4
--- /dev/null
+++ b/doc/devel/README
@@ -0,0 +1,5 @@
+This is a folder with content for Doxygen.
+This is used by developers and is not used directly in official builds.
+
+To build doxygen documentation run:
+make devel
diff --git a/doc/devel/bison.dox b/doc/devel/bison.dox
new file mode 100644
index 0000000..4c96302
--- /dev/null
+++ b/doc/devel/bison.dox
@@ -0,0 +1,450 @@
+// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+@page parser Flex/Bison Parsers
+
+@section parserIntro Parser background
+
+Kea's data format of choice is JSON (defined in https://tools.ietf.org/html/rfc7159), which is used
+in configuration files, in the command channel and also when communicating between the DHCP servers
+and the DHCP-DDNS component. It is almost certain to be used as the data format for any new
+features.
+
+Historically, Kea used the @ref isc::data::Element::fromJSON and @ref
+isc::data::Element::fromJSONFile methods to parse data expected to be in JSON syntax. This in-house
+parser was developed back in the early days of Kea when it was part of BIND 10. Its main advantages
+were that it didn't have any external dependencies and that it was already available in the source
+tree when Kea development started. On the other hand, it was very difficult to modify (several
+attempts to implement more robust comments had failed) and lacked a number of features. Also, it was
+a pure JSON parser, so accepted anything as long as the content was correct JSON. (This caused some
+problems: for example, the syntactic checks were conducted late in the parsing process, by which
+time some of the information, e.g. line numbers, was no longer available. To print meaningful error
+messages, the Kea team had to develop a way to store filename, line and column information.
+Unfortunately this gave rise to other problems such as data duplication.) The output from these
+parsers was a tree of @ref isc::data::Element objects using shared pointers. This part of the
+processing we can refer to as phase 1.
+
+The Element tree was then processed by set of dedicated parsers. Each
+parser was able to handle its own context, e.g. global, subnet list,
+subnet, pool etc. This step took the tree generated in phase 1, parsed
+it and generated an output configuration (e.g. @ref
+isc::dhcp::SrvConfig) or dynamic structures
+(e.g. isc::data::Host). During this stage, a large number of parser
+objects derived from DhcpConfigParser could be instantiated for each
+scope and instance of data (e.g. to parse 1000 host reservation
+entries a thousand dedicated parsers were created). For convenience,
+this step is called phase 2.
+
+Other issues with the old parsers are discussed here: @ref dhcpv6ConfigParserBison (this section is
+focused on DHCPv6, but the same issues affected DHCPv4 and D2) and here:
+https://gitlab.isc.org/isc-projects/kea/wikis/designs/simple-parser-design.
+
+@section parserBisonIntro Flex/Bison Based Parser
+
+To solve the issue of phase 1 mentioned earlier, a new parser has been developed that is based on
+the "flex and "bison" tools. The following text uses DHCPv6 as an example, but the same principle
+applies to DHCPv4 and D2; CA will likely to follow. The new parser consists of two core elements
+with a wrapper around them. The following descriptions are slightly oversimplified in order to
+convey the intent; a more detailed description is available in subsequent sections.
+
+-# Flex lexical analyzer (src/bin/dhcp6/dhcp6_lexer.ll): this is essentially a set of
+ regular expressions and C++ code that creates new tokens that represent whatever
+ was just parsed. This lexical analyzer (lexer) will be called iteratively by bison until the whole
+ input text is parsed or an error is encountered. For example, a snippet of the
+ code might look like this:
+ @code
+ \"socket-type\" {
+ return isc::dhcp::Dhcp6Parser::make_SOCKET_TYPE(driver.loc_);
+ }
+ @endcode
+ This tells the flex that if encounters "socket-type" (quoted), then it should
+ create a token SOCKET_TYPE and pass to it its current location (that's the
+ file name, line and column numbers).
+
+-# Bison grammar (src/bin/dhcp6/dhcp6_parser.yy): the module that defines the syntax. Grammar and
+ syntax are perhaps fancy words, but they simply define what is allowed and where. Bison grammar
+ starts with a list of tokens. Those tokens are defined only by name ("here's the list of possible
+ tokens that could appear"). What constitutes a token is actually defined in the lexer. The
+ grammar define how the incoming tokens are expected to fall into their places together. Let's
+ take an example of the following input text:
+ @code
+ {
+ "Dhcp6":
+ {
+ "renew-timer": 100
+ }
+ }
+ @endcode
+ The lexer would generate the following sequence of tokens: LCURLY_BRACKET, DHCP6, COLON,
+ LCURLY_BRACKET, RENEW_TIMER, COLON, INTEGER (a token with a value of 100), RCURLY_BRACKET,
+ RCURLY_BRACKET, END. The bison grammar recognizes that the sequence forms a valid sentence and
+ that there are no errors and act upon it. (Whereas if the left and right braces in the above
+ example were exchanged, the bison module would identify the sequence as syntactically incorrect.)
+
+-# Parser context. As there is some information that needs to be passed between parser and lexer,
+ @ref isc::dhcp::Parser6Context is a convenience wrapper around those two bundled together. It
+ also works as a nice encapsulation, hiding all the flex/bison details underneath.
+
+@section parserBuild Building Flex/Bison Code
+
+The only input file used by flex is the .ll file and the only input file used by bison is the .yy
+file. When making changes to the lexer or parser, only those two files are edited. When processed,
+the two tools generate a number of .h, .hh and .cc files. The major ones have the same name as their
+.ll and .yy counterparts (e.g. dhcp6_lexer.cc, dhcp6_parser.cc and dhcp6_parser.h etc.), but an
+additional file is also created: location.hh. Those are internal bison headers that are needed for
+compilation.
+
+To avoid the need for every user to have flex and bison installed, the output files are generated
+when the .ll or .yy files are altered and are stored in the Kea repository. To generate those files,
+issue the following sequence of commands from the top-level Kea directory:
+
+@code
+./configure --enable-generate-parser
+cd src/bin/dhcp6
+make parser
+@endcode
+
+Strictly speaking, the comment "make parser" is not necessary. If you updated the .ll or .yy file,
+the regular "make" command should pick those changes up. However, since one source file generates
+multiple output files and you are likely to be using a multi-process build (by specifying the "-j"
+switch on the "make" command), there may be odd side effects: explicitly rebuilding the files
+manually by using "make parser" avoids any trouble.
+
+One problem brought on by use of flex/bison is tool version dependency. If one developer uses
+version A of those tools and another developer uses B, the files generated by the different version
+may be significantly different. This causes all sorts of problems, e.g. coverity/cpp-check issues
+may appear and disappear: in short, it can cause all sorts of general unhappiness. To avoid those
+problems, the Kea team generates the flex/bison files on a dedicated machine. See KeaRegen page
+on ISC internal wiki for details.
+
+@section parserFlex Flex Detailed
+
+Earlier sections described the lexer in a bit of an over-simplified way. The .ll file contains a
+number of elements in addition to the regular expressions and they're not as simple as was
+described.
+
+The file starts with a number of sections separated by percent (%) signs. Depending on which section
+code is written in, it may be interpreted by flex, copied verbatim to the output .cc file, copied to
+the output .h file or copied to both.
+
+There is an initial section that defines flex options. These are somewhat documented, but the
+documentation for it may be a bit cryptic. When developing new parsers, it's best to start by
+copying whatever we have for DHCPv6 and tweak as needed.
+
+Next comes the flex conditions. They are defined with %%x and they define a state of the lexer. A
+good example of a state may be comment. Once the lexer detects that a comment's beginning, it
+switches to a certain condition (by calling BEGIN(COMMENT) for example) and the code then ignores
+whatever follows (especially strings that look like valid tokens) until the comment is closed (when
+it returns to the default condition by calling BEGIN(INITIAL)). This is something that is not
+frequently used and the only use cases for it are the forementioned comments and file inclusions.
+
+After this come the syntactic contexts. Let's assume we have a parser that uses an "ip-address"
+regular expression (regexp) that would return the IP_ADDRESS token. Whenever we want to allow
+"ip-address", the grammar allows the IP_ADDRESS token to appear. When the lexer is called, it will
+match the regexp, generate the IP_ADDRESS token and the parser will carry out its duty. This works
+fine as long as you have very specific grammar that defines everything. Sadly, that's not the case
+in DHCP as we have hooks. Hook libraries can have parameters that are defined by third party
+developers and they can pick whatever parameter names they want, including "ip-address". Another
+example could be Dhcp4 and Dhcp6 configurations defined in a single file. The grammar defining
+"Dhcp6" main contain a clause that says "Dhcp4" may contain any generic JSON. However, the lexer may
+find the "ip-address" string in the "Dhcp4" configuration and will say that it's not a part of
+generic JSON, but a dedicated IP_ADDRESS token instead. The parser will then complain and the whole
+thing would end up in failure. It was to solve this problem that syntactic contexts were introduced.
+They tell the lexer whether input strings have specific or generic meaning. For example, when
+parsing host reservations, the lexer is expected to report the IP_ADDRESS token if "ip-address" is
+detected. However, when parsing generic JSON, upon encountering "ip-address" it should return a
+STRING with a value of "ip-address". The list of all contexts is enumerated in @ref
+isc::dhcp::Parser6Context::ParserContext.
+
+For a DHCPv6-specific description of the conflict avoidance, see @ref dhcp6ParserConflicts.
+
+@section parserGrammar Bison Grammar
+
+Bison has much better documentation than flex. Its latest version seems to be available here:
+https://www.gnu.org/software/bison/manual. Bison is a LALR(1) parser, which essentially means that
+it is able to parse (separate and analyze) any text that is described by set of rules. You can see
+the more formal description here: https://en.wikipedia.org/wiki/LALR_parser, but the plain English
+explanation is that you define a set of rules and bison will walk through input text trying to match
+the content to those rules. While doing so, it will be allowed to peek at most one symbol (token)
+ahead.
+
+As an example, let's take a closer look at the bison grammar we have for DHCPv6. It is defined
+in src/bin/dhcp6/dhcp6_parser.yy. Here's a simplified excerpt:
+
+@code
+// This defines a global Dhcp6 object.
+dhcp6_object: DHCP6 COLON LCURLY_BRACKET global_params RCURLY_BRACKET;
+
+// This defines all parameters that may appear in the Dhcp6 object.
+// It can either contain a global_param (defined below) or a
+// global_params list, followed by a comma followed by a global_param.
+// Note this definition is recursive and can expand to a single
+// instance of global_param or multiple instances separated by commas.
+// This is how bison handles variable number of parameters.
+global_params: global_param
+ | global_params COMMA global_param
+ ;
+
+// These are the parameters that are allowed in the top-level for
+// Dhcp6.
+global_param: preferred_lifetime
+ | valid_lifetime
+ | renew_timer
+ | rebind_timer
+ | subnet6_list
+ | interfaces_config
+ | lease_database
+ | hosts_database
+ | mac_sources
+ | relay_supplied_options
+ | host_reservation_identifiers
+ | client_classes
+ | option_data_list
+ | hooks_libraries
+ | expired_leases_processing
+ | server_id
+ | dhcp4o6_port
+ ;
+
+renew_timer: RENEW_TIMER COLON INTEGER;
+
+// Many other definitions follow.
+@endcode
+
+The code above defines parameters that may appear in the Dhcp6 object declaration. One important
+trick to understand is understand the way to handle variable number of parameters. In bison it is
+most convenient to present them as recursive lists: in this example, global_params defined in a way
+that allows any number of global_param instances allowing the grammar to be easily extensible. If
+one needs to add a new global parameter, just add it to the global_param list.
+
+This type of definition has several levels, each representing logical structure of the configuration
+data. We start with global scope, then step into a Dhcp6 object that has a Subnet6 list, which in
+turn has Subnet6 instances, each of which has pools list and so on. Each level is represented as a
+separate rule.
+
+The "leaf" rules (that don't contain any other rules) must be defined by a series of tokens. An
+example of such a rule is renew_timer, above. It is defined as a series of 3 tokens: RENEW_TIMER,
+COLON and INTEGER.
+
+Speaking of integers, it is worth noting that some tokens can have values. Those values are defined
+using %token clause. For example, dhcp6_parser.yy contains the following:
+
+@code
+%token <std::string> STRING "constant string"
+%token <int64_t> INTEGER "integer"
+%token <double> FLOAT "floating point"
+%token <bool> BOOLEAN "boolean"
+@endcode
+
+The first line says that the token STRING has a type of std::string and when referring to this token
+in error messages, it should be printed as "constant string".
+
+In principle, it is valid to define just the grammar without any corresponding C++ code to it. Bison
+will go through the whole input text, match the rules and will either say the input adhered to the
+rules (parsing successful) or not (parsing failed). This may be a useful step when developing new
+parser, but it has no practical value. To perform specific actions, bison allows the injection of
+C++ code at almost any point. For example we could augment the parsing of renew_timer with some
+extra code:
+
+@code
+renew_timer: RENEW_TIMER {
+ cout << "renew-timer token detected, so far so good" << endl;
+} COLON {
+ cout << "colon detected!" << endl;
+} INTEGER {
+ uint32_t timer = $3;
+ cout << "Got the renew-timer value: " << timer << endl;
+ ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("renew-timer", prf);
+};
+@endcode
+
+This example showcases several important things. First, the ability to insert code at almost any
+step is very useful. It's also a powerful debugging tool.
+
+Second, some tokens are valueless (e.g. "renew-timer" when represented as the RENEW_TIMER token has
+no value), but some have values. In particular, the INTEGER token has value which can be extracted
+by $ followed by a number that represents its order, so $3 means "a value of third token or action
+in this rule". If needed, the location of specific token (filename, line and column) can be
+accessed with @ followed by a number that represents token number, e.g. @3 in the example above
+returns exact location of INTEGER token.
+
+Also, some rules may have values. This is not used often, but there are specific cases when it's
+convenient. Let's take a look at the following excerpt from dhcp6_parser.yy:
+
+@code
+ncr_protocol: NCR_PROTOCOL {
+ ctx.enter(ctx.NCR_PROTOCOL); (1)
+} COLON ncr_protocol_value {
+ ctx.stack_.back()->set("ncr-protocol", $4); (3)
+ ctx.leave(); (4)
+};
+
+ncr_protocol_value:
+ UDP { $$ = ElementPtr(new StringElement("UDP", ctx.loc2pos(@1))); }
+ | TCP { $$ = ElementPtr(new StringElement("TCP", ctx.loc2pos(@1))); } (2)
+ ;
+@endcode
+
+(The numbers in brackets at the end of some lines do not appear in the code; they are used identify
+the statements in the following discussion.)
+
+The "ncr-protocol" parameter accepts one of two values: either tcp or udp. To handle such a case, we
+first enter the NCR_PROTOCOL context to tell the lexer that we're in this scope. The lexer will then
+know that any incoming string of text that is either "UDP" or "TCP" should be represented as one of
+the TCP or UDP tokens. The parser knows that after NCR_PROTOCOL there will be a colon followed by an
+ncr_protocol_value. The rule for ncr_protocol_value says it can be either the TCP token or the UDP
+token. Let's assume the input text is:
+@code
+"ncr-protocol": "TCP"
+@endcode
+
+Here's how the parser will handle it. First, it will attempt to match the rule for ncr_protocol. It
+will discover the first token is NCR_PROTOCOL. As a result, it will run the code (1), which will
+tell lexer to parse incoming tokens as ncr protocol values. The next token is expected to be COLON
+and the one after that the ncr_protocol_value. The lexer has already been switched into the
+NCR_PROTOCOL context, so it will recognize "TCP" as TCP token, not as a string with a value of
+"TCP". The parser will receive that token and match the line (2), which creates an appropriate
+representation that will be used as the rule's value ($$). Finally, the parser will unroll back to
+ncr_protocol rule and execute the code in lines (3) and (4). Line (3) picks the value set up in
+line (2) and adds it to the stack of values. Finally, line (4) tells the lexer that we finished the
+NCR protocol parsing and it can go back to whatever state it was before.
+
+@section parserBisonStack Generating the Element Tree in Bison
+
+The bison parser keeps matching rules until it reaches the end of input file. During that process,
+the code needs to build a hierarchy (a tree) of inter-connected Element objects that represents the
+parsed text. @ref isc::data::Element has a complex structure that defines parent-child relation
+differently depending on the type of parent (ae.g. a map and a list refer to their children in
+different ways). This requires the code to be aware of the parent content. In general, every time a
+new scope (an opening curly bracket in input text) is encountered, the code pushes new Element to
+the stack (see @ref isc::dhcp::Parser6Context::stack_) and every time the scope closes (a closing
+curly bracket in input text) the element is removed from the stack. With this approach, we always
+have access to the parent element as it's the last element on the stack. For example, when parsing
+preferred-lifetime, the code does the following:
+
+@code
+preferred_lifetime: PREFERRED_LIFETIME COLON INTEGER {
+ ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("preferred-lifetime", prf);
+}
+@endcode
+
+The first line creates an instance of IntElement with a value of the token. The second line adds it
+to the current map (current = the last on the stack). This approach has a very nice property of
+being generic. This rule can be referenced from both global and subnet scope (and possibly other
+scopes as well) and the code will add the IntElement object to whatever is last on the stack, be it
+global, subnet or perhaps even something else (maybe one day we will allow preferred lifetime to be
+defined on a per pool or per host basis?).
+
+@section parserSubgrammar Parsing a Partial Configuration
+
+All the explanations so far assumed that we're operating in a default case of receiving the
+configuration as a whole. That is the case during startup and reconfiguration. However, both DHCPv4
+and DHCPv6 support certain cases when the input text is not the whole configuration, but rather
+certain parts of it. There are several examples of such cases. The most common are unit-tests. They
+typically don't have the outermost { } or Dhcp6 object, but simply define whatever parameters are
+being tested. Second, we have the command channel that will, in the near future, contain parts of
+the configuration, depending on the command. For example, "add-reservation" will contain a host
+reservation only.
+
+Bison by default does not support multiple start rules, but there's a trick that can provide such a
+capability. The trick assumes that the starting rule may allow one of the artificial tokens that
+represent the scope expected. For example, when called from the "add-reservation" command, the
+artificial token will be SUB_RESERVATION and it will trigger the parser to bypass the global braces
+{ and } and the "Dhcp6" token and jump immediately to the sub_reservation.
+
+This trick is also implemented in the lexer. A flag called start_token_flag, when initially set to
+true, will cause the lexer to emit an artificial token once, before parsing any input whatsoever.
+
+This optional feature can be skipped altogether if you don't plan to parse parts of the
+configuration.
+
+@section parserBisonExtend Extending the Grammar
+
+Adding new parameters to existing parsers is very easy once you get hold of the concept of what the
+grammar rules represent. The first step is to understand where the parameter is to be
+allowed. Typically a new parameter is allowed in one scope and only over time is it added to other
+scopes. Recently support for a 4o6-interface-id parameter has been added. That is a parameter that
+can be defined in a subnet and takes a string argument. You can see the actual change conducted in
+this commit: (https://github.com/isc-projects/kea/commit/9fccdbf54c4611dc10111ad8ff96d36cad59e1d6).
+
+Here's the complete set of changes that were necessary.
+
+1. Define a new token in dhcp6_parser.yy:
+ @code
+ SUBNET_4O6_INTERFACE_ID "4o6-interface-id"
+ @endcode
+ This defines a token called SUBNET_4O6_INTERFACE_ID that, when it needs to
+ be printed, e.g. in an error message, will be represented as "4o6-interface-id".
+
+2. Tell the lexer how to recognize the new parameter:
+ @code
+ \"4o6-interface-id\" {
+ switch(driver.ctx_) {
+ case isc::dhcp::Parser4Context::SUBNET4:
+ return isc::dhcp::Dhcp4Parser::make_SUBNET_4O6_INTERFACE_ID(driver.loc_);
+ default:
+ return isc::dhcp::Dhcp4Parser::make_STRING("4o6-interface-id", driver.loc_);
+ }
+ }
+ @endcode
+ It tells the parser that when in Subnet4 context, an incoming "4o6-interface-id" string should be
+ represented as the SUBNET_4O6_INTERFACE_ID token. In any other context, it should be represented
+ as a string.
+
+3. Add the rule that will define the value. A user is expected to add something like
+ @code
+ "4o6-interface-id": "whatever"
+ @endcode
+ The rule to match this and similar statements looks as follows:
+ @code
+ subnet_4o6_interface_id: SUBNET_4O6_INTERFACE_ID {
+ ctx.enter(ctx.NO_KEYWORD);
+ } COLON STRING {
+ ElementPtr iface(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("4o6-interface-id", iface);
+ ctx.leave();
+ };
+ @endcode
+ Here's a good example of the context use. We have no idea what sort of interface-id the user will
+ use. Typically that will be an integer, but it may be something weird that happens to match our
+ reserved keywords. Therefore we switch to no keyword context. This tells the lexer to interpret
+ everything as string, integer or float.
+
+4. Finally, extend the existing subnet4_param that defines all allowed parameters
+ in the Subnet4 scope to also cover our new parameter (the new line marked with *):
+ @code
+ subnet4_param: valid_lifetime
+ | renew_timer
+ | rebind_timer
+ | option_data_list
+ | pools_list
+ | subnet
+ | interface
+ | interface_id
+ | id
+ | rapid_commit
+ | client_class
+ | reservations
+ | reservation_mode
+ | relay
+ | match_client_id
+ | authoritative
+ | next_server
+ | subnet_4o6_interface
+ | subnet_4o6_interface_id (*)
+ | subnet_4o6_subnet
+ | unknown_map_entry
+ ;
+ @endcode
+
+5. Regenerate the flex/bison files by typing "make parser".
+
+6. Run the unit-tests that you wrote before you touched any of the bison stuff. You did write them
+ in advance, right?
+*/
diff --git a/doc/devel/config-backend.dox b/doc/devel/config-backend.dox
new file mode 100644
index 0000000..7991307
--- /dev/null
+++ b/doc/devel/config-backend.dox
@@ -0,0 +1,121 @@
+// Copyright (C) 2014-2023 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+
+ @page configBackend Kea Configuration Backends
+
+@section configBackendIntro Introduction
+
+Kea started as a sub-project in BIND10 that used a program (called
+bindctl) to deliver configuration information to its modules. This
+potentially allowed for modules to get their configuration information
+in a variety of ways using what were known as configuration backends.
+After BIND10 was cancelled, the Kea project briefly tried to maintain
+backward compatibility with the BIND10 framework, but the effort
+was discontinued due to lack of interest.
+
+Currently the Kea team does not plan to develop any additional
+configuration backends. Instead, effort is being focused on enhancing
+the current control channel (see @ref ctrlSocket) to be as flexible
+as possible. If you are thinking about developing new ways to
+configure Kea, the recommendation is to write an external piece of
+software that will communicate with Kea using this channel.
+
+@section configBackendHistoric Alternate Configuration Backends
+
+While this section currently has no practical value, it may become useful
+one day to develop a minimalistic, stripped down Kea version that does
+not have any command interface at all. This could prove useful for running
+Kea in embedded regime.
+
+The following steps are needed for the DHCPv4 server to be able to
+process a new method of configuration. (It is assumed that the
+modified component is DHCPv4. Similar approach applies to the other
+components: DHCPv6 or DHCP-DDNS):
+
+-# Write your own implementation of isc::dhcp::ControlledDhcpv4Srv::init(),
+ isc::dhcp::ControlledDhcpv4Srv::init() and isc::dhcp::ControlledDhcpv4Srv::cleanup(),
+ and put it in the src/bin/dhcp4 directory (e.g. as foo_controller.cc).
+-# Modify src/bin/dhcp4/Makefile.am to include your file (e.g. foo_controller.cc) in
+ the build.
+-# Modify the AC_ARG_WITH(kea-config,...) macro in configure.ac to include an
+ entry for your configuration backend.
+-# Add your own AM_CONDITIONAL(CONFIG_BACKEND_FOO, ...) and
+ AC_DEFINE(CONFIG_BACKEND_FOO, ...) macros to configure.ac (following the
+ above-mentioned AC_ARG_WITH macro) to set the C++ macro for your backend.
+-# Modify the sanity check in configure.ac to allow your configuration backend name.
+
+Optionally you can also:
+
+-# Implement unit tests for your backend in the src/bin/dhcp4/tests directory.
+-# Modify src/bin/dhcp4/tests/Makefile.am to include the file(s) containing the
+ unit tests.
+
+@section configBackendJSONDesign The JSON Configuration Backend
+
+The following are some details of the JSON backend framework.
+
+-# Each backend uses the common code for configuration and command
+ processing callbacks. They all assume that JSON formatted parameters are sent
+ and they are expected to return well formatted JSON responses. The exact
+ format of configuration and commands is module-specific.<br/><br/>
+-# A command handler handles the reading the configuration from a
+ file. Its main responsibility is to load the configuration and process
+ it. The JSON backend must call that handler when starting up the server.
+ This is implemented in configure() in the kea_controller.cc files
+ in src/bin/dhcp4 and src/bin/dhcp6 directories.<br/><br/>
+-# The current JSON parser in @ref
+ isc::data::Element::fromJSON() has been extended to allow optional
+ preprocessing. For now, that capability simply removes whole-line
+ comments starting with the hash character, but it is expected to grow over
+ time (in-line comments and file inclusions are the obvious envisaged
+ additions). This is implemented in @ref isc::data::Element::fromJSONFile.<br/><br/>
+-# The current format of the BIND10 configuration file (BIND 10 stored its
+ configuration in (installation directory) /var/bind10/b10-config.db) has been
+ retained as the configuration file format. Its actual naming is now arbitrary
+ and left up to the user (it is passed as a parameter to the -c command line
+ option). From the implementation perspective, this is slight change
+ from the BIND10 days, as back then a subset of the configuration was received by
+ the daemon processes. Nowadays the whole configuration is passed. To take a
+ specific example, the following is how b10-config.db looked many years ago:
+ @code
+ {
+ "Init": { ... }
+ "Dhcp4": {
+ "subnet4" { subnet definitions here },
+ "option-data" { option data here },
+ "interfaces": [ "eth0" ],
+ ...
+ },
+ "Dhcp6": {
+ "subnet6" { subnet definitions here },
+ "option-data" { option data here },
+ "interfaces": [ "eth0" ],
+ ...
+ },
+ "Logging": {
+ "Loggers": [{"name": *, "severity": "DEBUG" }]
+ }
+ }
+ @endcode
+ The Kea components used to receive only relevant parts of it (e.g. Kea4
+ received configuration data that only contained the content of the Dhcp4 element).
+ Now each component receives all of it: the code
+ iterates over the top level elements and picks the appropriate
+ tree (or get the element by name). That approach makes the common configuration
+ (such as the logging initialization code) very easy to share among Kea4, Kea6 and
+ DHCP-DDNS.<br/><br/>
+-# The .spec files used in BIND 10 by the control program to validate commands
+ have been removed in 1.4.<br/><br/>
+-# A shell script has been added (as src/bin/keactrl/keactrl) to
+ start, stop and reconfigure the daemons. Its only
+ job is to pass the configuration file to each daemon and remember its PID file, so
+ that sending signals is possible (for configuration reload or shutdown). It is also
+ able to print out a status.<br/><br/>
+-# The capability to share the same configuration file between servers and
+ agents, and the Logging toplevel entry, were removed in 1.7.10.
+*/
diff --git a/doc/devel/congestion-handling.dox b/doc/devel/congestion-handling.dox
new file mode 100644
index 0000000..83079e8
--- /dev/null
+++ b/doc/devel/congestion-handling.dox
@@ -0,0 +1,452 @@
+// Copyright (C) 2018-2023 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+
+@page congestionHandling Congestion Handling in Kea DHCP Servers
+
+@section background What is Congestion?
+Congestion occurs when servers are subjected to client queries
+faster than they can be fulfilled. Subsequently, the servers begin
+accumulating a backlog of pending queries. The longer the high rate of
+traffic continues the farther behind the servers fall. Depending on the
+client implementations, those that fail to get leases either give up or simply
+continue to retry forever. In the former case, the server may eventually
+recover. The latter case is vicious cycle from which the server is unable
+to escape.
+
+In a well-planned deployment, the number and capacity of servers is matched
+to the maximum client loads expected. As long as capacity is matched to
+load, congestion does not occur. If the load is routinely too heavy, then
+the deployment needs to be re-evaluated. Congestion typically occurs when
+there is a network event that causes overly large numbers of clients to
+simultaneously need leases such as recovery after a network outage.
+
+@section introduction Congestion Handling Overview
+
+Kea 1.5.0 introduces a new feature referred to as Congestion Handling. The
+goal of Congestion Handling is to help the servers mitigate the peak
+in traffic by fulfilling as many of the most relevant requests as possible
+until it subsides.
+
+Prior to Kea 1.5.0, Kea DHCP servers read inbound packets directly
+from the interface sockets in the main application thread. This meant that
+packets waiting to be processed were held in socket buffers themselves. Once
+these buffers fill any new packets are discarded. Under swamped conditions
+the servers can end up processing client packets that may no longer be
+relevant, or worse are redundant. In other words, the packets waiting in
+the FIFO socket buffers become increasingly stale.
+
+Congestion Handling offers the ability to configure the server to use a
+separate thread to read packets from the interface socket buffers. As the
+thread reads packets from the buffers they are added to an internal "packet
+queue". The server's main application thread processes packets from this queue
+rather than the socket buffers. By structuring it this way, we've introduced
+a configurable layer which can make decisions on which packets to process,
+how to store them, and the order in which they are processed by the server.
+
+The default packet queue implementation for both Kea DHCPv4 and DHCPv6 servers
+is a simple ring buffer. Once it reaches capacity, new packets get added to
+the back of queue by discarding packets from the front of queue. Rather than
+always discarding the newest packets, we now always discard the oldest
+packets. The capacity of the buffer (i.e. the maximum number of packets the
+buffer can contain) is configurable.
+
+@section custom-implementations Custom Packet Queues
+
+It is possible to replace the default packet queue implementation with a
+custom implementation by registering it with your Kea server via a hook
+library. The steps for doing this are listed below:
+
+-# Develop a derivation of the interface isc::dhcp::PacketQueue
+-# Registering and un-registering your implementation via Hook library
+-# Configure your Kea server to use your derivation
+
+(If you are not familiar with writing Kea hook libraries, you may wish to
+read @ref hooksdgDevelopersGuide before continuing).
+
+@subsection packet-queue-derivation Developing isc::dhcp::PacketQueue Derivations
+ @subsection packet-queue-derivation-basics The Basics
+
+Your custom packet queue must derive from the class template,
+isc::dhcp::PacketQueue. The class is almost entirely abstract and
+deliberately brief to provide developers wide latitude in the internals
+of their solutions.
+
+The template argument, @c PacketTypePtr, is expected to be either
+isc::dhcp::Pkt4Ptr or isc::dhcp::Pkt6Ptr, depending upon which
+protocol the implementation will handle. Please note that the
+while following text and examples largely focus on DHCPv4 out
+of convenience as the concepts are identical for DHCPv6. For
+completeness there are code snippets at the end of this
+chapter for DHCPv6.
+
+The two primary functions of interest are:
+
+-# isc::dhcp::PacketQueue::enqueuePacket() - This function is invoked by
+the receiver thread each time a packet has been read from an interface
+socket buffer and should be added to the queue. It is passed a pointer to
+the unpacked client packet (isc::dhcp::Pkt4Ptr or isc::dhcp::Pkt6Ptr), and
+a reference to the isc::dhcp::SocketInfo describing the interface socket
+from which the packet was read. Your derivation is free to use whatever
+logic you deem appropriate to decide if a given packet should be added
+to the queue or dropped. The socket information is passed along to be used
+(or not) in your decision making. The simplest derivation would add every
+packet, every time.
+
+-# isc::dhcp::PacketQueue::dequeuePacket() - This function is invoked by the
+server's main thread whenever the receiver thread indicates that packets are
+ready. Which packet is dequeued and returned is entirely up to your
+derivation.
+
+The remaining functions that you'll need to implement are self-explanatory.
+
+How your actual "queue" is implemented is entirely up to you. Kea's default
+implementation using a ring buffer based on Boost's boost::circular_buffer
+(please refer to isc::dhcp::PacketQueueRing, isc::dhcp::PacketQueueRing4 and
+isc::dhcp::PacketQueueRing6). The most critical aspects to remember when
+developing your implementation are:
+
+-# It MUST be thread safe since queuing and dequeuing packets are done by
+separate threads. (You might considering using std::mutex and std::lock_guard).
+
+-# Its efficiency (or lack thereof) will have a direct impact on server
+performance. You will have to consider the dynamics of your deployment
+to determine where the trade-off lies between the volume of packets responded
+to and preferring to respond to some subset of those packets.
+
+ @subsection packet-queue-derivation-factory Defining a Factory
+
+isc::dhcp::IfaceMgr using two derivations of isc::dhcp::PacketQueueMgr (one for
+DHCPv4 and one for DHCPv6), to register queue implementations and instantiate
+the appropriate queue type based the current configuration. In order to
+register your queue implementation your hook library must provide a factory
+function that will be used to create packet queues. This function will be
+invoked by the server during the configuration process to instantiate the
+appropriate queue type. For DHCPv4, the factory should be as follows:
+
+@code
+ PackQueue4Ptr factory(isc::data::ConstElementPtr parameters)
+@endcode
+
+and for DHCPv6:
+
+@code
+ PackQueue6Ptr factory(isc::data::ConstElementPtr parameters)
+@endcode
+
+The factory's only argument is an isc::data::ConstElementPtr. This is will be
+an isc::data::MapElement instance containing the contents of the configuration
+element "dhcp-queue-control" from the Kea server's configuration. It will
+always have the following two values:
+
+-# "enable-queue" - used by isc::dhcp::IfaceMgr to know whether
+congestion handling is enabled. Your implementation need not do anything
+with this value.
+
+-# "queue-type" - name of the registered queue implementation to use.
+It is used by isc::dhcp::IfaceMgr to invoke the appropriate queue factory.
+Your implementation must pass this value through to the isc::dhcp::PacketQueue
+constructor.
+
+Beyond that you may add whatever additional values you may require. In
+other words, the content is arbitrary so long as it is valid JSON. It is
+up to your factory implementation to examine the contents and use them
+to construct a queue instance.
+
+ @subsection packet-queue-derivation-example An Example
+
+Let's suppose you wish to develop a queue for DHCPv4 and your implementation
+requires two configurable parameters: capacity and threshold. Your class
+declaration might look something like this:
+
+@code
+class YourPacketQueue4 : public isc::dhcp::PacketQueue<isc::dhcp::Pkt4Ptr> {
+public:
+
+ // Logical name you will register your factory under.
+ static const std::string QUEUE_TYPE;
+
+ // Factory for instantiating queue instances.
+ static isc::dhcp::PacketQueue4Ptr factory(isc::data::ConstElementPtr params);
+
+ // Constructor
+ YourPacketQueue4(const std::string& queue_type, size_t capacity, size_t threshold)
+ : isc::dhcp::PacketQueue<isc::dhcp::Pkt4Ptr>(queue_type) {
+
+ // your constructor steps here
+ }
+
+ // Adds a packet to your queue using your secret formula based on threshold.
+ virtual void enqueuePacket(isc::dhcp::Pkt4Ptr packet, const dhcp::SocketInfo& source);
+
+ // Fetches the next packet to process from your queue using your other secret formula.
+ virtual isc::dhcp::Pkt4Ptr dequeuePacket();
+
+ : // Imagine you prototyped the rest of the functions
+
+};
+@endcode
+
+Your factory implementation would then look something like this:
+
+@code
+
+const std::string QUEUE_TYPE = "Your-Q4";
+
+isc::dhcp::PacketQueue4Ptr
+YourPacketQueue4::factory(isc::data::ConstElementPtr parameters) {
+
+ // You need queue-type to pass into the base class.
+ // It's guaranteed to be here.
+ std::string queue_type = isc::data::SimpleParser::getString(parameters, "queue-type");
+
+ // Now you need to fetch your required parameters.
+ size_t capacity;
+ try {
+ capacity = isc::data::SimpleParser::getInteger(parameters, "capacity");
+ } catch (const std::exception& ex) {
+ isc_throw(isc::dhcp::InvalidQueueParameter, "YourPacketQueue4:factory:"
+ " 'capacity' parameter is missing/invalid: " << ex.what());
+ }
+
+ size_t threshold;
+ try {
+ threshold = isc::data::SimpleParser::getInteger(parameters, "threshold");
+ } catch (const std::exception& ex) {
+ isc_throw(isc::dhcp::InvalidQueueParameter, "YourPacketQueue4:factory:"
+ " 'threshold' parameter is missing/invalid: " << ex.what());
+ }
+
+ // You should be all set to create your queue instance!
+ isc::dhcp::PacketQueue4Ptr queue(new YourPacketQueue4(queue_type, capacity, threshold));
+ return (queue);
+}
+
+@endcode
+
+Kea's configuration parser cannot know your parameter requirements and thus
+can only flag JSON syntax errors. Thus it is important for your factory to
+validate your parameters according to your requirements and throw meaningful
+exceptions when they are not met. This allows users to know what to correct.
+
+@subsection packet-queue-registration Registering Your Implementation
+
+All hook libraries must provide a load() and unload() function. Your hook
+library should register you queue factory during load() and un-register it
+during unload(). Picking up with the our example, those functions might
+look something like this:
+
+@code
+// This function is called when the library is loaded.
+//
+// param - handle library handle (we aren't using it)
+// return - 0 when initialization is successful, 1 otherwise
+int load(LibraryHandle& /* handle */) {
+ try {
+ // Here you register your DHCPv4 queue factory
+ isc::dhcp::IfaceMgr::instance().getPacketQueueMgr4()->
+ registerPacketQueueFactory(YourPacketQueue4::QUEUE_TYPE,
+ YourPacketQueue::factory);
+ } catch (const std::exception& ex) {
+ LOG_ERROR(your_logger, YOUR_LOAD_FAILED)
+ .arg(ex.what());
+ return (1);
+ }
+
+ LOG_INFO(your_logger, YOUR_LOAD_OK);
+ return (0);
+}
+
+// This function is called when the library is unloaded.
+//
+// return - 0 if deregistration was successful, 1 otherwise
+int unload() {
+
+ // You need to remove your queue factory. This must be done to make sure
+ // your queue instance is destroyed before your library is unloaded.
+ isc::dhcp::IfaceMgr::instance().getPacketQueueMgr4()->
+ unregisterPacketQueueFactory(YourPacketQueue4::QUEUE_TYPE);
+
+ LOG_INFO(your_logger, YOUR_UNLOAD_OK);
+ return (0);
+}
+@endcode
+
+@subsection packet-queue-factory Configuring Kea to use YourPacketQueue4
+
+You're almost there. You developed your implementation, you've unit tested it
+(You did unit test it right?). Now you just have to tell Kea to load it and
+use it. Continuing with the example, your kea-dhcp4 configuration would need
+to look something like this:
+
+@code
+{
+"Dhcp4":
+{
+ ...
+
+ "hooks-libraries": [
+ {
+ # Loading your hook library!
+ "library": "/somepath/lib/libyour_packet_queue.so"
+ }
+
+ # any other hook libs
+ ],
+
+ ...
+
+ "dhcp-queue-control": {
+ "enable-queue": true,
+ "queue-type": "Your-Q4",
+ "capacity" : 100,
+ "threshold" : 75
+ },
+
+ ...
+}
+@endcode
+
+@subsection packet-queue-example-dhcpv6 DHCPv6 Example Snippets
+
+For completeness, this section includes the example from above
+implemented for DHCPv6.
+
+DHCPv6 Class declaration:
+
+@code
+class YourPacketQueue6 : public isc::dhcp::PacketQueue<isc::dhcp::Pkt6Ptr> {
+public:
+
+ // Logical name you will register your factory under.
+ static const std::string QUEUE_TYPE;
+
+ // Factory for instantiating queue instances.
+ static isc::dhcp::PacketQueue6Ptr factory(isc::data::ConstElementPtr params);
+
+ // Constructor
+ YourPacketQueue6(const std::string& queue_type, size_t capacity, size_t threshold)
+ : isc::dhcp::PacketQueue<isc::dhcp::Pkt6Ptr>(queue_type) {
+
+ // your constructor steps here
+ }
+
+ // Adds a packet to your queue using your secret formula based on threshold.
+ virtual void enqueuePacket(isc::dhcp::Pkt6Ptr packet, const dhcp::SocketInfo& source);
+
+ // Fetches the next packet to process from your queue using your other secret formula.
+ virtual isc::dhcp::Pkt6Ptr dequeuePacket();
+
+ : // Imagine you prototyped the rest of the functions
+
+};
+@endcode
+
+DHCPv6 Factory implementation:
+
+@code
+const std::string QUEUE_TYPE = "Your-Q6";
+
+isc::dhcp::PacketQueue6Ptr
+YourPacketQueue6::factory(isc::data::ConstElementPtr parameters) {
+
+ // You need queue-type to pass into the base class.
+ // It's guaranteed to be here.
+ std::string queue_type = isc::data::SimpleParser::getString(parameters, "queue-type");
+
+ // Now you need to fetch your required parameters.
+ size_t capacity;
+ try {
+ capacity = isc::data::SimpleParser::getInteger(parameters, "capacity");
+ } catch (const std::exception& ex) {
+ isc_throw(isc::dhcp::InvalidQueueParameter, "YourPacketQueue6:factory:"
+ " 'capacity' parameter is missing/invalid: " << ex.what());
+ }
+
+ size_t threshold;
+ try {
+ threshold = isc::data::SimpleParser::getInteger(parameters, "threshold");
+ } catch (const std::exception& ex) {
+ isc_throw(isc::dhcp::InvalidQueueParameter, "YourPacketQueue6:factory:"
+ " 'threshold' parameter is missing/invalid: " << ex.what());
+ }
+
+ // You should be all set to create your queue instance!
+ isc::dhcp::PacketQueue6Ptr queue(new YourPacketQueue6(queue_type, capacity, threshold));
+ return (queue);
+}
+@endcode
+
+DHCPv6 Hook load/unload functions
+
+@code
+// This function is called when the library is loaded.
+//
+// param - handle library handle (we aren't using it)
+// return - 0 when initialization is successful, 1 otherwise
+int load(LibraryHandle& /* handle */) {
+ try {
+ // Here you register your DHCPv6 queue factory
+ isc::dhcp::IfaceMgr::instance().getPacketQueueMgr6()->
+ registerPacketQueueFactory(YourPacketQueue6::QUEUE_TYPE,
+ YourPacketQueue::factory);
+ } catch (const std::exception& ex) {
+ LOG_ERROR(your_logger, YOUR_LOAD_FAILED)
+ .arg(ex.what());
+ return (1);
+ }
+
+ LOG_INFO(your_logger, YOUR_LOAD_OK);
+ return (0);
+}
+
+// This function is called when the library is unloaded.
+//
+// return - 0 if deregistration was successful, 1 otherwise
+int unload() {
+
+ // You need to remove your queue factory. This must be done to make sure
+ // your queue instance is destroyed before your library is unloaded.
+ isc::dhcp::IfaceMgr::instance().getPacketQueueMgr6()->
+ unregisterPacketQueueFactory(YourPacketQueue6::QUEUE_TYPE);
+
+ LOG_INFO(your_logger, YOUR_UNLOAD_OK);
+ return (0);
+}
+@endcode
+
+Server configuration for kea-dhcp6:
+
+@code
+{
+"Dhcp6":
+{
+ ...
+
+ "hooks-libraries": [
+ {
+ # Loading your hook library!
+ "library": "/somepath/lib/libyour_packet_queue.so"
+ }
+
+ # any other hook libs
+ ],
+
+ ...
+
+ "dhcp-queue-control": {
+ "enable-queue": true,
+ "queue-type": "Your-Q6",
+ "capacity" : 100,
+ "threshold" : 75
+ },
+
+ ...
+}
+@endcode
+
+*/
diff --git a/doc/devel/contribute.dox b/doc/devel/contribute.dox
new file mode 100644
index 0000000..64ddf50
--- /dev/null
+++ b/doc/devel/contribute.dox
@@ -0,0 +1,16 @@
+// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+
+ @page contributorGuide Kea Contributor's Guide
+
+The Contributor's Guide has been moved to a stand alone document.
+See CONTRIBUTORS.md in the top level sources or:
+
+Link: https://gitlab.isc.org/isc-projects/kea/blob/master/CONTRIBUTING.md
+
+*/
diff --git a/doc/devel/cross-compile.dox b/doc/devel/cross-compile.dox
new file mode 100644
index 0000000..cc7685e
--- /dev/null
+++ b/doc/devel/cross-compile.dox
@@ -0,0 +1,226 @@
+// Copyright (C) 2020-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+
+ @page crossCompile Kea Cross-Compiling Example
+
+The idea is to install Kea on a Raspberry Pi 4 Model B running Raspbian
+operation system (e.g. the
+<a href="https://www.raspberrypi.org/software/operating-systems/">
+Raspbian Buster with desktop and recommended software</a> distribution)
+without having to compile Kea on the Raspberry Pi box itself as it
+takes some hours so using a standard Linux box with a x86_64 processor.
+
+To cross-compile anything for Raspbian you need:
+ - a cross-compiler running on x86_64 producing arm binaries
+ - either an image of system copied from a Raspberry disk or extracted
+ from a Raspbian repository.
+
+@section toolChain Cross-Compile Tool Chain
+
+A priori it is possible to compile your own tool chain or to use
+a package as the arm-linux-gnueabihf one on Ubuntu. But there is
+reported compatibility issue with old Raspberry Pi versions) so
+we recommend a pre-built dedicated tool chain for this purpose:
+<a href="https://github.com/Pro/raspi-toolchain">RaspberryPi toolchain on github</a>.
+
+The documentation of this tool chain gives a rsync command which
+copies selected parts of the Raspberry Pi root filesystem ("rootfs").
+If you have no access to a running Raspberry Pi it is still possible
+to get them following next section instructions. If you have, simply
+skip this part.
+
+@section noRaspberry How to get system and packages without a running Raspberry Pi
+
+It is not required to have access to a running Raspberry Pi.
+The system disk image is available at the Raspbian URL.
+Packages are in the Raspian repository which is given in
+sources list files in this disk image or below.
+
+The /etc/apt/sources.list file content is:
+@verbatim
+deb http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi
+# Uncomment line below then 'apt-get update' to enable 'apt-get source'
+# deb-src http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi
+@endverbatim
+
+and the /etc/apt/sources.list.d/raspi.list file content is:
+@verbatim
+deb http://archive.raspberrypi.org/debian/ buster main
+# Uncomment line below then 'apt-get update' to enable 'apt-get source'
+# deb-src http://archive.raspberrypi.org/debian/ buster main
+@endverbatim
+
+The disk image is a zipped image of a 4GB disk with a standard MSDOS
+boot block with two volumes:
+ - boot (useless for this purpose)
+ - rootfs (the Raspberry Pi root filesystem)
+
+The idea is to mount the rootfs on the Linux box (it can work on another
+system as soon as it supports the ext4 file system type):
+ - first use fdisk or similar tool to get the offset of the first block
+ of the rootfs (second) volume
+ - if the offset is in block unit multiply by 512 (block size)
+ - mount as root (sudo mount ...) with the loop option, the offset option,
+ the unzipped image file name and a mount point (absolute path of
+ a directory)
+If you have a SD card with Raspbian installed on it and a SD reader
+you can directly mount the rootfs from it.
+
+If a dependency (i.e. the Raspbian version of a library) is not in the
+rootfs image you need to simulate its installation:
+ - get the .deb file from a Raspbian repository
+ - extract files using the dpkg-deb -R tool on the .deb file
+ - install the files (usually in the "rootfs"/usr directory)
+The idea is the files (includes and libraries) can be found by
+the cross-compiling tool chain.
+
+It is possible to emulate a Raspberry Pi using qemu even I do not think
+it can run Kea. But at least it can run some tests as the hello world sample
+of the tool chain. Required qemu kernels can be found in
+<a href="https://github.com/dhruvvyas90/qemu-rpi-kernel">this github repo</a> with
+a documentation, which is well worth reading.
+
+@section crossCompilePitfalls Usual problems
+
+There are two usual problems when cross-compiling:
+ - have a binary for the wrong processor, e.g. either trying to run
+ an arm binary on the x86_64 box or building a x86_64 binary when
+ it will be run by the Raspberry Pi
+ - use the x86_64 system include or library instead of the Raspberry Pi
+ one from the rootfs image. Usually it gives a direct error for a library
+ but a wrong include is more subtle.
+Note that Kea has a build tool (kea-msg-compiler) but its use is optional
+so it should not be a problem. If anyway you need it simply copy it from
+a native (i.e. not cross-compiled) Kea build.
+
+@section raspbianDependencies Usual Kea Dependencies
+
+Required and optional Kea dependencies, usually available as packages:
+ - Python (built-in)
+ - libssl-dev (built-in in the full image)
+ - liblog4cplus-dev (in liblog4cplus package, load both the library and
+ the development part)
+ - libboost-system-dev (in boost-xxx, load both the boost-system and
+ the boost-libraries packages, the second includes header files)
+ - googletest (download the last release from github)
+ - doc (sphinx, texlive, etc: just generate docs on the build system)
+ - MySQL (in mysql-defaults and mariadb-* packages?)
+ - PostgreSQL (in postgresql-12?)
+
+@section prepareCrossCompile Prepare Cross Compiling
+
+This script was used with success: it sets the environment variables
+before calling ./configure.
+@code
+# change when at another location
+export ROOTFS="$HOME/rpi/rootfs"
+
+# build commands
+export BUILD=x86_64-pc-linux-gnu
+export HOST=arm-linux-gnueabihf
+export CC="${HOST}-gcc"
+export CXX="${HOST}-g++"
+export LD="${HOST}-ld"
+export AR="${HOST}-ar"
+export RANLIB="${HOST}-ranlib"
+export STRIP="${HOST}-strip"
+export NM="${HOST}-nm"
+
+# g++ flags
+CXXFLAGS="-O2 -g -isysroot ${ROOTFS} -I${ROOTFS}/usr/include"
+CXXFLAGS+=" -I${ROOTFS}/usr/include/${HOST}"
+
+# ld flags
+LDFLAGS="--sysroot=${ROOTFS}"
+LDFLAGS+=" -L/opt/cross-pi-gcc/arm-linux-gnueabihf/lib -Wl,-rpath-link,/opt/cross-pi-gcc/arm-linux-gnueabihf/lib"
+LDFLAGS+=" -L/opt/cross-pi-gcc/lib -Wl,-rpath-link,/opt/cross-pi-gcc/lib"
+LDFLAGS+=" -L${ROOTFS}/opt/vc/lib -Wl,-rpath-link,${ROOTFS}/opt/vc/lib"
+LDFLAGS+=" -L${ROOTFS}/lib/${HOST} -Wl,-rpath-link,${ROOTFS}/lib/${HOST}"
+LDFLAGS+=" -L${ROOTFS}/usr/local/lib -Wl,-rpath-link,${ROOTFS}/usr/local/lib"
+LDFLAGS+=" -L${ROOTFS}/usr/lib/${HOST} -Wl,-rpath-link,${ROOTFS}/usr/lib/${HOST}"
+LDFLAGS+=" -L${ROOTFS}/usr/lib -Wl,-rpath-link,${ROOTFS}/usr/lib"
+LDFLAGS+=" -L${ROOTFS}/usr/lib/${HOST}/blas -Wl,-rpath-link,${ROOTFS}/usr/lib/${HOST}/blas"
+LDFLAGS+=" -L${ROOTFS}/usr/lib/${HOST}/lapack -Wl,-rpath-link,${ROOTFS}/usr/lib/${HOST}/lapack"
+
+# CPU tuning (use the most generic one)
+# PI 4 (not sure for the FPU)
+# CXXFLAGS+=" -mcpu=cortex-a72 -mfpu=neon-vfpv4 -mfloat-abi=hard"
+# PI 3
+# CXXFLAGS+=" -mcpu=cortex-a53 -mfpu=neon-vfpv4 -mfloat-abi=hard"
+# PI 2
+# CXXFLAGS+=" -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard"
+# PI 1, zero, ...
+CXXFLAGS+=" -mcpu=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard"
+
+export CXXFLAGS
+export LDFLAGS
+
+export PKG_CONFIG_PATH="${ROOTFS}/usr/lib/pkgconfig"
+
+export PATH=/opt/cross-pi-gcc/bin:/opt/cross-pi-gcc/libexec/gcc/arm-linux-gnueabihf/8.3.0:$PATH
+
+# libraries are in fact in ${ROOTFS}/usr/lib/${HOST} but
+# the library path can be set only for boost.
+CONF_CMD="./configure --build=${BUILD} --host=${HOST}"
+CONF_CMD+=" --with-sysroot=${ROOTFS}"
+CONF_CMD+=" --with-openssl=${ROOTFS}/usr"
+CONF_CMD+=" --with-log4cplus=${ROOTFS}/usr"
+CONF_CMD+=" --with-boost-include=${ROOTFS}/usr/include"
+CONF_CMD+=" --with-boost-lib-dir=${ROOTFS}/usr/lib/${HOST}"
+@endcode
+
+Some explanations:
+ - the rootfs was copied or mounted in rpi/rootfs in the home directory.
+ - the build system triplet is x86_64-pc-linux-gnu (processor,
+ system, application binary interface). It is returned by the config.guess
+ script so please verify.
+ - the host system triplet is arm-linux-gnueabihf. It is used as the prefix
+ for cross-compiling tools so this value is critical.
+ - all tool variables are set to the cross-compiling tool name
+ - CXXFLAGS is defined to use the rootfs image for includes. It is critical
+ it does not use any build system include.
+ - LDFLAGS is defined to use the rootfs, all cross-compiler support libraries
+ and libraries from the rootfs image. It is critical it does not use
+ any build system library.
+ - CXXFLAGS can be tuned for a specific Raspberry Pi version. Proposed
+ tuning supports all versions.
+ - even if Kea ./configure does not depends on pkgconfig its path is set.
+ - PATH is updated to find first cross-compiling tools.
+ - I did not try yet database config scripts: perhaps they detect
+ cross-compiling and produce correct paths.
+ - CONF_CMD contains the ./configure common arguments.
+
+The script can be used by:
+ - eventually run "autoreconf -i" (if sources are from git)
+ - put its content in a file, e.g. ccenv
+ - load the file by ". ccenv"
+ - configure Kea build by "$CONF_CMD <your arguments>"
+
+Known problems:
+ - AC_TRY_RUN and AC_CHECK_FILE[S] autoconf macros do not support
+ cross-compiling. They were removed from ./configure.ac in Kea 1.7.10.
+ - libtool is a disaster for cross-compiling, in particular it produces
+ silly libtool archive (.la) files. Fortunately they are useless.
+ - bad paths for mkdir or sed on Raspbian.
+ - recent Debian systems including recent Ubuntu modified libtool to
+ not accept indirect dependencies. Makefiles were updated to have no
+ such indirect dependencies in common cases as it is in Kea Makefile
+ writing guidelines.
+ - the libtool variable managing this is link_all_deplibs. You can
+ edit the libtool script to set it to unknown or yes. Or simply
+ use another Linux distrib.
+ - there is no ldd in cross-compiling tools. The table of used
+ shared libraries in available by readelf -d which is in the
+ cross-compiling tools.
+
+Final words: this is still highly experimental and does not cover
+everything. New ways to offload almost everything outside the Raspberry
+Pi are still to be found. Currently to provide Raspbian Kea packages
+is not possible for ISC.
+
+*/
diff --git a/doc/devel/debug.dox b/doc/devel/debug.dox
new file mode 100644
index 0000000..556ed2e
--- /dev/null
+++ b/doc/devel/debug.dox
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+
+ @page debug Debugging Kea
+
+@section debugSymbols Enabling debug symbols
+
+The --enable-debug flag can be useful when developing, since it
+makes the compiler produce more abundant debugging information that
+can be read by a debugger.
+
+Some compilers don't document some of the flags, such as clang for
+-g3. However, practice shows that clang behaves the same way as g++
+in that regard. As an experiment, providing -g4 results in
+`error: unknown argument: '-g4'`, but providing -g3 succesfully
+compiles, and supposedly puts it into effect.
+
+On top of that, the flag enables log4cplus's own logging, and adds
+more sanity checks in DNS code.
+
+*/
diff --git a/doc/devel/doc.dox b/doc/devel/doc.dox
new file mode 100644
index 0000000..cde7f90
--- /dev/null
+++ b/doc/devel/doc.dox
@@ -0,0 +1,86 @@
+// Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+
+ @page docs Building Kea Documentation
+
+ There are several types of documentation for Kea. The primary one, intended to
+ be read by users, is User's Guide. It comes in HTML, PDF and txt format. All
+ of them generated from the same sources. To generate this doc, you need to
+ run configure script with --enable-generate-docs option. sphinx has to be
+ enabled in the system.
+ You can generate this by doing:
+@code
+$ ./configure --enable-generate-docs
+$ make -C ./doc
+@endcode
+
+The output files will be generated in the ./doc/sphinx/_build directory.
+
+The ARM has an appendix that lists all Kea commands. The commands are integrated
+into RST using the tool located at doc/sphinx/api2doc.py. The basic principle
+is that for every command there is a JSON file that briefly describes the major
+aspects such as name, short description, expected syntax, expected response,
+a hook that needs to be loaded, first Kea version where it appeared, etc.
+Those JSON files are loaded by the api2doc.py tool that will generate api.txt
+that will be used by sphinx. There is no need to call this tool explicitly.
+It is called automatically when building the ARM.
+
+Since Kea 1.9.9, the ARM has an appendix with the grammar. If there were new
+parameters added, you can regenerate the grammars and the appendix with the
+following procedure:
+
+@code
+$ autoreconf -i
+$ ./configure --enable-generate-docs --enable-generate-parser
+$ cd doc
+$ make grammar
+$ make -C sphinx html
+@endcode
+
+After that, inspect the html output and make sure it's ok, review changes in
+\c doc/sphinx/grammar/ and then check in those that are something more than a date
+update. The date is there, so we (and users) can determine if the grammar
+is or isn't out of date.
+
+@section docsNewCommand Documenting new command
+
+There are several steps needed to document a new API command:
+
+ 1. Configure sources with ./configure --enable-generate-docs
+ 1. Copy src/share/api/_template.json to appropriate name.
+ 2. Remove comments from it and fill in the actual content.
+ 3. Update api_files.mk file in src/share/api/Makefile.am
+ 4. make html will generate multi-page html.
+ 5. make singlehtml will generate a single page html.
+
+A word of caution regaring editing JSON files. The files themselves need to be
+valid JSON files. They also often contain fields, such as command syntax or
+command response, there are themselves a JSON or JSON like structures. That
+means that some trickery with escaping double quotes will be involved. Note
+there is no need to escape any other character, unless you want to specify
+non-printable characters.
+
+Also, while Kea's JSON parser supports comments and multi-line string, they
+are not part of JSON standard. That means that external tools, such as python
+or Sphinx parsers are not able to deal with them. Therefore comments must
+be removed and long strings (such as command descriptions or example invocations)
+are to be presented as a list of strings ( e.g. [ "line1", "line2, "line3" ]).
+
+@section docsDevelGuide Generating Developer's Guide
+
+Generating Developer's Guide is very simple, although you need to have
+doxygen installed in your system. If you also have graphviz installed, it will
+generate nice diagrams. To generate developer's guide, do the following commands:
+
+@code
+$ ./configure
+$ cd doc/devel
+$ make devel
+@endcode
+
+*/
diff --git a/doc/devel/fuzz.dox b/doc/devel/fuzz.dox
new file mode 100644
index 0000000..f4ec931
--- /dev/null
+++ b/doc/devel/fuzz.dox
@@ -0,0 +1,303 @@
+// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+@page fuzzer Fuzzing Kea
+
+@section fuzzIntro Introduction
+
+Fuzzing is a software-testing technique whereby a program is presented with a
+variety of generated data as input and is monitored for abnormal conditions
+such as crashes or hangs.
+
+Fuzz testing of Kea uses the AFL (American Fuzzy Lop) program. In this, Kea is
+built using an AFL-supplied program that not only compiles the software but
+also instruments it. When run, AFL generates test cases and monitors the
+execution of Kea as it processes them. AFL will adjust the input based on
+these measurements, seeking to discover and test new execution paths.
+
+@section fuzzTypes Types of Kea Fuzzing
+
+@subsection fuzzTypeNetwork Fuzzing with Network Packets
+
+In this mode, AFL will start an instance of Kea and send it a packet of data.
+Kea reads this packet and processes it in the normal way. AFL monitors code
+paths taken by Kea and, based on this, will vary the data sent in subsequent
+packets.
+
+@subsection fuzzTypeConfig Fuzzing with Configuration Files
+
+Kea has a configuration file check mode whereby it will read a configuration
+file, report whether the file is valid, then immediately exit. Operation of
+the configuration parsing code can be tested with AFL by fuzzing the
+configuration file: AFL generates example configuration files based on a
+dictionary of valid keywords and runs Kea in configuration file check mode on
+them. As with network packet fuzzing, the behaviour of Kea is monitored and
+the content of subsequent files adjusted accordingly.
+
+@section fuzzBuild Building Kea for Fuzzing
+
+Whatever tests are done, Kea needs to be built with fuzzing in mind. The steps
+for this are:
+
+-# Install AFL on the system on which you plan to build Kea and do the fuzzing.
+ AFL may be downloaded from http://lcamtuf.coredump.cx/afl. At the time of
+ writing (August 2019), the latest version is 2.52b. AFL should be built as
+ per the instructions in the README file in the distribution. The LLVM-based
+ instrumentation programs should also be built, as per the instructions in
+ the file llvm_mode/README.llvm (also in the distribution). Note that this
+ requires that LLVM be installed on the machine used for the fuzzing.
+
+-# Build Kea. Kea should be compiled and built as usual, although the
+ following additional steps should be observed:
+ - Set the environment variable CXX to point to the afl-clang-fast++
+ compiler.
+ - Specify a value of "--prefix" on the command line to set the directory
+ into which Kea is installed.
+ - Add the "--enable-fuzz" switch to the "configure" command line.
+ .
+ For example:
+ @code
+ CXX=/opt/afl/afl-clang-fast++ ./configure --enable-fuzz --prefix=$HOME/installed
+ make
+ @endcode
+
+-# Install Kea to the directory specified by "--prefix":
+ @code
+ make install
+ @endcode
+ This step is not strictly necessary, but makes running AFL easier.
+ "libtool", used by the Kea build procedure to build executable images, puts
+ the executable in a hidden ".libs" subdirectory of the target directory and
+ creates a shell script in the target directory for running it. The wrapper
+ script handles the fact that the Kea libraries on which the executable depends
+ are not installed by fixing up the LD_LIBRARY_PATH environment variable to
+ point to them. It is possible to set the variable appropriately and use AFL
+ to run the image from the ".libs" directory; in practice, it is a lot
+ simpler to install the programs in the directories set by "--prefix" and run
+ them from there.
+
+@section fuzzRun Running the Fuzzer
+
+@subsection fuzzRunNetwork Fuzzing with Network Packets
+
+-# In this type of fuzzing, Kea is processing packets from the fuzzer over a
+ network interface. This interface could be a physical interface or it could
+ be the loopback interface. Either way, it needs to be configured with a
+ suitable IPv4 or IPv6 address depending on whether kea-dhcp4 or kea-dhcp6 is
+ being fuzzed.
+
+-# Once the interface has been decided, these need to be set in the
+ configuration file used for the test. For example, to fuzz Kea-dhcp4
+ using the loopback interface "lo" and IPv4 address 10.53.0.1, the
+ configuration file would contain the following snippet:
+ @code
+ "Dhcp4": {
+ :
+ "interfaces-config": {
+ "interfaces": ["lo/10.53.0.1"]
+ },
+ "subnet4": [
+ {
+ :
+ "interface": "lo",
+ :
+ }
+ ],
+ :
+ }
+ @endcode
+
+-# The specification of the interface and address in the configuration file
+ is used by the main Kea code. Owing to the way that the fuzzing interface
+ between Kea and AFL is implemented, the address and interface also need to
+ be specified by the environment variables KEA_AFL_INTERFACE and
+ KEA_AFL_ADDRESS. With a configuration file containing statements listed
+ above, the relevant commands are:
+ @code
+ export KEA_AFL_INTERFACE="lo"
+ export KEA_AFL_ADDRESS="10.53.0.1"
+ @endcode
+ (If kea-dhcp6 is being fuzzed, then KEA_AFL_ADDRESS should specify an IPv6
+ address.)
+
+-# The fuzzer can now be run: a suitable command line is:
+ @code
+ afl-fuzz -m 4096 -i seeds -o fuzz-out -- ./kea-dhcp6 -c kea.conf -p 9001 -P 9002
+ @endcode
+ In the above:
+ - It is assumed that the directory holding the "afl-fuzz" program is in
+ the path, otherwise include the path name when invoking it.
+ - "-m 4096" allows Kea to take up to 4096 MB of memory. (Use "ulimit" to
+ check and optionally modify the amount of virtual memory that can be used.)
+ - The "-i" switch specifies a directory (in this example, one named "seeds")
+ holding "seed" files. These are binary files that AFL will use as its
+ source for generating new packets. They can generated from a real packet
+ stream with wireshark: right click on a packet, then export as binary
+ data. Ensure that only the payload of the UDP packet is exported.
+ - The "-o" switch specifies a directory (in this example called "fuzz-out")
+ that AFL will use to hold packets it has generated and packets that it has
+ found causes crashes or hangs.
+ - "--" Separates the AFL command line from that of Kea.
+ - "./kea-dhcp6" is the program being fuzzed. As mentioned above, this
+ should be an executable image, and it will be simpler to fuzz one
+ that has been installed.
+ - The "-c" switch sets the configuration file Kea should use while being
+ fuzzed.
+ - "-p 9001 -P 9002". The port on which Kea should listen and the port to
+ which it should send replies. If omitted, Kea will try to use the default
+ DHCP ports, which are in the privileged range. Unless run with "sudo",
+ Kea will fail to open the port and Kea will exit early on: no useful
+ information will be obtained from the fuzzer.
+
+-# Check that the fuzzer is working. If run from a terminal (with a black
+ background - AFL is particular about this), AFL will bring up a curses-style
+ interface showing the progress of the fuzzing. A good indication that
+ everything is working is to look at the "total paths" figure. Initially,
+ this should increase reasonably rapidly. If not, it is likely that Kea is
+ failing to start or initialize properly and the logging output (assuming
+ this has been configured) should be examined.
+
+@subsection fuzzRunConfig Fuzzing with Configuration Files
+
+AFL can be used to check the parsing of the configuration files. In this type
+of fuzzing, AFL generates configuration files which is passes to Kea to check.
+Steps for this fuzzing are:
+
+-# Build Kea as described above.
+
+-# Create a dictionary of keywords. Although AFL will mutate the files by
+ byte swaps, bit flips and the like, better results are obtained if it can
+ create new files based on keywords that could appear in the file. The
+ dictionary is described in the AFL documentation, but in brief, the file
+ contains successive lines of the form 'variable=keyword"', e.g.
+ @code
+ PD_POOLS="pd-pools"
+ PEERADDR="peeraddr"
+ PERSIST="persist"
+ PKT="pkt"
+ PKT4="pkt4"
+ @endcode
+ "variable" can be anything, as its name is ignored by AFL. However, all the
+ variable names in the file must be different. "keyword" is a valid keyword
+ that could appear in the configuration file. The convention adopted in the
+ example above seems to work well - variables have the same name as keywords,
+ but are in uppercase and have hyphens replaced by underscores.
+
+-# Run Kea with a command line of the form:
+ @code
+ afl-fuzz -m 4096 -i seeds -o fuzz-out -x dict.dat -- ./kea-dhcp4 -t @@
+ @endcode
+ In the above command line:
+ - Everything up to and including the "--" is the AFL command. The switches
+ are as described in the previous section apart from the "-x" switch: this
+ specifies the dictionary file ("dict.dat" in this example) described
+ above.
+ - The Kea command line uses the "-t" switch to specify the configuration
+ file to check. This is specified by two consecutive "@" signs: AFL
+ will replace these with the name of a file it has created when starting
+ Kea.
+
+@section Fuzzing Internals
+
+@subsection fuzzInternalNetwork Fuzzing with Network Packets
+
+The AFL fuzzer delivers packets to Kea's stdin. Although the part of Kea
+concerning the reception of packets could have been modified to accept input
+from stdin and have Kea pick them up in the normal way, a less-intrusive method
+was adopted.
+
+The packet loop in the main server code for kea-dhcp4 and kea-dhcp6 is
+essentially:
+@code{.unparsed}
+while (not shutting down) {
+ Read and process one packet
+}
+@endcode
+When --enable-fuzz is specified, this is conceptually modified to:
+@code{.unparsed}
+while (not shutting down) {
+ Read stdin and copy data to address/port on which Kea is listening
+ Read and process one packet
+}
+@endcode
+
+Implementation is via an object of class "Fuzz". When created, it identifies
+an interface, address and port on which Kea is listening and creates the
+appropriate address structures for these. The port is passed as an argument to
+the constructor because at the point at which the object is constructed, that
+information is readily available. The interface and address are picked up from
+the environment variables mentioned above. Consideration was given to
+extracting the interface and address information from the configuration file,
+but it was decided not to do this:
+
+-# The configuration file can contain the definition of multiple interfaces;
+ if this is the case, the one being used for fuzzing is unclear.
+-# The code is much simpler if the data is extracted from environment
+ variables.
+
+Every time through the loop, the object reads the data from stdin and writes it
+to the identified address/port. Control then returns to the main Kea code,
+which finds data available on the address/port on which it is listening and
+handles the data in the normal way.
+
+In practice, the "while" line is actually:
+@code{.unparsed}
+while (__AFL_LOOP(count)) {
+@endcode
+__AFL_LOOP is a token recognized and expanded by the AFL compiler (so no need
+to "#include" a file defining it) that implements the logic for the fuzzing.
+Each time through the loop (apart from the first), it raises a SIGSTOP signal
+telling AFL that the packet has been processed and instructing it to provide
+more data. The "count" value is the number of times through the loop before
+the loop terminates and the process is allowed to exit normally. When this
+happens, AFL will start the process anew. The purpose of periodically shutting
+down the process is to avoid issues raised by the fuzzing being confused with
+any issues associated with the process running for a long time (e.g. memory
+leaks).
+
+@subsection fuzzInternalConfig Fuzzing with Configuration Files
+
+No changes were required to Kea source code to fuzz configuration files. In
+fact, other than compiling with afl-clang++ and installing the resultant
+executable, no other steps are required. In particular, there is no need to
+use the "--enable-fuzz" switch in the configuration command line (although
+doing so will not cause any problems).
+
+@subsection fuzzThreads Changes Required for Multi-Threaded Kea
+
+The early versions of the fuzzing code used a separate thread to receive the
+packets from AFL and to write them to the socket on which Kea is listening.
+The lack of synchronization proved a problem, with Kea hanging in some
+instances. Although some experiments with thread synchronization were
+successful, in the end the far simpler single-threaded implementation described
+above was adopted for the single-threaded Kea 1.6. Should Kea be modified to
+become multi-threaded, the fuzzing code will need to be changed back to reading
+the AFL input in the background.
+
+@section fuzzNotes Notes
+
+@subsection fuzzNotesUnitTests Unit Test Failures
+
+If unit tests are built when --enable-fuzzing is specified, note that tests
+which check or use the DHCP servers (i.e. the unit tests in src/bin/dhcp4,
+src/bin/dhcp6 and src/bin/kea-admin) will fail. With no AFL-related
+environment variables defined, a C++ exception will be thrown with the
+description "no fuzzing interface has been set". However, if the
+KEA_AFL_INTERFACE and KEA_AFL_ADDRESS variables are set to valid values, the
+tests will hang.
+
+Both these results are expected and should cause no concern. The exception is
+thrown by the fuzzing object constructor when it attempts to create the address
+structures for routing packets between AFL and Kea but discovers it does not
+have the necessary information. The hang is due to the fact that the AFL
+processing loop does a synchronous read from stdin, something not expected by
+the test. (Should random input be supplied on stdin, e.g. from the keyboard,
+the test will most likely fail as the input is unlikely to be that expected by
+the test.)
+
+
+*/
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
new file mode 100644
index 0000000..5151e15
--- /dev/null
+++ b/doc/devel/mainpage.dox
@@ -0,0 +1,160 @@
+// Copyright (C) 2012-2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+ * @mainpage Kea Developer's Guide
+ *
+ * Welcome to the Kea Developer's Guide. This documentation is addressed at
+ * either existing or prospective Kea developers and contributors, and
+ * provides information needed to extend and maintain Kea source code.
+
+ * If you wish to write hook code - the code that is loaded by Kea at
+ * run-time and modifies its behavior, you should read the section
+ * @ref hooksdgDevelopersGuide.
+ *
+ * Kea maintenance information is divided into a number of sections.
+ * Information on DHCP-specific topics can be found
+ * in the @ref dhcpMaintenanceGuide. General topics are discussed in
+ * @ref miscellaneousTopics.
+ *
+ * If you are a user or system administrator, rather than software engineer,
+ * you should read the
+ * <a href="https://kea.readthedocs.io">Kea
+ * Administrator Reference Manual</a> instead. If you are using a beta or
+ * development version of Kea, the
+ * <a href="https://kea.readthedocs.io/">
+ * development version of the manual</a> is recommended.
+ *
+ * Regardless of your field of expertise, you are encouraged to visit the
+ * <a href="https://gitlab.isc.org/isc-projects/kea/wikis/home">Kea wikipage (https://gitlab.isc.org/isc-projects/kea/wikis/home)</a>
+ *
+ * @section contrib Contributor's Guide
+ * - @subpage contributorGuide - This page describes the process of sending
+ * a patch to ISC and what happens next. Please read it if you are considering
+ * sending us any code.
+ *
+ * @section qa Quality Assurance
+ * - @subpage qaIntro
+ * - @subpage unitTests
+ * - @subpage unitTestsEnvironmentVariables
+ * - @subpage unitTestsSanitizers
+ * - @subpage unitTestsDatabaseConfig
+ * - @subpage unitTestsSysrepo
+ * - @subpage writingShellScriptsAndTests
+ * - @subpage performance
+ * - @subpage fuzzer
+ * - @subpage qa
+ * - @subpage ciGithub
+ * - @subpage ciGitlab
+ * - @subpage ciJenkins
+ *
+ * @section hooksFramework Hooks Framework
+ * - @subpage hooksdgDevelopersGuide
+ * - @subpage dhcpv4Hooks
+ * - @subpage dhcpv6Hooks
+ * - @subpage agentHooks
+ * - @subpage d2Hooks
+ * - @subpage hooksComponentDeveloperGuide
+ * - @subpage hooksmgMaintenanceGuide
+ * - @subpage libdhcp_ha
+ * - @subpage libdhcp_user_chk
+ * - @subpage libdhcp_lease_cmds
+ * - @subpage libdhcp_stat_cmds
+ *
+ * @section dhcpMaintenanceGuide DHCP Maintenance Guide
+ * - @subpage dhcp4
+ * - @subpage dhcpv4ConfigParser
+ * - @subpage dhcpv4ConfigInherit
+ * - @subpage dhcpv4OptionsParse
+ * - @subpage dhcpv4DDNSIntegration
+ * - @subpage dhcpv4Classifier
+ * - @subpage dhcpv4ConfigBackend
+ * - @subpage dhcpv4SignalBasedReconfiguration
+ * - @subpage dhcpv4Other
+ * - @subpage dhcpv4o6Dhcp4
+ * - @subpage dhcp6
+ * - @subpage dhcpv6ConfigParser
+ * - @subpage dhcpv6ConfigInherit
+ * - @subpage dhcpv6DDNSIntegration
+ * - @subpage dhcpv6OptionsParse
+ * - @subpage dhcpv6Classifier
+ * - @subpage dhcpv6ConfigBackend
+ * - @subpage dhcpv6SignalBasedReconfiguration
+ * - @subpage dhcpv6Other
+ * - @subpage dhcpv4o6Dhcp6
+ * - @subpage congestionHandling
+ * - @subpage d2
+ * - @subpage d2ProcessDerivation
+ * - @subpage d2ConfigMgt
+ * - @subpage d2NCRReceipt
+ * - @subpage d2DDNSUpdateExecution
+ * - @subpage d2EventLoop
+ * - @subpage d2TransDetail
+ * - @subpage d2StateModel
+ * - @subpage d2TransExecExample
+ * - @subpage controlAgent
+ * - @subpage ctrlAgentHttp
+ * - @subpage ctrlAgentCreatingResponse
+ * - @subpage ctrlAgentCommandMgr
+ * - @subpage CtrlAgentSecurity
+ * - @subpage lfc
+ * - @subpage lfcProcessing
+ * - @subpage lfcFiles
+ * - @subpage ctrlSocket
+ * - @subpage ctrlSocketOverview
+ * - @subpage ctrlSocketClient
+ * - @subpage ctrlSocketImpl
+ * - @subpage ctrlSocketConnections
+ * - @subpage dhcpDatabaseBackends
+ * - @subpage configBackend
+ * - @subpage configBackendJSONDesign
+ *
+ * @section libraries Kea libraries
+ * - @subpage libutil
+ * - @subpage libasiolink
+ * - @subpage libcc
+ * - @subpage libdatabase
+ * - @subpage libdhcp
+ * - @subpage libdhcpIntro
+ * - @subpage libdhcpRelay
+ * - @subpage libdhcpIfaceMgr
+ * - @subpage libdhcpPktFilter
+ * - @subpage libdhcpPktFilter6
+ * - @subpage libdhcpErrorLogging
+ * - @subpage libstats
+ * - @subpage libdhcp_ddns
+ * - @subpage libdhcpsrv
+ * - @subpage leasemgr
+ * - @subpage cfgmgr
+ * - @subpage hostmgr
+ * - @subpage optionsConfig
+ * - @subpage allocengine
+ * - @subpage timerManager
+ * - @subpage leaseReclamationRoutine
+ * - @subpage subnetSelect
+ * - @subpage dhcp4o6Ipc
+ * - @subpage libeval
+ * - @subpage libprocess
+ * - @subpage cpl
+ * - @subpage cplSignals
+ * - @subpage libyang
+ * - @subpage libhttp
+ *
+ * @section miscellaneousTopics Miscellaneous Topics
+ * - @subpage terminology
+ * - @subpage parser
+ * - @subpage logKeaLogging
+ * - @subpage logBasicIdeas
+ * - @subpage logDeveloperUse
+ * - @subpage logNotes
+ * - @subpage LoggingApi
+ * - @subpage SocketSessionUtility
+ * - @subpage crossCompile
+ * - @subpage debug
+ * - @subpage docs
+ * - <a href="./doxygen-error.log"><b>Documentation Warnings and Errors</b></a>
+ *
+ */
diff --git a/doc/devel/performance.dox b/doc/devel/performance.dox
new file mode 100644
index 0000000..4d4420f
--- /dev/null
+++ b/doc/devel/performance.dox
@@ -0,0 +1,15 @@
+// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+
+ @page performance Performance Testing
+
+ This section is for topics pertaining to Kea runtime performance.
+
+ @subpage perfdhcpInternals perfdhcp Internals
+
+*/
diff --git a/doc/devel/qa.dox b/doc/devel/qa.dox
new file mode 100644
index 0000000..d82c4e7
--- /dev/null
+++ b/doc/devel/qa.dox
@@ -0,0 +1,52 @@
+// Copyright (C) 2023 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+@page qaIntro Quality Assurance in Kea
+
+This is only a brief excerpt about some QA systems used at ISC. For more
+information, please refer to the DHCP QA department.
+
+@section ciGithub Running CI pipeline on Github
+
+While our primary environment for running CI pipeline is Jenkins hosted on AWS,
+there are some tools that are only available on github. One of such tools is
+CodeQL. CodeQL is a static analysis tool that can be used to find security
+vulnerabilities in the code. It is a part of Github Advanced Security suite.
+Github Advanced Security suite is available for free for open source projects.
+
+The job is defined in `.github/workflows/codeql.yml` file. It is configured
+to run once per week on `master` and `ci` branches. Sadly, it requires Kea
+compilation. With the runners provided on github having only 2 CPUs, it's
+a slow process. But we don't care that much - we get the results once per
+week. The results are available in the `Security` tab of the repository
+(see https://github.com/isc-projects/kea/security). This tab is only visible to
+logged in members of the isc-projects organization.
+
+@section ciGitlab Running CI pipeline on Gitlab
+
+There are several jobs configure on gitlab CI:
+
+- shellcheck
+- danger
+- dhcpdb_create-upgrade-consistency
+- duplicate-includes
+- missing-api-commands
+- missing-config-h-include
+- missing-git-attribute
+- sast-analyzer
+- flawfinder-sast
+
+The pipeline can be inspected and configure here:
+https://gitlab.isc.org/isc-projects/kea/-/ci/editor
+
+@section ciJenkins Running CI pipeline on Jenkins
+
+Jenkins is the primary CI environment. It is hosted on AWS. For details,
+see internal QA-DHCP wiki, ask on QA channel on mattermost or take a look at the
+internal `qa-dhcp` repository.
+
+*/
diff --git a/doc/devel/terminology.dox b/doc/devel/terminology.dox
new file mode 100644
index 0000000..11750ff
--- /dev/null
+++ b/doc/devel/terminology.dox
@@ -0,0 +1,28 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+@page terminology Terminology
+
+This page explains some common abbreviations and terms:
+
+- CA - Control Agent. That's a separate module that talks with Kea DHCPv4, DHCPv6 (and soon also D2)
+ over control channel and exposes their internal commands using RESTful interface.
+
+- D2 - This is a nickname of DHCP-Dynamic DNS server module. Since using the full name is awkward,
+ we often use shortened version of it: D2.
+
+- DHCP - Dynamic Host Configuration Protocol. There are two similar, but operationally different
+ protocols: DHCPv4 and DHCPv6. When v4 or v6 is not specified, the DHCP expression applies to both.
+
+- DHCPv4 - Dynamic Host Configuration Protocol for IPv4, a protocol that defines how IPv4 hosts can
+ obtain IPv4 addresses and other configuration from the servers. Defined in RFC2131.
+
+- DHCPv6 - Dynamic Host Configuration Protocol for IPv6, a protocol that defines how IPv6 hosts and
+ router can obtain IPv6 addresses, IPv6 prefixes and other configuration from the servers. Defined
+ in RFC3315.
+
+*/
diff --git a/doc/devel/unit-tests.dox b/doc/devel/unit-tests.dox
new file mode 100644
index 0000000..42d0643
--- /dev/null
+++ b/doc/devel/unit-tests.dox
@@ -0,0 +1,613 @@
+// Copyright (C) 2015-2023 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+
+ @page unitTests Building Kea with Unit Tests
+
+By default, Kea is built without unit-tests as they're used mostly by
+developers and prospective contributors. Kea's unit-tests are using
+<a href="https://github.com/google/googletest">gtest framework</a> from
+Google. Google's approach has changed over the years. For some time,
+they were very keen on not installing gtest as a normal software would
+be, but rather provide gtest as sources. This was further complicated
+with the fact that some Linux distributions packaged gtest and tried
+to mimic its installation. Kea tries its best to accommodate all typical
+situations and provides two switches to point to gtest. You can use
+`--with-gtest` or `--with-gtest-source`. Both attempt to locate gtest
+on their own. However, if neither of them can find it, you can specify
+the path explicitly. For example, on ubuntu with googletest package installed,
+you can do the following for Kea to find it:
+
+@code
+sudo apt install googletest
+./configure --with-gtest-source=/usr/src/googletest
+@endcode
+
+Depending on how you compiled or installed \c gtest (e.g. from sources
+or using some package management system) one of those two switches will
+find \c gtest. After that you make and run the unit-tests with:
+
+@code
+make
+make check
+@endcode
+
+As usual, using \c -jX option will speed up compilation. This parameter is
+even more useful for unit-tests as there are over 6000 unit-tests and their
+compilation is significantly slower than just the production Kea sources.
+
+Kea should work with reasonably recent gtest versions. We recently tried
+with 1.7.0, 1.8.0, 1.8.1 and 1.10.0.
+
+@section unitTestsEnvironmentVariables Environment Variables
+
+The following environment variable can affect the unit tests:
+
+- KEA_LOCKFILE_DIR - Specifies a directory where the logging system should
+ create its lock file. If not specified, it is <i>prefix</i>/var/run/kea,
+ where <i>prefix</i> defaults to /usr/local. This variable must not end
+ with a slash. There is one special value, "none", which instructs Kea to
+ not create a lock file at all. This may cause issues if several processes
+ log to the same file. (Also see the Kea User's Guide, section 15.3.)
+
+- KEA_LOGGER_DESTINATION - Specifies the logging destination. If not set, logged
+ messages will not be recorded anywhere. There are three special values:
+ stdout, stderr and syslog. Any other value is interpreted as a filename.
+ (Also see Kea User's Guide, section 15.3.)
+
+- KEA_LOG_CHECK_VERBOSE - Specifies the log check default verbosity. If not
+ set, unit tests using the log utils to verify that logs are generated as
+ expected are by default silent. If set, these unit tests display real
+ and expected logs.
+
+- KEA_MYSQL_HAVE_SSL - Specifies the SSL/TLS support status of MySQL.
+ When not set the corresponding MySQL global variable is read and
+ the environment of the unit test process is updated so usually this
+ variable is manually set only in order to enforce a particular status.
+
+- KEA_PIDFILE_DIR - Specifies the directory which should be used for PID files
+ as used by dhcp::Daemon or its derivatives. If not specified, the
+ default is <i>prefix</i>/var/run/kea, where <i>prefix</i> defaults to
+ /usr/local. This variable must not end with a slash.
+
+- KEA_SOCKET_TEST_DIR - If set, it specifies the directory where Unix
+ sockets are created. There is an operating system limitation on how
+ long a Unix socket path can be, typically slightly over 100
+ characters. By default unit-tests create sockets in temporary folder
+ under /tmp folder. KEA_SOCKET_TEST_DIR can be specified to instruct
+ unit-tests to use a different directory. It must not end with slash.
+
+- KEA_TEST_DB_WIPE_DATA_ONLY - Unit tests which use a Kea unit test
+ database take steps to ensure they are starting with an empty database
+ of the correct schema version. The first step taken is to simply
+ delete the transient data (such as leases, reservations, etc..), provided
+ the schema exists and is the expected version. If the schema does not
+ exist, is not the expected version, or for some reason the data wipe fails,
+ the schema will be dropped and recreated. Setting this value to "false"
+ will cause the test setup logic to always drop and create the database
+ schema. The default value is "true".
+
+- KEA_TLS_CHECK_VERBOSE - Specifies the TLS check default verbosity. If not
+ set, TLS unit tests triggering expected failures are by default silent.
+ If set, these TLS unit tests display the error messages which are very
+ dependent on the cryptographic backend and boost library versions.
+
+@note Setting KEA_TEST_DB_WIPE_DATA_ONLY to false may dramatically
+increase the time it takes each unit test to execute.
+
+- GTEST_OUTPUT - Save the test results in XML files. Make it point to a location
+where a file or directory can be safely created. If there is no file or
+directory at that location, adding a trailing slash
+`GTEST_OUTPUT=${PWD}/test-results/` will create a directory containing an XML
+file for each directory being tested. Leaving the slash out will create a single
+XML file and will put all the test results in it.
+
+- DEBUG - Set this variable to make shell tests output the commands that are
+run. They are shown just before they are effectively run. Can be set to
+anything e.g. `DEBUG=true`. `unset DEBUG` to remove this behavior.
+
+@section unitTestsSanitizers Use Sanitizers
+
+ GCC and LLVM support some sanitizers which perform additional tests
+ at runtime, for instance the ThreadSanitizer (aka TSan) detects data
+ race in executed C++ code (unfortunately on macOS it intercepts
+ signals and fails to send them to waiting select system calls so
+ some tests always fail when it is used, experiments are run with
+ different versions of Tsan).
+
+ The simplest way to enable a sanitizer is to add it to the CXXFLAGS
+ environment variable in .configure by e.g. <i>-fsanitize=thread</i>.
+
+ When enabling lcov (code coverage), some gtest functions are detected as
+ not being thread safe. It is recommended to disable lcov when enabling
+ thread sanitizer.
+
+@section unitTestsDatabaseConfig Databases Configuration for Unit Tests
+
+ With the use of databases requiring separate authorisation, there are
+ certain database-specific pre-requisites for successfully running the unit
+ tests. These are listed in the following sections.
+
+ @subsection unitTestsDatabaseUsers Database Users Required for Unit Tests
+
+ Unit tests validating database backends require that the <i>keatest</i>
+ database is created. This database should be empty. The unit tests
+ also require that the <i>keatest</i> user is created and that this user
+ is configured to access the database with a password of <i>keatest</i>.
+ Unit tests use these credentials to create database schema, run test cases
+ and drop the schema. Thus, the <i>keatest</i> user must have sufficiently
+ high privileges to create and drop tables, as well as insert and modify the
+ data within those tables.
+
+ The database backends which support read only access to the host
+ reservations databases (currently MySQL and PostgreSQL) include unit
+ tests verifying that a database user with read-only privileges can be
+ used to retrieve host reservations. Those tests require another user,
+ <i>keatest_readonly</i>, with SQL SELECT privilege to the <i>keatest</i>
+ database (i.e. without INSERT, UPDATE etc.), is also created.
+ <i>keatest_readonly</i> should also have the password <i>keatest</i>.
+
+ The following sections provide step-by-step guidelines how to setup the
+ databases for running unit tests.
+
+ @subsection mysqlUnitTestsPrerequisites MySQL Database
+
+ The steps to create the database and users are:
+
+ -# Log into MySQL as root:
+ @verbatim
+ % mysql -u root -p
+ Enter password:
+ :
+ mysql>@endverbatim\n
+ -# Create the test database. This must be called "keatest":
+ @verbatim
+ mysql> CREATE DATABASE keatest;
+ mysql>@endverbatim\n
+ -# Create the users under which the test client will connect to the database
+ (the apostrophes around the words <i>keatest</i>, <i>keatest_readonly</i>, and
+ <i>localhost</i> are required):
+ @verbatim
+ mysql> CREATE USER 'keatest'@'localhost' IDENTIFIED BY 'keatest';
+ mysql> CREATE USER 'keatest_readonly'@'localhost' IDENTIFIED BY 'keatest';
+ mysql> CREATE USER 'keatest_secure'@'localhost' IDENTIFIED BY 'keatest';
+ mysql> ALTER USER 'keatest_secure'@'localhost' REQUIRE X509;
+ mysql>@endverbatim\n
+ Some old versions of MySQL do not support the REQUIRE keyword in ALTER
+ USER. Fortunately all versions support it in GRANT even if it is less secure
+ as the requirement will apply only to commands for the database instead
+ to all connections so all commands. And of course in production many
+ stronger requirements are available: X509 only requires the user to
+ present a certificate: you can specify which certificate by requiring
+ for instance a particular Subject Name, etc.
+ -# Grant the created users permissions to access the <i>keatest</i> database
+ (again, the apostrophes around the user names and <i>localhost</i>
+ are required):
+ @verbatim
+ mysql> GRANT ALL ON keatest.* TO 'keatest'@'localhost';
+ mysql> GRANT SELECT ON keatest.* TO 'keatest_readonly'@'localhost';
+ mysql> GRANT ALL ON keatest.* TO 'keatest_secure'@'localhost';
+ mysql>@endverbatim\n
+ When the REQUIRE in ALTER USER is not supported change the last line by:
+ @verbatim
+ mysql> GRANT ALL ON keatest.* TO 'keatest_secure'@'localhost' REQUIRE X509;
+ mysql>@endverbatim\n
+ -# If you get <i>You do not have the SUPER privilege and binary logging is
+ enabled</i> error message, you need to add:
+ @verbatim
+ mysql> SET GLOBAL LOG_BIN_TRUST_FUNCTION_CREATORS = 1;
+ mysql>@endverbatim\n
+ -# Exit MySQL:
+ @verbatim
+ mysql> quit
+ Bye
+ %@endverbatim
+
+ The unit tests are run automatically when "make check" is executed (providing
+ that Kea has been built with the \c --with-mysql switch (see the installation
+ section in the <a href="https://kea.readthedocs.io/">Kea Administrator
+ Reference Manual</a>).
+
+ @subsection mysqlUnitTestsTLS MySQL Database with SSL/TLS
+
+ Usually MySQL is compiled with SSL/TLS support using OpenSSL.
+ This is easy to verify using the:
+
+@verbatim
+mysql> SHOW GLOBAL VARIABLES LIKE 'have_ssl';
+@endverbatim
+
+ The variable is documented to have three possible values:
+
+- DISABLED: compiled with TLS support but it was not configured
+
+- YES: compiled with configured TLS support
+
+- NO: not compiled with TLS support
+
+The value of this MySQL global variable is reflected by the
+KEA_MYSQL_HAVE_SSL environment variable.
+
+The keatest_secure user requires X509 so a client certificate. Of course
+in production a stricter requirement should be used, in particular when
+a client certificate should be bound to a particular user.
+
+MySQL unit tests reuse the asiolink library setup. This .my.cnf
+configuration file works with MariaDB 10.6.4:
+
+@verbatim
+[mysqld]
+ssl_cert=<kea-sources>/src/lib/asiolink/testutils/ca/kea-server.crt
+ssl_key=<kea-sources>/src/lib/asiolink/testutils/ca/kea-server.key
+ssl_ca=<kea-sources>/src/lib/asiolink/testutils/ca/kea-ca.crt
+
+[client-mariadb]
+ssl_cert=<kea-sources>/src/lib/asiolink/testutils/ca/kea-client.crt
+ssl_key=<kea-sources>/src/lib/asiolink/testutils/ca/kea-client.key
+ssl_ca=<kea-sources>/src/lib/asiolink/testutils/ca/kea-ca.crt
+ssl-verify-server-cert
+@endverbatim
+
+The last statement requires mutual authentication named two way in the
+MariaDB documentation. For MySQL versions greater than 5.7.11 this
+statement should be replaced by:
+
+@verbatim
+[client]
+...
+ssl-mode=VERIFY_IDENTITY
+@endverbatim
+
+On Debian some MySQL packages use GnuTLS instead OpenSSL to provide
+the SSL/TLS support: this is known to not work with this proposed setup.
+
+ @subsection pgsqlUnitTestsPrerequisites PostgreSQL Database
+
+ PostgreSQL set up differs from system to system. Please consult your
+ operating system-specific PostgreSQL documentation. The remainder of
+ that section uses Ubuntu 13.10 x64 (with PostgreSQL 9.0+) as an example.
+
+ On Ubuntu, PostgreSQL is installed (with <tt>sudo apt-get install
+ postgresql</tt>) under user <i>postgres</i>. To create new databases
+ or add new users, initial commands must be issued under this username:
+
+@verbatim
+$ sudo -u postgres psql postgres
+[sudo] password for thomson:
+psql (9.1.12)
+Type "help" for help.
+postgres=# CREATE USER keatest WITH PASSWORD 'keatest';
+CREATE ROLE
+postgres=# CREATE DATABASE keatest;
+CREATE DATABASE
+postgres=# GRANT ALL PRIVILEGES ON DATABASE keatest TO keatest;
+GRANT
+postgres=# \q
+@endverbatim
+
+ PostgreSQL versions earlier than 9.0 don't provide an SQL statement for granting
+ privileges on all tables in a database. In newer PostgreSQL versions, it is
+ possible to grant specific privileges on all tables within a schema.
+ However, this only affects tables which exist when the privileges are granted.
+ To ensure that the user has specific privileges to tables dynamically created
+ by the unit tests, the default schema privileges must be altered.
+
+ The following example demonstrates how to create the user <i>keatest_readonly</i>,
+ which has SELECT privilege to the tables within the <i>keatest</i> database,
+ in Postgres 9.0+. For earlier versions of Postgres, it is recommended to
+ simply grant full privileges to <i>keatest_readonly</i>, using the
+ same steps as for the <i>keatest</i> user.
+
+@verbatim
+$ psql -U postgres
+Password for user postgres:
+psql (9.1.12)
+Type "help" for help.
+
+postgres=# CREATE USER keatest_readonly WITH PASSWORD 'keatest';
+CREATE ROLE
+postgres=# \q
+
+$ psql -U keatest
+Password for user keatest:
+psql (9.1.12)
+Type "help" for help.
+
+keatest=> ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES to keatest_readonly;
+ALTER DEFAULT PRIVILEGES
+keatest=> \q
+@endverbatim
+
+ Note that the <i>keatest</i> user (rather than <i>postgres</i>) is used to grant
+ privileges to the <i>keatest_readonly</i> user. This ensures that the SELECT
+ privilege is granted only on the tables that the <i>keatest</i> user can access
+ within the public schema.
+
+ It seems this no longer works on recent versions of PostgreSQL: if you get
+ a permission problem on SELECT on the schema_version table for
+ eatest_readonly, please try with the schema loaded:
+
+@verbatim
+$ psql -h localhost -U keatest -d keatest
+Password for user keatest:
+psql (11.3 (Debian 11.3-1))
+SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
+Type "help" for help.
+
+keatest=> GRANT SELECT ON ALL TABLES IN SCHEMA public TO keatest_readonly;
+GRANT
+keatest=> \q
+@endverbatim
+
+ Now we should be able to log into the newly created database using both user
+ names:
+@verbatim
+$ psql -d keatest -U keatest
+Password for user keatest:
+psql (9.1.12)
+Type "help" for help.
+
+keatest=> \q
+
+$ psql -d keatest -U keatest_readonly
+Password for user keatest_readonly:
+psql (9.1.12)
+Type "help" for help.
+
+keatest=>
+@endverbatim
+
+ If instead of seeing keatest=> prompt, your login is refused with an error
+ code about failed peer or
+ <tt>Ident authentication failed for user "keatest"</tt>, it means that
+ PostgreSQL is configured to check unix username and reject login attempts if
+ PostgreSQL names are different. To alter that, the PostgreSQL pg_hba.conf
+ configuration file must be changed. It usually resides at
+ <tt>/var/lib/postgresql/data/pg_hba.conf</tt> or at
+ <tt>/etc/postgresql/${version}/main/pg_hba.conf</tt>, but you can find out
+ for sure by running
+ <tt>sudo -u postgres psql -t -c 'SHOW hba_file'</tt>. Make sure
+ that all the authentication methods are changed to "md5" like this:
+
+@verbatim
+local all all md5
+host all all 127.0.0.1/32 md5
+host all all ::1/128 md5
+@endverbatim
+
+ Another possible problem is that you get no password prompt. This is
+ most probably because you have no <tt>pg_hba.conf</tt> config file
+ and everybody is by default trusted. As it has a very bad effect
+ on the security you should have been warned this is a highly unsafe
+ configuration. The solution is the same, i.e., require password or
+ md5 authentication method.
+
+ If you lose the postgres user access you can first add:
+@verbatim
+local all postgres trust
+@endverbatim
+ to trust only the local postgres user. Note the postgres user can
+ be pgsql on some systems.
+
+ Please consult your PostgreSQL user manual before applying those changes as
+ those changes may expose your other databases that you run on the same system.
+ In general case, it is a poor idea to run anything of value on a system
+ that runs tests. Use caution!
+
+ The unit tests are run automatically when "make check" is executed (providing
+ that Kea has been build with the \c --with-pgsql switch (see the installation
+ section in the <a href="https://kea.readthedocs.io">Kea Administrator
+ Reference Manual</a>).
+
+@section unitTestsKerberos Kerberos Configuration for Unit Tests
+
+The GSS-TSIG hook library uses the GSS-API with Kerberos. While there are
+no doubts that the hook can be safely used with a valid Kerberos configuration
+in production, unit tests reported problems on some systems.
+
+GSS-TSIG hook unit tests use a setup inherited from bind9 with old crypto
+settings which are not allowed by default Kerberos system configuration.
+A simple workaround is to set the KRB5_CONFIG environment variable to
+a random value that doesn't match a file (e.g. KRB5_CONFIG=).
+
+@section writingShellScriptsAndTests Writing shell scripts and tests
+
+Shell tests are `shellcheck`ed. But there are other writing practices that are
+good to follow in order to keep, not only shell tests, but shell scripts in
+general, POSIX-compliant. See below:
+
+- For portability, all shell scripts should have a shebang.
+@code
+#!/bin/sh
+@endcode
+The `sh` shell can differ on various operating systems. On most systems it is
+GNU sh. Notable exceptions are Alpine which links it to ash, FreeBSD which has
+the primordial non-GNU sh, Ubuntu which links it to dash. These four shells
+should all be tested against, when adding shell scripts or making changes to
+them.
+
+- Reference variables with curly brackets.
+@code
+${var} # better
+$var
+@endcode
+For consistency with cases where you need advanced features from the variables
+which make the curly brackets mandatory. Such cases are:
+@code
+# Retrieving variable/string length...
+${#var}
+
+# Defaulting to a given value when the variable is undefined...
+${var-default}
+
+# Substituting the variable with a given value when the variable is defined...
+${var+value}
+
+# Concatenating the value of a variable with an alphanumeric constant...
+${var}constant
+@endcode
+
+- Always use `printf` instead of `echo`. There are times when a newline is not
+desired such as when you want to print on a single line from multiple points
+in your script or when you want to get the character count from an expression:
+@code
+var1='not '
+var2=' you want to ignore'
+
+# Prints the number of characters.
+printf '%s' "${var1}something${var2}" | wc -c
+# Result:
+ 32
+
+# This one prints a plus one i.e. the inherent newline.
+echo "${var1}something${var2}" | wc -c
+# Result:
+ 33
+
+# `echo` does have `-n` to suppress newline, but...
+# SC2039: In POSIX sh, echo flags are undefined.
+echo -n "${var1}something${var2}" | wc -c
+# Result:
+ 32 # sometimes, other times an error
+@endcode
+`printf` also has the benefit of separating the format from the actual variables
+which has many use cases. One such use case is coloring output with ANSI escape
+sequence codes, see the `test_finish` function in
+`src/lib/testutils/dhcp_test_lib.sh.in`, which is not possible with POSIX echo.
+
+- `set -e` should be enabled at all times to immediately fail when a command
+returns a non-zero exit code. There are times when you expect a non-zero exit
+code in your tests. This is what the `run_command` function in
+`src/lib/testutils/dhcp_test_lib.sh.in` is for. It momentarily disables the `-e`
+flag to capture the output and exit code and enables it again afterwards. The
+variables used are `${EXIT_CODE}` and `${OUTPUT}`. /dev/stderr is not captured.
+`run_command` also doesn't work with pipes and redirections. When these
+mechanisms are needed, you can always wrap your complex expression in a function
+and then call `run_command wrapping_function`. Alternatively, if you only care
+about checking for zero exit code, you can use `if` conditions.
+@code
+# The non-zero exit code does not stop script execution, but we can still adjust
+# behavior based on it.
+if maybe-failing-command; then
+ f
+else
+ g
+fi
+@endcode
+There are times when your piped or redirected command that is expected to return
+non-zero is so small or has so few instantiations that it doesn't deserve a
+separate function. Such an example could be grepping for something in a
+variable. `grep` returns a non-zero exit code if it doesn't find anything. In
+that case, you can add `|| true` at the end to signal the fact that you allow
+finding nothing like so:
+@code
+printf '%s' "${var}" | grep -F 'search-criterion' || true
+@endcode
+
+- `set -u` should be enabled at all times to immediately signal an undefined
+variable. If you're a stickler for the legacy behavior of defaulting to an empty
+space then you can reference all your variables with:
+@code
+# Default variable is an empty space.
+${var-}
+
+# Or like this if you prefer to quote the empty space.
+${var-''}
+@endcode
+
+- SC2086: Double quote to prevent globbing and word splitting.
+Even though covered by shellcheck, it's worth mentioning because shellcheck
+doesn't always warn you because of what might be a systematic deduction of when
+quoting is not needed. Globbing is a pattern matching mechanism. It's used a lot
+with the `*` wildcard character e.g. `ls *.txt`. Sometimes, you want to glob
+intentionally. In that case, you can omit quoting, but it is preferable to take
+the wildcard characters outside the variable so that you are able to quote to
+prevent other globbing and word splitting e.g.:
+@code
+# Globbing done right
+ls "${var}"*.txt
+
+# Word splitting problem
+path='/home/my user'
+ls ${path}
+
+# Result:
+ ls: cannot access '/home/my': No such file or directory
+ ls: cannot access 'user': No such file or directory
+
+# Word splitting avoided
+path='/home/my user'
+ls "${path}"
+
+# Result:
+ Desktop
+ Documents
+ Downloads
+@endcode
+If you have an expression composed of multiple variables don't just quote the
+variables. It's correct, but not readable. Quote the entire expression.
+@code
+# no
+"${var1}"some-fixed-contiguous-value"${var2}"
+
+# yes
+"${var1}some-fixed-contiguous-value${var2}"
+@endcode
+
+- Single quote expressions when no variables are inside. This is to avoid the
+need to escape special shell characters like `$`.
+
+- All shell tests are created from `.in` autoconf template files. They
+initially contain template variables like `@prefix@` which are then substituted
+with the configured values. All of these should be double quoted, not
+single-quoted since they themselves can contain shell variables that need to be
+expanded.
+
+- Use `$(...)` notation instead of legacy backticks. One important advantage is
+that the `$(...)` notation allows for nested executions.
+@code
+# SC2006 Use `$(...)` notation instead of legacy backticked `...`.
+hostname=`cat /etc/hostname`
+
+# Better
+hostname=$(cat /etc/hostname)
+
+# Nested executions
+is_ssh_open=$(nc -vz $(cat /etc/hostname).lab.isc.org 22)
+
+# Results in confusing "command not found" messages.
+is_ssh_open=`nc -vz `cat /etc/hostname`.lab.isc.org 22`
+@endcode
+
+- When using `test` and `[`, `==` is just a convenience alias for `=`. Use `=`
+because it's more widely supported. If using, `[[`, then indeed `==` has extra
+features like glob matching. But don't use `[[`, it's not part of the POSIX
+standard.
+
+- Capturing parameters in functions or scripts simply cannot be done without
+breaking POSIX compliance. In POSIX, pass the quoted parameters `"${@}"` as
+positional parameters to all the function and scripts invocations. if it gets
+too unmanageable or you need custom positional arguments then break your script
+into multiple scripts or handle all possible parameters and don't accept any
+ad-hoc parameters.
+@code
+# Neither of these preserve original quoting.
+parameters="${*}"
+parameters="${@}"
+
+# In advanced shells this could be done with lists.
+parameters=( "${@}" )
+do-something --some --other --optional --parameters "${parameters[@]}"
+
+# Proper POSIX way
+do-something --some --other --optional --parameters "${@}"
+@endcode
+
+- Never use `eval`. It doesn't preserve original quoting. Have faith that there
+are always good alternatives.
+
+ */
diff --git a/doc/examples/agent/comments.json b/doc/examples/agent/comments.json
new file mode 100644
index 0000000..10c82e3
--- /dev/null
+++ b/doc/examples/agent/comments.json
@@ -0,0 +1,58 @@
+// This is a example of a configuration for Control-Agent (CA) or simply Agent.
+// It uses embedded (i.e., which will be included in configuration objects
+// and not stripped by at lexical analysis) comments.
+
+{
+ "Control-agent":
+ {
+ // Global scope
+ "comment": "A Control Agent",
+
+ "http-host": "127.0.0.1",
+
+ // If enabling HA and multi-threading, the 8000 port is used by the HA
+ // hook library http listener. When using HA hook library with
+ // multi-threading to function, make sure the port used by dedicated
+ // listener is different (e.g. 8001) than the one used by CA. Note
+ // the commands should still be sent via CA. The dedicated listener
+ // is specifically for HA updates only.
+ "http-port": 8000,
+
+ // In authentication
+ "authentication":
+ {
+ "comment": "basic HTTP authentication",
+
+ "type": "basic",
+
+ // In basic HTTP authentication clients
+ "clients":
+ [
+ {
+ "comment": "admin is authorized",
+ "user": "admin",
+ "password": "1234"
+ }
+ ]
+ },
+
+ // In control socket
+ "control-sockets":
+ {
+ "dhcp4":
+ {
+ "comment": "control socket for DHCPv4 server",
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ }
+ },
+
+ // In loggers
+ "loggers": [
+ {
+ "comment": "A logger",
+ "name": "kea-ctrl-agent"
+ }
+ ]
+ }
+}
diff --git a/doc/examples/agent/https.json b/doc/examples/agent/https.json
new file mode 100644
index 0000000..c06039c
--- /dev/null
+++ b/doc/examples/agent/https.json
@@ -0,0 +1,32 @@
+// This is an example of a configuration for Control-Agent (CA) HTTPS i.e.
+// HTTP over TLS.
+{
+ "Control-agent":
+ {
+ // We need to specify where the agent should listen to incoming HTTP
+ // queries.
+ "http-host": "127.0.0.1",
+
+ // If enabling HA and multi-threading, the 8000 port is used by the HA
+ // hook library http listener. When using HA hook library with
+ // multi-threading to function, make sure the port used by dedicated
+ // listener is different (e.g. 8001) than the one used by CA. Note
+ // the commands should still be sent via CA. The dedicated listener
+ // is specifically for HA updates only.
+ "http-port": 8000,
+
+ // TLS trust anchor (Certificate Authority). This is a file name or
+ // (for OpenSSL only) a directory path.
+ "trust-anchor": "my-ca",
+
+ // TLS server certificate file name.
+ "cert-file": "my-cert",
+
+ // TLS server private key file name.
+ "key-file": "my-key",
+
+ // TLS require client certificates flag. Default is true and means
+ // require client certificates. False means they are optional.
+ "cert-required": true
+ }
+}
diff --git a/doc/examples/agent/rbac.json b/doc/examples/agent/rbac.json
new file mode 100644
index 0000000..1ac2453
--- /dev/null
+++ b/doc/examples/agent/rbac.json
@@ -0,0 +1,90 @@
+// This is an example of a configuration for Control-Agent (CA) using
+// the Role Based Access Control (RBAC) hook library.
+{
+ "Control-agent":
+ {
+ // We need to specify where the agent should listen to incoming HTTP
+ // queries.
+ "http-host": "127.0.0.1",
+
+ // If enabling HA and multi-threading, the 8000 port is used by the HA
+ // hook library http listener. When using HA hook library with
+ // multi-threading to function, make sure the port used by dedicated
+ // listener is different (e.g. 8001) than the one used by CA. Note
+ // the commands should still be sent via CA. The dedicated listener
+ // is specifically for HA updates only.
+ "http-port": 8000,
+
+ // TLS trust anchor (Certificate Authority). This is a file name or
+ // (for OpenSSL only) a directory path.
+ "trust-anchor": "my-ca",
+
+ // TLS server certificate file name.
+ "cert-file": "my-cert",
+
+ // TLS server private key file name.
+ "key-file": "my-key",
+
+ // TLS require client certificates flag. Default is true and means
+ // require client certificates. False means they are optional.
+ "cert-required": true,
+
+ // Add hooks here.
+ "hooks-libraries": [
+ {
+ "library": "/opt/lib/libca_rbac.so",
+ "parameters": {
+ // This section configures the RBAC hook library.
+ // Mandatory parameters.
+ "assign-role-method": "cert-subject",
+ "api-files": "/opt/share/kea/api",
+ // Optional parameters.
+ "require-tls": true,
+ "commands": [
+ {
+ "name": "my-command",
+ "access": "read",
+ "hook": "my-hook"
+ } ],
+ "access-control-lists": [
+ {
+ "my-none": { "not": "ALL" }
+ },{
+ "another-none": { "and": [ "ALL", "NONE" ] }
+ },{
+ "my-read": { "access": "read" }
+ } ],
+ "roles": [
+ {
+ "name": "kea-client",
+ "accept-commands":
+ {
+ "commands": [ "list-commands", "status-get" ]
+ },
+ "reject-commands": "NONE",
+ "other-commands": "reject",
+ "list-match-first": "accept",
+ "response-filters": [ "list-commands" ]
+ },{
+ "name": "admin",
+ "accept-commands": "ALL",
+ "reject-commands":
+ {
+ "hook": "cb_cmds"
+ },
+ "list-match-first": "reject"
+ } ],
+ "default-role":
+ {
+ "accept-commands": "NONE",
+ "reject-commands": "ALL"
+ },
+ "unknown-role":
+ {
+ "accept-commands": "READ",
+ "reject-commands": "WRITE"
+ }
+ }
+ } ]
+ }
+}
diff --git a/doc/examples/agent/simple.json b/doc/examples/agent/simple.json
new file mode 100644
index 0000000..5e82b99
--- /dev/null
+++ b/doc/examples/agent/simple.json
@@ -0,0 +1,150 @@
+// This is a simple example of a configuration for Control-Agent (CA) or simply
+// Agent. This server provides RESTful interface for all Kea servers.
+{
+ "Control-agent":
+ {
+ // We need to specify where the agent should listen to incoming HTTP
+ // queries.
+ "http-host": "127.0.0.1",
+
+ // If enabling HA and multi-threading, the 8000 port is used by the HA
+ // hook library http listener. When using HA hook library with
+ // multi-threading to function, make sure the port used by dedicated
+ // listener is different (e.g. 8001) than the one used by CA. Note
+ // the commands should still be sent via CA. The dedicated listener
+ // is specifically for HA updates only.
+ "http-port": 8000,
+
+ // Optional authentication.
+ "authentication":
+ {
+ // Required authentication type. The only supported value is
+ // basic for the basic HTTP authentication.
+ "type": "basic",
+
+ // An optional parameter is the basic HTTP authentication realm.
+ // Its default is "kea-control-agent".
+ "realm": "kea-control-agent",
+
+ // This optional parameter can be used to specify a common
+ // prefix for files handling client credentials.
+ "directory": "/tmp/kea-creds",
+
+ // This list specifies the user ids and passwords to use for
+ // basic HTTP authentication. If empty or not present any client
+ // is authorized.
+ "clients":
+ [
+ // This specifies an authorized client.
+ {
+ "comment": "admin is authorized",
+
+ // The user id must not be empty or contain the ':'
+ // character. It is a mandatory parameter.
+ "user": "admin",
+
+ // If password is not specified an empty password is used.
+ "password": "1234"
+ },
+
+ // This specifies a hiddent client.
+ {
+ // The user id is the content of the file /tmp/kea-creds/hiddenu.
+ "user-file": "hiddenu",
+
+ // The password is the content of the file /tmp/kea-creds/hiddenp.
+ "password-file": "hiddenp"
+ },
+
+ // This specifies a hidden client using a secret in a file.
+ {
+ // The secret is the content of the file /tmp/kea-creds/hiddens
+ // which must be in the <user-id>:<password> format.
+ "password-file": "hiddens"
+ }
+ ]
+ },
+
+ // This map specifies where control channel of each server is configured
+ // to listen on. See 'control-socket' object in the respective
+ // servers. At this time the only supported socket type is "unix".
+ // Make sure that the Agent and respective servers configuration
+ // matches exactly, otherwise they won't be able to communicate.
+ // One extra feature that requires some explanation is
+ // user-context. This is a structure that you can define at
+ // global scope, in control sockets and others. It is parsed by
+ // Kea, but not used directly. It is intended to keep anything
+ // you may want to put there - comments, extra designations,
+ // floor or department names etc. These structures will be made
+ // available to Kea hooks. A comment entry is translated into a
+ // user-context with a "comment" property so you can include
+ // comments inside the configuration itself.
+ "control-sockets":
+ {
+ // This is how the Agent can communicate with the DHCPv4 server.
+ "dhcp4":
+ {
+ "comment": "socket to DHCPv4 server",
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Location of the DHCPv6 command channel socket.
+ "dhcp6":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea6-ctrl-socket"
+ },
+
+ // Location of the D2 command channel socket.
+ "d2":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea-ddns-ctrl-socket",
+ "user-context": { "in-use": false }
+ }
+ },
+
+ // CA is able to load hook libraries that augment its operation.
+ // The primary functionality is the ability to add new commands.
+ "hooks-libraries": [
+ // Hook libraries list may contain more than one library.
+ {
+ // The only necessary parameter is the library filename.
+ "library": "/opt/local/control-agent-commands.so",
+
+ // Some libraries may support parameters. Make sure you
+ // type this section carefully, as the CA does not validate
+ // it (because the format is library-specific).
+ "parameters": {
+ "param1": "foo"
+ }
+ }
+ ],
+
+ // Similar to other Kea components, CA also uses logging.
+ "loggers": [
+ {
+ "name": "kea-ctrl-agent",
+ "output-options": [
+ {
+ "output": "/var/log/kea-ctrl-agent.log",
+ // Several additional parameters are possible in addition
+ // to the typical output. Flush determines whether logger
+ // flushes output to a file. Maxsize determines maximum
+ // filesize before the file is rotated. maxver
+ // specifies the maximum number of rotated files being
+ // kept.
+ "flush": true,
+ "maxsize": 204800,
+ "maxver": 4,
+ // We use pattern to specify custom log message layout
+ "pattern": "%d{%y.%m.%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0
+ }
+ ]
+ }
+}
diff --git a/doc/examples/ddns/all-keys-netconf.json b/doc/examples/ddns/all-keys-netconf.json
new file mode 100644
index 0000000..642258d
--- /dev/null
+++ b/doc/examples/ddns/all-keys-netconf.json
@@ -0,0 +1,183 @@
+// WARNING: This example configuration is not meant for production use.
+// The Kea Kea's DHCP-DDNS server can refuse this configuration because
+// it may contain mutually exclusive configuration parameters.
+//
+// The primary purpose of the example file is to provide a comprehensive
+// list of parameters supported by the Kea DHCP-DDNS server along with the
+// brief description of each parameter.
+//
+// This stable version is used for YANG as we do not want to update code
+// and models each time a keyword is added to the syntax.
+{
+ // Kea DHCP-DDNS server configuration begins here.
+ "DhcpDdns": {
+
+ // Global Parameters
+
+ // IP address D2 will listen for update requests at.
+ // Default is 127.0.0.1
+ "ip-address": "127.0.0.1",
+
+ // Port D2 will listen for update requests on.
+ // Default is 53001.
+ "port": 53001,
+
+ // Maximum time to we will wait for a DNS server to respond to us.
+ // Unit is the millisecond, default is 100ms.
+ "dns-server-timeout" : 100,
+
+ // Protocol to use for Name Change Requests from a Kea DHCP server.
+ // Currently only 'UDP' is supported.
+ "ncr-protocol": "UDP",
+
+ // Format to use for Name Change Requests from a Kea DHCP server.
+ // Currently only 'JSON' is supported.
+ "ncr-format": "JSON",
+
+ // Command control socket configuration parameters for Kea DHCP-DDNS server.
+ "control-socket": {
+
+ // Location of the UNIX domain socket file the DHCP-DDNS server uses
+ // to receive control commands from the Kea Control Agent or the
+ // local server administrator.
+ "socket-name": "/tmp/kea-ddns-ctrl-socket",
+
+ // Control socket type used by the Kea DHCP-DDNS server.
+ // The 'unix' socket is currently the only supported type.
+ "socket-type": "unix"
+ },
+
+ // List of hook libraries and their specific configuration parameters
+ // to be loaded by Kea DHCP-DDNS server.
+ "hooks-libraries": [
+ {
+ // Location of the hook library to be loaded.
+ "library": "/opt/local/ddns-server-commands.so",
+
+ // Hook library-specific configuration parameters.
+ "parameters": { }
+ }
+ ],
+
+ // Forward DDNS (Dynamic DNS).
+ "forward-ddns": {
+
+ // List of DDNS domains.
+ "ddns-domains": [
+ {
+ // Name of the zone (required).
+ "name": "example.com.",
+
+ // Name of the TSIG key used to protect DNS updates for
+ // names in the domain.
+ "key-name": "d2.md5.key",
+
+ // List of DNS servers where to send DNS updates.
+ "dns-servers": [
+ {
+ // DNS server IP address (required).
+ "ip-address": "2001:db8:1::10",
+
+ // DNS server UDP port. Default is 53 (DNS service).
+ "port": 7802,
+
+ // Name of the TSIG key used to protect DNS updates
+ // sent to the DNS server.
+ "key-name": "d2.sha1.key"
+ }
+ ]
+ }
+ ]
+ },
+
+ // Reverse DDNS (Dynamic DNS).
+ "reverse-ddns": {
+
+ // List of DDNS domains.
+ "ddns-domains": [
+ {
+ // Name of the zone (required).
+ "name": "2.0.192.in-addr.arpa.",
+
+ // Name of the TSIG key used to protect DNS updates for
+ // names in the domain.
+ "key-name": "d2.sha1.key",
+
+ // List of DNS servers where to send DNS updates.
+ "dns-servers": [
+ {
+ // DNS server IP address (required).
+ "ip-address": "172.16.1.1",
+
+ // DNS server UDP port. Default is 53 (DNS service).
+ "port": 5301,
+
+ // Name of the TSIG key used to protect DNS updates
+ "key-name": "d2.md5.key"
+ }
+ ]
+ }
+ ]
+ },
+
+ // List of TSIG keys used to protect DNS updates.
+ "tsig-keys": [
+ {
+ // Name of the TSIG key (required).
+ "name": "d2.md5.key",
+
+ // Algorithm of the TSIG key (required).
+ // The value must be a valid algorithm name e.g.
+ // HMAC-MD5, HMAC-SHA1, HMAC-SHA224, ...
+ "algorithm": "HMAC-MD5",
+
+ // Number of bits in the digest. Default is 0 which means
+ // to use all bits provided by the algorithm. Too short values
+ // (depending on the algorithm) are rejected.
+ "digest-bits": 0,
+
+ // Secret of the TSIG key (required) in base-64.
+ "secret": "LSWXnfkKZjdPJI5QxlpnfQ=="
+ }
+ ],
+
+ // List of loggers used by the servers using this configuration file.
+ "loggers": [
+ {
+ // Debug level, a value between 0..99. The greater the value
+ // the more detailed the debug log.
+ "debuglevel": 99,
+
+ // Name of the logger.
+ "name": "kea-dhcp-ddns",
+
+ // Configures how the log should be output.
+ "output-options": [
+ {
+ // Determines whether the log should be flushed to a file.
+ "flush": true,
+
+ // Specifies maximum filesize before the file is rotated.
+ "maxsize": 10240000,
+
+ // Specifies the maximum number of rotated files to be kept.
+ "maxver": 1,
+
+ // Specifies the logging destination.
+ "output": "stdout",
+
+ // Specifies log entry content
+ "pattern": "%D{%Y-%m-%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+ }
+ ],
+
+ // Specifies logging severity, i.e. "ERROR", "WARN", "INFO", "DEBUG".
+ "severity": "INFO"
+ }
+ ],
+
+ // Look at sample1 example for the use of user-contexts.
+ "user-context": { }
+}
+
+}
diff --git a/doc/examples/ddns/all-keys.json b/doc/examples/ddns/all-keys.json
new file mode 100644
index 0000000..6907d17
--- /dev/null
+++ b/doc/examples/ddns/all-keys.json
@@ -0,0 +1,183 @@
+// WARNING: This example configuration is not meant for production use.
+// The Kea Kea's DHCP-DDNS server can refuse this configuration because
+// it may contain mutually exclusive configuration parameters.
+//
+// The primary purpose of the example file is to provide a comprehensive
+// list of parameters supported by the Kea DHCP-DDNS server along with the
+// brief description of each parameter.
+//
+// This current version should be up to date, i.e. new keywords should be
+// added in this file at the same time as in the parser specification.
+{
+ // Kea DHCP-DDNS server configuration begins here.
+ "DhcpDdns": {
+
+ // Global Parameters
+
+ // IP address D2 will listen for update requests at.
+ // Default is 127.0.0.1
+ "ip-address": "127.0.0.1",
+
+ // Port D2 will listen for update requests on.
+ // Default is 53001.
+ "port": 53001,
+
+ // Maximum time to we will wait for a DNS server to respond to us.
+ // Unit is the millisecond, default is 100ms.
+ "dns-server-timeout" : 100,
+
+ // Protocol to use for Name Change Requests from a Kea DHCP server.
+ // Currently only 'UDP' is supported.
+ "ncr-protocol": "UDP",
+
+ // Format to use for Name Change Requests from a Kea DHCP server.
+ // Currently only 'JSON' is supported.
+ "ncr-format": "JSON",
+
+ // Command control socket configuration parameters for Kea DHCP-DDNS server.
+ "control-socket": {
+
+ // Location of the UNIX domain socket file the DHCP-DDNS server uses
+ // to receive control commands from the Kea Control Agent or the
+ // local server administrator.
+ "socket-name": "/tmp/kea-ddns-ctrl-socket",
+
+ // Control socket type used by the Kea DHCP-DDNS server.
+ // The 'unix' socket is currently the only supported type.
+ "socket-type": "unix"
+ },
+
+ // List of hook libraries and their specific configuration parameters
+ // to be loaded by Kea DHCP-DDNS server.
+ "hooks-libraries": [
+ {
+ // Location of the hook library to be loaded.
+ "library": "/opt/local/ddns-server-commands.so",
+
+ // Hook library-specific configuration parameters.
+ "parameters": { }
+ }
+ ],
+
+ // Forward DDNS (Dynamic DNS).
+ "forward-ddns": {
+
+ // List of DDNS domains.
+ "ddns-domains": [
+ {
+ // Name of the zone (required).
+ "name": "example.com.",
+
+ // Name of the TSIG key used to protect DNS updates for
+ // names in the domain.
+ "key-name": "d2.md5.key",
+
+ // List of DNS servers where to send DNS updates.
+ "dns-servers": [
+ {
+ // DNS server IP address (required).
+ "ip-address": "2001:db8:1::10",
+
+ // DNS server UDP port. Default is 53 (DNS service).
+ "port": 7802,
+
+ // Name of the TSIG key used to protect DNS updates
+ // sent to the DNS server.
+ "key-name": "d2.sha1.key"
+ }
+ ]
+ }
+ ]
+ },
+
+ // Reverse DDNS (Dynamic DNS).
+ "reverse-ddns": {
+
+ // List of DDNS domains.
+ "ddns-domains": [
+ {
+ // Name of the zone (required).
+ "name": "2.0.192.in-addr.arpa.",
+
+ // Name of the TSIG key used to protect DNS updates for
+ // names in the domain.
+ "key-name": "d2.sha1.key",
+
+ // List of DNS servers where to send DNS updates.
+ "dns-servers": [
+ {
+ // DNS server IP address (required).
+ "ip-address": "172.16.1.1",
+
+ // DNS server UDP port. Default is 53 (DNS service).
+ "port": 5301,
+
+ // Name of the TSIG key used to protect DNS updates
+ "key-name": "d2.md5.key"
+ }
+ ]
+ }
+ ]
+ },
+
+ // List of TSIG keys used to protect DNS updates.
+ "tsig-keys": [
+ {
+ // Name of the TSIG key (required).
+ "name": "d2.md5.key",
+
+ // Algorithm of the TSIG key (required).
+ // The value must be a valid algorithm name e.g.
+ // HMAC-MD5, HMAC-SHA1, HMAC-SHA224, ...
+ "algorithm": "HMAC-MD5",
+
+ // Number of bits in the digest. Default is 0 which means
+ // to use all bits provided by the algorithm. Too short values
+ // (depending on the algorithm) are rejected.
+ "digest-bits": 0,
+
+ // Secret of the TSIG key (required) in base-64.
+ "secret": "LSWXnfkKZjdPJI5QxlpnfQ=="
+ }
+ ],
+
+ // List of loggers used by the servers using this configuration file.
+ "loggers": [
+ {
+ // Debug level, a value between 0..99. The greater the value
+ // the more detailed the debug log.
+ "debuglevel": 99,
+
+ // Name of the logger.
+ "name": "kea-dhcp-ddns",
+
+ // Configures how the log should be output.
+ "output-options": [
+ {
+ // Determines whether the log should be flushed to a file.
+ "flush": true,
+
+ // Specifies maximum filesize before the file is rotated.
+ "maxsize": 10240000,
+
+ // Specifies the maximum number of rotated files to be kept.
+ "maxver": 1,
+
+ // Specifies the logging destination.
+ "output": "stdout",
+
+ // Specifies log entry content
+ "pattern": "%D{%Y-%m-%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+ }
+ ],
+
+ // Specifies logging severity, i.e. "ERROR", "WARN", "INFO", "DEBUG".
+ "severity": "INFO"
+ }
+ ],
+
+ // Look at sample1 example for the use of user-contexts.
+ "user-context": { }
+}
+
+}
diff --git a/doc/examples/ddns/comments.json b/doc/examples/ddns/comments.json
new file mode 100644
index 0000000..a7717d1
--- /dev/null
+++ b/doc/examples/ddns/comments.json
@@ -0,0 +1,64 @@
+// This is an example configuration file for D2, Kea's DHCP-DDNS processor.
+// It uses embedded comments which will be included in configuration objects
+// within user-contexts rather than stripped away by at lexical analysis.
+
+{
+"DhcpDdns":
+{
+
+ // Global scope
+ "comment": "A DHCP-DDNS server",
+ "ip-address": "127.0.0.1",
+ "port": 53001,
+ "dns-server-timeout" : 1000,
+
+ "control-socket":
+ {
+ "comment": "Control channel",
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea-ddns-ctrl-socket"
+ },
+
+ "forward-ddns":
+ {
+ "ddns-domains":
+ [
+ // In DDNS domain
+ {
+ "comment": "DdnsDomain for zone 'four.example.com.'",
+ "name": "four.example.com.",
+ "key-name": "d2.md5.key",
+ // In DNS server
+ "dns-servers":
+ [
+ {
+ "comment": "four.example.com. server",
+ "ip-address": "172.16.1.1"
+ }
+ ]
+ }
+ ]
+ },
+
+ // In TSIG key
+ "tsig-keys":
+ [
+ {
+ "comment": "four.example.com. key",
+ "name": "d2.md5.key",
+ "algorithm": "HMAC-MD5",
+ "secret": "LSWXnfkKZjdPJI5QxlpnfQ=="
+ }
+ ],
+
+ // In loggers
+ "loggers": [
+ {
+ "comment": "A logger",
+ "name": "kea-dhcp-ddns",
+ "severity": "info"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/ddns/gss-tsig.json b/doc/examples/ddns/gss-tsig.json
new file mode 100644
index 0000000..0af1351
--- /dev/null
+++ b/doc/examples/ddns/gss-tsig.json
@@ -0,0 +1,127 @@
+// This is an example configuration file for D2, Kea's DHCP-DDNS processor.
+// It uses the GSS-TSIG hook library.
+{
+"DhcpDdns": {
+ // The following parameters are used to receive NCRs (NameChangeRequests)
+ // from the local Kea DHCP server. Make sure your kea-dhcp4 and kea-dhcp6
+ // matches this.
+ "ip-address": "127.0.0.1",
+ "port": 53001,
+ "dns-server-timeout" : 1000,
+
+ // Forward zone: secure.example.org. It uses GSS-TSIG. It is served
+ // by two DNS servers, which listen for DDNS requests at 192.0.2.1
+ // and 192.0.2.2.
+ "forward-ddns":
+ {
+ "ddns-domains":
+ [
+ // DdnsDomain for zone "secure.example.org."
+ {
+ "name": "secure.example.org.",
+ "comment": "DdnsDomain example",
+ "dns-servers":
+ [
+ {
+ // This server has an entry in gss/servers and
+ // thus will use GSS-TSIG.
+ "ip-address": "192.0.2.1"
+ },
+ {
+ // This server also has an entry there, so will
+ // use GSS-TSIG, too.
+ "ip-address": "192.0.2.2",
+ "port": 5300
+ }
+ ]
+ }
+ ]
+ },
+
+ // Reverse zone: we want to update the reverse zone "2.0.192.in-addr.arpa".
+ "reverse-ddns":
+ {
+ "ddns-domains":
+ [
+ {
+ "name": "2.0.192.in-addr.arpa.",
+ "dns-servers":
+ [
+ {
+ // There is GSS-TSIG definition for this server (see
+ // DhcpDdns/gss-tsig/servers), so it will use
+ // Krb/GSS-TSIG.
+ "ip-address": "192.0.2.1"
+ }
+ ]
+ }
+ ]
+ },
+
+ // The GSS-TSIG hook is loaded and its configuration is specified here.
+ "hooks-libraries": [
+ {
+ "library": "/opt/lib/libddns_gss_tsig.so",
+ "parameters": {
+ // This section governs the GSS-TSIG integration. Each server
+ // mentioned in forward-ddns and/or reverse-ddns needs to have
+ // an entry here to be able to use GSS-TSIG defaults (optional,
+ // if specified they apply to all the GSS-TSIG servers, unless
+ // overwritten on specific server level).
+
+ "server-principal": "DNS/server.example.org@EXAMPLE.ORG",
+ "client-principal": "DHCP/admin.example.org@EXAMPLE.ORG",
+
+ // client-keytab and credentials-cache can both be used to
+ // store client keys. As credentials cache is more flexible,
+ // it is recommended to use it. Typically, using both at the
+ // same time may cause problems.
+ // "client-keytab": "FILE:/etc/dhcp.keytab", // toplevel only
+ "credentials-cache": "FILE:/etc/ccache", // toplevel only
+
+ "gss-replay-flag": true, // GSS anti replay service
+ "gss-sequence-flag": false, // no GSS sequence service
+ "tkey-lifetime": 3600, // 1 hour
+ "rekey-interval": 2700, // 45 minutes
+ "retry-interval": 120, // 2 minutes
+ "tkey-protocol": "TCP",
+ "fallback": false,
+
+ // The list of GSS-TSIG capable servers
+ "servers": [
+ {
+ // First server (identification is required)
+ "id": "server1",
+ "domain-names": [ ], // if not specified or empty, will
+ // match all domains that want to
+ // use this IP+port pair
+ "ip-address": "192.0.2.1",
+ "port": 53,
+ "server-principal": "DNS/server1.example.org@EXAMPLE.ORG",
+ "client-principal": "DHCP/admin1.example.org@EXAMPLE.ORG",
+ "gss-replay-flag": false, // no GSS anti replay service
+ "gss-sequence-flag": false, // no GSS sequence service
+ "tkey-lifetime": 7200, // 2 hours
+ "rekey-interval": 5400, // 90 minutes
+ "retry-interval": 240, // 4 minutes
+ "tkey-protocol": "TCP",
+ "fallback": true // if no key is available fallback to the
+ // standard behavior (vs skip this server)
+ },
+ {
+ // The second server (it has most of the parameters missing
+ // as those are using the defaults specified above)
+ "id": "server2",
+ "ip-address": "192.0.2.2",
+ "port": 5300
+ }
+ ]
+ }
+ }
+ ]
+
+ // Additional parameters, such as logging, control socket and
+ // others omitted for clarity.
+}
+
+}
diff --git a/doc/examples/ddns/sample1.json b/doc/examples/ddns/sample1.json
new file mode 100644
index 0000000..4c8190e
--- /dev/null
+++ b/doc/examples/ddns/sample1.json
@@ -0,0 +1,172 @@
+// This is an example configuration file for D2, Kea's DHCP-DDNS processor.
+// It supports updating two Forward DNS zones "four.example.com" and
+// "six.example.com"; and one Reverse DNS zone, "2.0.192.in-addr.arpa."
+
+{
+// ------------------ DHCP-DDNS ---------------------
+"DhcpDdns":
+{
+
+// -------------- Global Parameters ----------------
+// D2 will listen for update requests for Kea DHCP servers at 127.0.0.1
+// on port 53001. Maximum time to we will wait for a DNS server to
+// respond to us is 1000 ms.
+
+ "ip-address": "127.0.0.1",
+ "port": 53001,
+ "dns-server-timeout" : 1000,
+
+// One extra feature that requires some explanation is
+// user-context. This is a structure that you can define at global scope,
+// in ddns domain, dns server, tsig key and others. It is parsed by
+// Kea, but not used directly. It is intended to keep anything you
+// may want to put there - comments, extra designations, floor or
+// department names etc.
+// A comment entry is translated into a user-context with a "comment"
+// property so you can include comments inside the configuration itself.
+
+ "user-context": { "version": 1 },
+
+// ----------------- Control Socket -----------------
+
+ "control-socket":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea-ddns-ctrl-socket"
+ },
+
+// ----------------- Hooks Libraries -----------------
+
+ "hooks-libraries":
+ [
+ // Hook libraries list may contain more than one library.
+ {
+ // The only necessary parameter is the library filename.
+ "library": "/opt/local/ddns-server-commands.so",
+
+ // Some libraries may support parameters. Make sure you
+ // type this section carefully, as the CA does not validate
+ // it (because the format is library-specific).
+ "parameters":
+ {
+ "param1": "foo"
+ }
+ }
+ ],
+
+// ----------------- Forward DDNS ------------------
+// 1. Zone - "four.example.com.
+// It uses TSIG, key name is "d2.md5.key"
+// It is served by one DNS server which listens for DDNS requests at
+// 172.16.1.1 on the default port 53 (standard DNS port)
+// 2. Zone - "six.example.com."
+// It does not use TSIG.
+// It is server by one DNS server at "2001:db8:1::10" on port 7802
+
+ "forward-ddns":
+ {
+ "ddns-domains":
+ [
+// DdnsDomain for zone "four.example.com."
+ {
+ "comment": "DdnsDomain example",
+ "name": "four.example.com.",
+ "key-name": "d2.md5.key",
+ "dns-servers":
+ [
+ {
+ "ip-address": "172.16.1.1"
+ }
+ ]
+ },
+
+// DdnsDomain for zone "six.example.com."
+ {
+ "name": "six.example.com.",
+ "dns-servers":
+ [
+ {
+ "ip-address": "2001:db8:1::10",
+ "port": 7802
+ }
+ ]
+ }
+ ]
+ },
+
+// ----------------- Reverse DDNS ------------------
+// We will update Reverse DNS for one zone "2.0.192.in-addr-arpa". It
+// uses TSIG with key "d2.sha1.key" and is served by two DNS servers:
+// one listening at "172.16.1.1" on 53001 and the other at "192.168.2.10".
+ "reverse-ddns":
+ {
+ "ddns-domains":
+ [
+ {
+ "name": "2.0.192.in-addr.arpa.",
+ "key-name": "d2.sha1.key",
+ "dns-servers":
+ [
+ {
+ "ip-address": "172.16.1.1",
+ "port": 53001
+ },
+ {
+ "ip-address": "192.168.2.10"
+ }
+ ]
+ }
+ ]
+ },
+
+// ------------------ TSIG keys ---------------------
+// Each key has a name, an algorithm (HMAC-MD5, HMAC-SHA1, HMAC-SHA224...)
+// and a base-64 encoded shared secret.
+ "tsig-keys":
+ [
+ {
+ "name": "d2.md5.key",
+ "algorithm": "HMAC-MD5",
+ "secret": "LSWXnfkKZjdPJI5QxlpnfQ=="
+ },
+ {
+ "name": "d2.sha1.key",
+ "algorithm": "HMAC-SHA1",
+ "secret": "hRrp29wzUv3uzSNRLlY68w=="
+ },
+ {
+ "name": "d2.sha512.key",
+ "algorithm": "HMAC-SHA512",
+ "digest-bits": 256,
+ "secret": "/4wklkm04jeH4anx2MKGJLcya+ZLHldL5d6mK+4q6UXQP7KJ9mS2QG29hh0SJR4LA0ikxNJTUMvir42gLx6fGQ=="
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at least
+// informational level (info, warn, error and fatal) should be logged to stdout.
+// It also specifies a custom log pattern.
+ "loggers": [
+ {
+ "name": "kea-dhcp-ddns",
+ "output-options": [
+ {
+ "output": "stdout",
+ // Several additional parameters are possible in addition
+ // to the typical output. Flush determines whether logger
+ // flushes output to a file. Maxsize determines maximum
+ // filesize before the file is rotated. maxver
+ // specifies the maximum number of rotated files being
+ // kept.
+ "flush": true,
+ "maxsize": 204800,
+ "maxver": 4,
+ "pattern": "%d [%c/%i] %m\n"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/ddns/template.json b/doc/examples/ddns/template.json
new file mode 100644
index 0000000..c4da250
--- /dev/null
+++ b/doc/examples/ddns/template.json
@@ -0,0 +1,113 @@
+// This file may be used a template for constructing DHCP-DDNS JSON
+// configuration.
+// It must start with a left-curly-bracket.
+{
+
+"DhcpDdns" :
+{
+// -------------- Global Parameters ----------------
+// All of the global parameters have default values as shown. If these
+// are satisfactory you may omit them.
+// "ip-address" : "127.0.0.1",
+// "port" : 53001,
+// "dns-server-timeout" : 100,
+// "ncr-protocol" : "UDP"
+// "ncr-format" : "JSON"
+
+// ----------------- Control Socket -----------------
+
+// "control-socket":
+// {
+// "socket-type": "unix",
+// "socket-name": "/tmp/kea-ddns-ctrl-socket"
+// },
+
+// ----------------- Forward DDNS ------------------
+ "forward-ddns" :
+ {
+ "ddns-domains" :
+ [
+// {
+// "name" : "<zone name 1>",
+// "key-name" : "<key name>",
+// "dns-servers" :
+// [
+// {
+// "ip-address" : "<ip address>"
+// ,"port" : 53
+// }
+// ,
+// {
+// next DNS server for this DdnsDomain
+// }
+// :
+// ]
+// }
+// ,
+// {
+// next Forward DdnsDomain
+// }
+// :
+ ]
+ },
+
+// ----------------- Reverse DDNS ------------------
+ "reverse-ddns" :
+ {
+ "ddns-domains" :
+ [
+// {
+// "name" : "<reverse zone name 1>",
+// "key-name" : "<key name>",
+// "dns-servers" :
+// [
+// {
+// "ip-address" : "<ip address>"
+// ,"port" : 53
+// }
+// ,
+// {
+// next DNS server for this DdnsDomain
+// }
+// :
+// ]
+// }
+// ,
+// {
+// next Reverse DdnsDomain
+// }
+// :
+ ]
+ },
+// ------------------ TSIG keys ---------------------
+ "tsig-keys" :
+ [
+// {
+// "name" : "<key name>",
+// "algorithm" : "<algorithm name>",
+// Valid values for algorithm are: HMAC-MD5, HMAC-SHA1,
+// HMAC-SHA224, HMAC-SHA256,
+// HMAC-SHA384, HMAC-SHA512
+// "digest-bits" : 256,
+// Minimum truncated length in bits.
+// Default 0 (means truncation is forbidden).
+// "secret" : "<shared secret value>"
+// }
+// ,
+// {
+// next TSIG Key
+// }
+ ]
+
+// Logging
+// ,"loggers":
+// [
+// {
+// "name": "kea-dhcp-ddns",
+// "severity": "info"
+// }
+// ]
+}
+
+// It must end with an right-curly-bracket.
+}
diff --git a/doc/examples/https/httpd2/kea-httpd2.conf b/doc/examples/https/httpd2/kea-httpd2.conf
new file mode 100644
index 0000000..b138673
--- /dev/null
+++ b/doc/examples/https/httpd2/kea-httpd2.conf
@@ -0,0 +1,129 @@
+# This file contains a partial Apache2 server configuration which
+# enables reverse proxy service for Kea RESTful API. An access to
+# the service is protected by client's certificate verification
+# mechanism. Before using this configuration a server administrator
+# must generate server certificate and private key as well as
+# the certificate authority (CA). The clients' certificates must
+# be signed by the CA.
+#
+# Note that the steps provided below to generate and setup certificates
+# are provided as an example for testing purposes only. Always
+# consider best known security measures to protect your production
+# environment.
+#
+# The server certificate and key can be generated as follows:
+#
+# openssl genrsa -des3 -out kea-proxy.key 4096
+# openssl req -new -x509 -days 365 -key kea-proxy.key -out kea-proxy.crt
+#
+# The CA certificate and key can be generated as follows:
+#
+# openssl genrsa -des3 -out ca.key 4096
+# openssl req -new -x509 -days 365 -key ca.key -out ca.crt
+#
+#
+# The client certificate needs to be generated and signed:
+#
+# openssl genrsa -des3 -out kea-client.key 4096
+# openssl req -new -key kea-client.key -out kea-client.csr
+# openssl x509 -req -days 365 -in kea-client.csr -CA ca.crt \
+# -CAkey ca.key -set_serial 10 -out kea-client.crt
+#
+# Note that the 'common name' value used when generating the client
+# and the server certificates must differ from the value used
+# for the CA certificate.
+#
+# The client certificate must be deployed on the client system.
+# In order to test the proxy configuration with 'curl' run
+# command similar to the following:
+#
+# curl -k --key kea-client.key --cert kea-client.crt -X POST \
+# -H Content-Type:application/json -d '{ "command": "list-commands" }' \
+# https://kea.example.org/kea
+#
+# On some curl running on macOS the crypto library requires a PKCS#12
+# bundle with the private key and the certificate as the cert argument.
+# The PKCS#12 file can be generated by:
+#
+# openssl pkcs12 -export -in kea-client.crt -inkey kea-client.key \
+# -out kea-client.p12
+#
+# If the password is kea, curl command becomes:
+#
+# curl -k --cert kea-client.p12:kea -X POST \
+# -H Content-Type:application/json -d '{ "command": "list-commands" }' \
+# https://kea.example.org/kea
+#
+#
+# In order to use this configuration within your Apache2 configuration
+# put the following line in the main Apache 2 configuration file:
+#
+# Include /path/to/kea-httpd2.conf
+#
+# and specify a path appropriate for your system.
+#
+#
+# Apache2 server configuration starts here.
+#
+# Address and port that the server should bind to.
+# Usually an explicit address is specified to avoid binding to
+# many addresses. For testing https connection on the localhost
+# use:
+# Listen [::1]:443 or
+# Listen 127.0.0.1:443
+Listen *:443
+
+# List the ciphers that the client is permitted to negotiate,
+# and that httpd will negotiate as the client of a proxied server.
+# See the OpenSSL documentation for a complete list of ciphers, and
+# ensure these follow appropriate best practices for this deployment.
+# httpd 2.2.30, 2.4.13 and later force-disable aNULL, eNULL and EXP ciphers,
+# while OpenSSL disabled these by default in 0.9.8zf/1.0.0r/1.0.1m/1.0.2a.
+SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4
+SSLProxyCipherSuite HIGH:MEDIUM:!MD5:!RC4
+
+# User agents such as web browsers are not configured for the user's
+# own preference of either security or performance, therefore this
+# must be the prerogative of the web server administrator who manages
+# cpu load versus confidentiality, so enforce the server's cipher order.
+SSLHonorCipherOrder on
+
+# List the protocol versions which clients are allowed to connect with.
+# Disable SSLv2 and SSLv3 by default (cf. RFC 7525 3.1.1). TLSv1 (1.0)
+# should be disabled as quickly as practical. By the end of 2016, only
+# the TLSv1.2 protocol or later should remain in use.
+SSLProtocol all -SSLv2 -SSLv3
+SSLProxyProtocol all -SSLv2 -SSLv3
+
+# Semaphore:
+# Configure the path to the mutual exclusion semaphore the
+# SSL engine uses internally for inter-process synchronization.
+SSLMutex "file:/usr/local/var/run/apache2/ssl_mutex"
+
+<VirtualHost *:443>
+ # For URLs such as https://kea.example.org/kea, forward the requests
+ # to http://127.0.0.1:8000
+ ProxyPass /kea http://127.0.0.1:8000/
+ ProxyPassReverse /kea http://127.0.0.1:8000/
+
+ # Disable connection keep alive between the proxy and Kea because
+ # Kea doesn't support this mechanism.
+ SetEnv proxy-nokeepalive 1
+
+ # Set server name.
+ ServerName kea.example.org
+
+ # Enable SSL for this virtual host.
+ SSLEngine on
+
+ # Server certificate and private key.
+ SSLCertificateFile "/path/to/kea-proxy.crt"
+ SSLCertificateKeyFile "/path/to/kea-proxy.key"
+
+ # Enable verification of the client certificate.
+ SSLVerifyClient require
+
+ # Certificate Authority. Client certificate must be signed by the CA.
+ SSLCACertificateFile "/path/to/ca.crt"
+
+</VirtualHost>
diff --git a/doc/examples/https/nginx/kea-nginx.conf b/doc/examples/https/nginx/kea-nginx.conf
new file mode 100644
index 0000000..cdbd7b3
--- /dev/null
+++ b/doc/examples/https/nginx/kea-nginx.conf
@@ -0,0 +1,88 @@
+# This file contains an example nginx HTTP server configuration which
+# enables reverse proxy service for Kea RESTful API. An access to
+# the service is protected by client's certificate verification
+# mechanism. Before using this configuration a server administrator
+# must generate server certificate and private key as well as
+# the certificate authority (CA). The clients' certificates must
+# be signed by the CA.
+#
+# Note that the steps provided below to generate and setup certificates
+# are provided as an example for testing purposes only. Always
+# consider best known security measures to protect your production
+# environment.
+#
+# The server certificate and key can be generated as follows:
+#
+# openssl genrsa -des3 -out kea-proxy.key 4096
+# openssl req -new -x509 -days 365 -key kea-proxy.key -out kea-proxy.crt
+#
+# The CA certificate and key can be generated as follows:
+#
+# openssl genrsa -des3 -out ca.key 4096
+# openssl req -new -x509 -days 365 -key ca.key -out ca.crt
+#
+#
+# The client certificate needs to be generated and signed:
+#
+# openssl genrsa -des3 -out kea-client.key 4096
+# openssl req -new -key kea-client.key -out kea-client.csr
+# openssl x509 -req -days 365 -in kea-client.csr -CA ca.crt \
+# -CAkey ca.key -set_serial 10 -out kea-client.crt
+#
+# Note that the 'common name' value used when generating the client
+# and the server certificates must differ from the value used
+# for the CA certificate.
+#
+# The client certificate must be deployed on the client system.
+# In order to test the proxy configuration with 'curl' run
+# command similar to the following:
+#
+# curl -k --key kea-client.key --cert kea-client.crt -X POST \
+# -H Content-Type:application/json -d '{ "command": "list-commands" }' \
+# https://kea.example.org
+#
+# On some curl running on macOS the crypto library requires a PKCS#12
+# bundle with the private key and the certificate as the cert argument.
+# The PKCS#12 file can be generated by:
+#
+# openssl pkcs12 -export -in kea-client.crt -inkey kea-client.key \
+# -out kea-client.p12
+#
+# If the password is kea, curl command becomes:
+#
+# curl -k --cert kea-client.p12:kea -X POST \
+# -H Content-Type:application/json -d '{ "command": "list-commands" }' \
+# https://kea.example.org
+#
+# nginx configuration starts here.
+
+events {
+}
+
+http {
+ # HTTPS server
+ server {
+ # Use default HTTPS port.
+ listen 443 ssl;
+ # Set server name.
+ server_name kea.example.org;
+
+ # Server certificate and key.
+ ssl_certificate /path/to/kea-proxy.crt;
+ ssl_certificate_key /path/to/kea-proxy.key;
+
+ # Certificate Authority. Client certificate must be signed by the CA.
+ ssl_client_certificate /path/to/ca.crt;
+
+ # Enable verification of the client certificate.
+ ssl_verify_client on;
+
+ # For the URL https://kea.example.org forward the
+ # requests to http://127.0.0.1:8000.
+ # kea-shell defaults to / but --path can be used to set another value
+ # for instance kea-shell --path kea which will matches location /kea
+ location / {
+ proxy_pass http://127.0.0.1:8000;
+ }
+ }
+}
diff --git a/doc/examples/https/shell/kea-stunnel.conf b/doc/examples/https/shell/kea-stunnel.conf
new file mode 100644
index 0000000..1d40aca
--- /dev/null
+++ b/doc/examples/https/shell/kea-stunnel.conf
@@ -0,0 +1,46 @@
+; This file contains an example stunnel TLS client configuration which
+; enables secure transport for Kea RESTful API. An access to
+; the service is protected by client's and server's certificate
+; verification mechanism (as known as mutual authentication).
+;
+; Note that the setup below (and reused nginx or httpd2 setups)
+; are provided as an example for testing purposes only. Always
+; consider best known security measures to protect your production
+; environment.
+;
+; Transport marked with ==> (vs -->) is secured against passive
+; (i.e. eavesdropping) and active (i.e. man-in-the-middle) attacks
+;
+; kea-shell -- 127.0.0.1 port 8888 -->
+; stunnel == 127.0.0.1 port 443 ==>
+; nginx -- 127.0.0.1 port 8000 -->
+; kea-agent
+;
+; stunnel configuration starts here.
+
+; in the case you would like to follow what happens
+;; foreground = yes
+;; debug = 7
+
+; kea service
+[kea]
+ ; client (vs server) mode
+ client = yes
+
+ ; accept requests from the kea-shell tool
+ accept = 127.0.0.1:8888
+
+ ; forward requests to the https peer
+ connect = 127.0.0.1:443
+
+ ; client certificate
+ cert = kea-client.crt
+
+ ; client private key
+ key = kea-client.key
+
+ ; check server certificate
+ verifyPeer = yes
+
+ ; server certificate
+ CAfile = kea-proxy.crt
diff --git a/doc/examples/kea4/advanced.json b/doc/examples/kea4/advanced.json
new file mode 100644
index 0000000..16a9498
--- /dev/null
+++ b/doc/examples/kea4/advanced.json
@@ -0,0 +1,208 @@
+// This is an example configuration file for DHCPv4 server in Kea.
+// It covers some of the more advanced features. This file may not be coherent
+// as its main purpose is to demonstrate the features. They don't necessarily
+// have to make sense used together.
+
+// The new parser supports 3 comment styles:
+
+// This is C++ style.
+
+# This is a bash style.
+
+/* This is a C style comment. */
+
+/* C style comment
+ can span
+ multiple lines */
+
+{ "Dhcp4":
+
+{
+ // Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ],
+
+ // This specifies what type of socket Kea uses. Currently supported
+ // are 'raw' (which is the default) and 'udp'. Raw has the benefit
+ // of receiving all traffic every time and a downside of bypassing
+ // all firewall rules and having marginally bigger performance impact.
+ // 'udp' is generally better if you have only relayed traffic. Kea
+ // than opens up normal UDP socket and the kernel does all the
+ // Ethernet/IP stack processing.
+ "dhcp-socket-type": "udp",
+
+ // Typically the DHCP server will send its response back on the same
+ // interface the query came in. This is the default ("same-as-inbound").
+ // However, sometimes it is useful to have the ability to send the
+ // packet as plain UDP packet and let the kernel and the routing tables
+ // determine the right interface ("use-routing"). This option only works
+ // for "dhcp-socket-type" set to "udp" and is ignored otherwise.
+ "outbound-interface": "use-routing",
+
+ // This makes interfaces to be re-detected at each (re-)configuration.
+ // By default it is true.
+ "re-detect": true
+ },
+
+ "sanity-checks": {
+ // This parameter determines what to do when a new lease appears in the
+ // system (i.e. either is read from disk during memfile startup or is
+ // added via lease commands). There are five modes supported:
+ // none - do nothing, accept them as is
+ // warn - if subnet-id problems are detected, print a warning, but
+ // otherwise load the lease as is. This is the default value.
+ // fix - attempt to fix the lease by finding appropriate subnet-id value.
+ // if there is no suitable subnet, the lease is loaded as is.
+ // fix-del - attempt to fix the lease by finding appropriate subnet-id
+ // value. If there is no suitable subnet, the lease is deleted.
+ // del - delete leases that have incorrect subnet-id values.
+ "lease-checks": "fix-del"
+ },
+
+ // Option 43 last resort definition can make well-formed messages
+ // to be rejected because they use not compatible "raw" value,
+ // and different vendors may define different sub-options.
+ // The option definition should be applied to avoid these problems,
+ // for instance by defining at the global scope the option as binary.
+ // In client-classes the option may be redefined as carrying vendor
+ // dependent sub-options.
+ "option-def": [ {
+ "name": "vendor-encapsulated-options",
+ "code": 43,
+ "type": "binary"
+ } ],
+
+ // We need to specify the database used to store leases. As of
+ // June 2022, three database backends are supported: MySQL,
+ // PostgreSQL and the in-memory database, Memfile.
+ // We'll use memfile because it doesn't require any prior set up.
+ // For memfile, it's important to always specify lfc-interval, so
+ // the lease file would not grow without bounds and be sanitized
+ // once per hour.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+ // This defines a control socket. If defined, Kea will open a UNIX socket
+ // and will listen for incoming commands. See section 15 of the Kea User's
+ // Guide for list of supported commands.
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Addresses will be assigned with a lifetime of 4000 seconds.
+ // The client is told to start renewing after 1000 seconds. If the server
+ // does not respond within 2000 seconds of the lease being granted, client
+ // is supposed to start REBIND procedure (emergency renewal that allows
+ // switching to a different server).
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+ // RFC6842 says that the server is supposed to echo back client-id option.
+ // However, some older clients do not support this and are getting confused
+ // when they get their own client-id. Kea can disable RFC6842 support.
+ "echo-client-id": false,
+
+ // Some clients don't use stable client identifier, but rather
+ // generate them during each boot. This may cause a client that
+ // reboots frequently to get multiple leases, which may not be
+ // desirable. As such, sometimes admins prefer to tell their DHCPv4
+ // server to ignore client-id value altogether and rely exclusively
+ // on MAC address. This is a parameter that is defined globally, but
+ // can be overridden on a subnet level.
+ "match-client-id": true,
+
+ // By default, Kea ignores requests by clients for unknown IP addresses,
+ // because other non-cooperating DHCP servers could reside on the same
+ // network (RFC 2131). This parameter is defined globally, but can be
+ // overridden on a subnet level
+ "authoritative": false,
+
+ // The following list defines subnets. Each subnet consists of at
+ // least subnet and pool entries. One extra feature that requires
+ // some explanation is user-context. This is a structure that you can
+ // define in subnets, pools and others. It is parsed by Kea, but not
+ // used directly. It is intended to keep anything you may want to
+ // put there - comments, extra designations, floor or department
+ // names etc. These structures will be made available to Kea hooks.
+ // A comment entry is translated into a user-context with a
+ // "comment" property so you can include comments inside the
+ // configuration itself.
+ "subnet4": [
+ {
+ "pools": [ {
+ "pool": "192.0.2.1 - 192.0.2.200",
+ "user-context": { "info": "what a large pool" }
+ } ],
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "user-context": {
+ "comment": "Our first subnet!"
+ }
+ // Equivalent using smart parser
+ // "comment": "Our first subnet!"
+ },
+ {
+ // This particular subnet has match-client-id value changed.
+ // This causes Kea to ignore client-id values in this subnet
+ // and rely exclusively on MAC addresses.
+ "pools": [ { "pool": "192.0.3.100 - 192.0.3.200" } ],
+ "id": 2,
+ "subnet": "192.0.3.0/24",
+ "match-client-id": false
+ },
+ {
+ "pools": [ { "pool": "192.0.4.1 - 192.0.4.254" } ],
+ "id": 3,
+ "subnet": "192.0.4.0/24",
+
+ // Sometimes the relay may use an IPv4 address that does
+ // not match the subnet. This is discouraged, but there are
+ // valid cases when it makes sense. One case is when there
+ // is a shared subnet.
+ "relay": {
+ "ip-address": "192.168.1.1"
+ }
+ },
+ {
+ // This particular subnet has the authoritative value changed.
+ // This causes Kea to reply to requests for unknown IP addresses
+ // with a DHCPNAK message.
+ "pools": [ { "pool": "192.0.5.100 - 192.0.5.200" } ],
+ "id": 4,
+ "subnet": "192.0.5.0/24",
+ "authoritative": true
+ }
+ ],
+
+ // The following configures logging. It assumes that messages with
+ // at least informational level (info, warn, error and fatal) should
+ // be logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout",
+ // Several additional parameters are possible in addition
+ // to the typical output. Flush determines whether logger
+ // flushes output to a file. Maxsize determines maximum
+ // filesize before the file is rotated. maxver
+ // specifies the maximum number of rotated files being
+ // kept.
+ "flush": true,
+ "maxsize": 204800,
+ "maxver": 4,
+ // We use pattern to specify custom log message layout
+ "pattern": "%d{%y.%m.%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
+ }
+
+}
diff --git a/doc/examples/kea4/all-keys-netconf.json b/doc/examples/kea4/all-keys-netconf.json
new file mode 100644
index 0000000..c5cf903
--- /dev/null
+++ b/doc/examples/kea4/all-keys-netconf.json
@@ -0,0 +1,1245 @@
+// WARNING: This example configuration is not meant for production use.
+// The Kea DHCPv4 server will refuse this configuration because it contains
+// mutually exclusive configuration parameters.
+//
+// The primary purpose of the example file is to provide a comprehensive
+// list of parameters supported by the Kea DHCPv4 server, along with a brief
+// description of each parameter.
+//
+// This stable version is used for YANG, as we do not want to update code
+// and models each time a keyword is added to the syntax.
+{
+ // Kea DHCPv4 server configuration begins here.
+ "Dhcp4": {
+ // Global flag selecting an IP address allocation strategy for all
+ // subnets. Use "random" for a random allocation strategy.
+ "allocator": "iterative",
+
+ // Global authoritative flag to handle requests by clients for
+ // unknown IP addresses (ignore if disabled, NAK if enabled).
+ "authoritative": false,
+
+ // Global bootfile name to be set in the 'file' field.
+ "boot-file-name": "/dev/null",
+
+ // Ordered list of client classes used by the DHCPv4 server.
+ "client-classes": [
+ {
+ // Class-specific bootfile name to be set in the 'file' field.
+ "boot-file-name": "/tmp/bootfile.efi",
+
+ // Class name.
+ "name": "phones_server1",
+
+ // Class-specific next server address to use in bootstrap, which
+ // is set in 'siaddr' field.
+ "next-server": "10.2.3.4",
+
+ // Class-specific DHCPv4 options list.
+ "option-data": [],
+
+ // Class-specific DHCPv4 option definitions, i.e. custom formats
+ // specified for non-standard options.
+ "option-def": [],
+
+ // Class-specific optional server hostname, which is set in
+ // 'sname' field.
+ "server-hostname": "",
+
+ // Class selection expression. The DHCP packet is assigned to this
+ // class when the given expression evaluates to true.
+ "test": "member('HA_server1')",
+
+ // Class valid lifetime.
+ "valid-lifetime": 6000,
+
+ // Class min valid lifetime.
+ "min-valid-lifetime": 4000,
+
+ // Class max valid lifetime.
+ "max-valid-lifetime": 8000,
+
+ // If greater than zero, it is the lifetime of leases temporarily allocated
+ // on DISCOVER. When zero (the default), leases are not allocated on DISCOVER.
+ "offer-lifetime" : 65
+ },
+ {
+ // Default value of the class-specific bootfile name. An empty name
+ // means that the bootfile name is unspecified.
+ "boot-file-name": "",
+
+ // Second class name.
+ "name": "phones_server2",
+
+ // Default value of the class-specific next server address. The
+ // zero IPv4 address means that it is unspecified.
+ "next-server": "0.0.0.0",
+
+ // Class-specific DHCPv4 options list.
+ "option-data": [],
+
+ // Class-specific DHCPv4 option definitions, i.e. custom formats
+ // specified for non-standard options.
+ "option-def": [],
+
+ // Class-specific optional server hostname, which is set in
+ // 'sname' field.
+ "server-hostname": "",
+
+ // Class selection expression. The DHCP packet is assigned to this
+ // class when the given expression evaluates to true.
+ "test": "member('HA_server2')"
+ },
+ {
+ // Third class name.
+ "name": "late",
+
+ // Boolean flag indicating whether the class expression is only evaluated
+ // when the class is required, e.g. the selected address pool configuration
+ // includes this class name in its "require-client-classes" list. The
+ // default value false means that the class test expression must
+ // always be evaluated.
+ "only-if-required": true,
+
+ // Class selection expression.
+ "test": "member('ALL')"
+ },
+ {
+ // Fourth class name.
+ "name": "my-template-class",
+
+ // Template class flag that holds the expression used to generate the names for all
+ // the spawned subclasses. In this case, the classes are named after the client ID.
+ "template-test": "substring(option[61].hex, 0, all)"
+ }
+ ],
+
+ // Parameters for triggering behaviors compatible with broken or
+ // non-compliant clients, relays, or other agents
+ "compatibility": {
+ // Ignore DHCP Server Identifier option if set to true.
+ // Enabling this will cause Kea to accept any query, even
+ // if the address in the option belongs to another server,
+ // instead of dropping it. This config option defaults to
+ // false, as enabling it breaks RFC compliance.
+ "ignore-dhcp-server-identifier": false,
+
+ // Ignore Relay Agent Information Link Selection suboption if set
+ // to true. Enabling this will cause Kea to use normal subnet
+ // selection logic instead of attempting to use the subnet
+ // specified in the suboption. This config option defaults to
+ // false, as enabling it breaks RFC compliance.
+ "ignore-rai-link-selection": false,
+
+ // Parse options more leniently where fields can be deduced
+ // deterministically, even if against RFC or common practice.
+ "lenient-option-parsing": true,
+
+ // Boolean flag indicating whether .0 and .255 addresses
+ // must be considered as never free in subnets with a prefix length
+ // of 24 or less. The default is false, as these addresses are not
+ // special; only the first and the last addresses are.
+ "exclude-first-last-24": false
+ },
+
+ // Command control socket configuration parameters for the Kea DHCPv4 server.
+ "control-socket": {
+ // Location of the UNIX domain socket file the DHCPv4 server uses
+ // to receive control commands from the Kea Control Agent or the
+ // local server administrator.
+ "socket-name": "/tmp/kea4-ctrl-socket",
+
+ // Control socket type used by the Kea DHCPv4 server. The 'unix'
+ // socket is currently the only supported type.
+ "socket-type": "unix"
+ },
+
+ // Specifies a prefix to be prepended to the generated Client FQDN.
+ // It may be specified at the global, shared-network, and subnet levels.
+ "ddns-generated-prefix": "myhost",
+
+ // Boolean flag indicating whether the server should ignore DHCP client
+ // wishes to update DNS on its own. With that flag set to true,
+ // the server will send DNS updates for both forward and
+ // reverse DNS data. The default value is false, which indicates
+ // that the server will delegate a DNS update to the client when
+ // requested. It may be specified at the global, shared-network,
+ // and subnet levels.
+ "ddns-override-client-update": false,
+
+ // Boolean flag indicating whether the server should override the DHCP
+ // client's wish to not update the DNS. With this parameter
+ // set to true, the server will send a DNS update even when
+ // the client requested no update. It may be specified at the
+ // global, shared-network, and subnet levels.
+ "ddns-override-no-update": false,
+
+ // Suffix appended to the partial name sent to the DNS. The
+ // default value is an empty string, which indicates that no
+ // suffix is appended. It may be specified at the global,
+ // shared-network, and subnet levels.
+ "ddns-qualifying-suffix": "",
+
+ // Enumeration specifying whether the server should honor
+ // the hostname or Client FQDN sent by the client or replace
+ // this name. The acceptable values are: "never" (use the
+ // name the client sent), "always" (replace the name the
+ // client sent), "when-present" (replace the name the client
+ // sent, but do not generate one when the client didn't send
+ // the name), "when-not-present" (generate the name when
+ // client didn't send one, otherwise leave the name the
+ // client sent). The default value is "never". It may be
+ // specified at the global, shared-network, and subnet levels.
+ "ddns-replace-client-name": "never",
+
+ // Boolean flag which enables or disables DDNS updating. It
+ // defaults to true. It may be specified at the global, shared-
+ // network, and subnet levels. It works in conjunction with
+ // dhcp-ddns:enable-updates, which must be true to enable connectivity
+ // to kea-dhcp-ddns.
+ "ddns-send-updates": true,
+
+ // Boolean flag, which when true instructs the server to always
+ // update DNS when leases are renewed, even if the DNS information
+ // has not changed. The server's default behavior (i.e. flag is false)
+ // is to only update DNS if the DNS information has changed. It
+ // may be specified at the global, shared-network, and subnet levels.
+ "ddns-update-on-renew": true,
+
+ // Boolean flag which is passed to kea-dhcp-ddns with each DDNS
+ // update request, to indicate whether DNS update conflict
+ // resolution as described in RFC 4703 should be employed for the
+ // given update request. The default value for this flag is true.
+ // It may be specified at the global, shared-network, and subnet levels.
+ "ddns-use-conflict-resolution": true,
+
+ // When greater than 0.0, it is the percent of the lease's lifetime
+ // to use for the DNS TTL.
+ "ddns-ttl-percent": 0.75,
+
+ // Time in seconds specifying how long a declined lease should be
+ // excluded from DHCP assignments. The default value is 86400 (24 hours).
+ "decline-probation-period": 86400,
+
+ // Name Change Request forwarding configuration for the Kea DHCPv4 server.
+ // NCRs are sent to the Kea D2 module to update DNS upon allocation of
+ // DHCP leases.
+ "dhcp-ddns": {
+ // Boolean flag indicating whether Kea DHCPv4 server should connect to
+ // kea-dhcp-ddns. This must be true for NCRs to be created and
+ // sent to kea-dhcp-ddns. By default, NCRs are not generated.
+ "enable-updates": false,
+
+ // Specifies maximum number of NCRs to queue waiting to be sent
+ // to the Kea D2 server.
+ "max-queue-size": 1024,
+
+ // Packet format to use when sending NCRs to the Kea D2 server.
+ // Currently, only JSON format is supported.
+ "ncr-format": "JSON",
+
+ // Socket protocol to use when sending NCRs to D2. Currently,
+ // only UDP is supported.
+ "ncr-protocol": "UDP",
+
+ // IP address that the Kea DHCPv4 server should use to send
+ // NCRs to D2. The default value of zero indicates that Kea
+ // should pick a suitable address.
+ "sender-ip": "0.0.0.0",
+
+ // Port number that the Kea DHCPv4 server should use to send
+ // NCRs to D2. The default value of zero indicates that Kea
+ // should pick a suitable port.
+ "sender-port": 0,
+
+ // IP address on which D2 listens for NCRs.
+ "server-ip": "127.0.0.1",
+
+ // Port number on which D2 listens for NCRs.
+ "server-port": 53001,
+
+ // The following parameters are DEPRECATED. They have been
+ // replaced with parameters that may be set at the global,
+ // shared-network, and subnet4 scopes. They are listed here
+ // as configuration parsing still accepts them. Eventually
+ // support for them will be removed.
+ "generated-prefix": "myhost",
+ "hostname-char-replacement": "x",
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+ "override-client-update": false,
+ "override-no-update": false,
+ "qualifying-suffix": "",
+ "replace-client-name": "never"
+ },
+
+ // Specifies the first of the two consecutive ports of the UDP
+ // sockets used for communication between DHCPv6 and DHCPv4
+ // servers. See RFC 7341.
+ "dhcp4o6-port": 6767,
+
+ // Boolean flag indicating whether the Kea DHCPv4 server
+ // should send back the Client Identifier option in its responses.
+ // The default value is true, which indicates that the option
+ // must be sent back if the client included it. The false
+ // value instructs the server to not send this option for
+ // backward compatibility with older DHCP specifications, which
+ // stated that Client Identifier must not be sent back.
+ "echo-client-id": true,
+
+ // Collection of Kea DHCPv4 server parameters configuring how
+ // the server should process expired DHCP leases.
+ "expired-leases-processing": {
+ // Specifies the number of seconds since the last removal of
+ // the expired leases, when the next removal should occur.
+ // If both "flush-reclaimed-timer-wait-time" and
+ // "hold-reclaimed-time" are not 0, when the client sends a release
+ // message the lease is expired instead of being deleted from
+ // lease storage.
+ "flush-reclaimed-timer-wait-time": 25,
+
+ // Specifies the length of time in seconds to keep expired
+ // leases in the lease database (lease affinity).
+ // If both "flush-reclaimed-timer-wait-time" and
+ // "hold-reclaimed-time" are not 0, when the client sends a release
+ // message the lease is expired instead of being deleted from
+ // lease storage.
+ "hold-reclaimed-time": 3600,
+
+ // Specifies the maximum number of expired leases that can be
+ // processed in a single attempt to clean up expired leases
+ // from the lease database. If there are more
+ // expired leases, they will be processed during the next
+ // cleanup attempt.
+ "max-reclaim-leases": 100,
+
+ // Specifies the maximum time in milliseconds that a single attempt
+ // to clean up expired leases from the lease database may take.
+ "max-reclaim-time": 250,
+
+ // Specifies the length of time in seconds since the last attempt
+ // to process expired leases before initiating the next attempt.
+ "reclaim-timer-wait-time": 10,
+
+ // Specifies the maximum number of expired lease-processing cycles
+ // which didn't result in full cleanup of exired leases from the
+ // lease database, after which a warning message is issued.
+ "unwarned-reclaim-cycles": 5
+ },
+
+ // List of hook libraries and their specific configuration parameters
+ // to be loaded by Kea DHCPv4 server.
+ "hooks-libraries": [
+ {
+ // Location of the hook library to be loaded.
+ "library": "/opt/lib/kea/hooks/libdhcp_lease_cmds.so",
+
+ // Hook library-specific configuration parameters.
+ "parameters": { }
+ }
+ ],
+
+ // List of access credentials to external sources of IPv4 reservations,
+ "hosts-databases": [
+ {
+ // Name of the database to connect to.
+ "name": "keatest",
+
+ // Host on which the database resides.
+ "host": "localhost",
+
+ // Database password.
+ "password": "keatest",
+
+ // Port on which the database is available.
+ "port": 3306,
+
+ // Type of database, e.g. "mysql", "postgresql".
+ "type": "mysql",
+
+ // Username to be used to access the database.
+ "user": "keatest",
+
+ // Read-only mode.
+ "readonly": false,
+
+ // The next entries are for OpenSSL support in MySQL.
+
+ // Trust anchor aka certificate authority file or directory.
+ "trust-anchor": "my-ca",
+
+ // Client certificate file name.
+ "cert-file": "my-cert",
+
+ // Private key file name.
+ "key-file": "my-key",
+
+ // Cipher list (see the OpenSSL ciphers command manual).
+ "cipher-list": "AES",
+
+ // Connection reconnect wait time.
+ // This parameter governs how long Kea waits before attempting
+ // to reconnect. Expressed in milliseconds. The default is 0
+ // (disabled) for MySQL and PostgreSQL.
+ "reconnect-wait-time": 3000,
+
+ // Connection maximum reconnect tries.
+ "max-reconnect-tries": 3,
+
+ // Action to take when connection recovery fails.
+ // Supported values: stop-retry-exit, serve-retry-exit,
+ // serve-retry-continue
+ "on-fail": "stop-retry-exit",
+
+ // Connection connect timeout in seconds.
+ "connect-timeout": 100,
+
+ // Timeout of database read operations in seconds.
+ "read-timeout": 120,
+
+ // Timeout of database write operations in seconds.
+ "write-timeout": 180
+ },
+ {
+ // Name of the database to connect to.
+ "name": "keatest",
+
+ // Host on which the database resides.
+ "host": "localhost",
+
+ // Database password.
+ "password": "keatest",
+
+ // Port on which the database is available.
+ "port": 5432,
+
+ // Type of database, e.g. "mysql", "postgresql".
+ "type": "postgresql",
+
+ // Username to be used to access the database.
+ "user": "keatest",
+
+ // TCP user timeout while communicating with the database.
+ // It is specified in seconds.
+ "tcp-user-timeout": 100
+ }
+ ],
+
+ // List of host reservation identifier types to be used by the
+ // Kea DHCPv4 server to fetch static reservations for
+ // DHCP clients. All identifiers are used by default, which
+ // means that the server will issue multiple queries to the
+ // database to find if there is a reservation for a particular
+ // client. If a particular deployment uses only a subset, e.g.
+ // one identifier type, this identifier should be only listed
+ // here to prevent unnecessary queries to the database.
+ "host-reservation-identifiers": [
+ "hw-address",
+ "duid",
+ "circuit-id",
+ "client-id",
+ "flex-id"
+ ],
+
+ // Specifies configuration of interfaces on which the Kea DHCPv4
+ // server is listening to the DHCP queries.
+ "interfaces-config": {
+ // Specifies whether the server should use "udp" sockets or
+ // "raw" sockets to listen to DHCP traffic. The "raw"
+ // sockets are useful when direct DHCP traffic is being
+ // received.
+ "dhcp-socket-type": "udp",
+
+ // Specifies a list of interfaces on which the Kea DHCPv4
+ // server should listen to DHCP requests.
+ "interfaces": [
+ "eth0"
+ ],
+
+ // Enumeration which indicates what interface should be used
+ // to send DHCP responses to the client. The default value is
+ // "same-as-inbound", which indicates that the response should
+ // be sent via the interface on which the client's query
+ // was received. The "use-routing" value indicates that the
+ // Kea server should use the kernel's routing table to find a
+ // suitable interface.
+ "outbound-interface": "same-as-inbound",
+
+ // Boolean flag indicating whether the available interfaces should
+ // be re-detected upon server reconfiguration. The default value
+ // is true, which means that the interfaces are always
+ // re-detected.
+ "re-detect": true,
+
+ // Kea tries to bind the service sockets during initialization, but it may
+ // fail due to a port being already opened or a misconfiguration. Kea can
+ // suppress these errors and only log them. This flag prevents starting
+ // the DHCP server without binding all sockets. If unspecified, it
+ // defaults to false.
+ "service-sockets-require-all": true,
+
+ // Kea tries to bind the service sockets during initialization. This
+ // option specifies how many times binding to interface will be retried.
+ // The default value is 0, which means that the operation will not be
+ // repeated.
+ "service-sockets-max-retries": 5,
+
+ // The time interval in milliseconds to wait before the next attempt to
+ // retry opening a service socket.
+ "service-sockets-retry-wait-time": 5000
+ },
+
+ // Boolean parameter which controls whether an early global host
+ // reservations lookup should be performed. This lookup takes place
+ // before subnet selection and when a global reservation is found
+ // with some client classes, it triggers a second phase classification.
+ // It can also be used to drop queries using host reservations as a
+ // decision table indexed by reservation identifiers.
+ "early-global-reservations-lookup": true,
+
+ // Boolean parameter which controls the DHCP server's behavior with respect
+ // to creating host reservations for the same IP address. By default
+ // this flag is set to true, in which case the server prevents creation
+ // of multiple host reservations for the same IP address. When this
+ // parameter is set to false, the server allows for creating multiple
+ // reservations for the same IP address within a subnet. This setting
+ // is useful in deployments in which a given host may be communicating
+ // with a DHCP server over multiple interfaces and, depending on the
+ // chosen interface, a different MAC address (or other identifier) will
+ // be used to identify the host. Note that some host backends do not
+ // support the mode in which multiple reservations for the same IP
+ // address are used. If these backends are in use and this setting
+ // is attempted, a configuration error will occur. The MySQL and
+ // PostgreSQL backends do support this mode.
+ "ip-reservations-unique": true,
+
+ // Boolean parameter which controls whether host reservations lookup
+ // should be performed before lease lookup. This parameter has effect
+ // only when multi-threading is disabled. When multi-threading is
+ // enabled, host reservations lookup is always performed first to avoid
+ // lease-lookup resource locking.
+ "reservations-lookup-first": true,
+
+ // Specifies credentials to access lease database.
+ "lease-database": {
+ // memfile backend-specific parameter specifying the interval
+ // in seconds at which the lease file should be cleaned up (outdated
+ // lease entries are removed to prevent the lease file from growing
+ // infinitely).
+ "lfc-interval": 3600,
+
+ // Maximum number of lease-file read errors allowed before
+ // loading the file is abandoned. Defaults to 0 (no limit).
+ "max-row-errors": 100,
+
+ // Name of the lease file. In the case of a database it specifies the
+ // database name.
+ "name": "/tmp/kea-dhcp4.csv",
+
+ // memfile-specific parameter indicating whether leases should
+ // be saved on persistent storage (disk) or not. The true value
+ // is the default and it indicates that leases are stored in
+ // persistent storage. This setting must be used in production.
+ // The false value should only be used for testing purposes
+ // because non-stored leases will be lost upon Kea server restart.
+ "persist": true,
+
+ // Lease database backend type, i.e. "memfile", "mysql" or
+ // "postgresql".
+ "type": "memfile"
+ },
+
+ // Boolean value indicating whether the Kea DHCPv4 server should use the client
+ // identifier value sent by the client or ignore it. The default value
+ // is true, which indicates that the server should use the client identifier
+ // and that it takes precedence over the client's MAC address. In deployments
+ // where MAC address should take precedence, this value can be set to
+ // false, in which case the clients will be identified by MAC address.
+ // This is specifically useful when clients don't generate unique
+ // identifiers or these identifiers are not stable, etc.
+ "match-client-id": false,
+
+ // Global value of the next server address set in 'siaddr' field.
+ // The global value may be overridden in lower-level configuration
+ // scopes.
+ "next-server": "192.0.2.123",
+
+ // Global value which limits the number of client packets (e.g.
+ // DHCPREQUESTs) that may be parked while waiting for hook library
+ // work to complete, prior to a response (e.g. DHCPACK) being sent
+ // back to the client. A typical example is when kea-dhcp4 parks a
+ // DHCPREQUEST while it sends the lease update(s) to its HA peer(s).
+ // The packet is unparked once the update(s) have been acknowledged.
+ // This value limits the number of packets that can be held pending
+ // the updates. In times of heavy client traffic, this value can keep
+ // kea-dhcp4 from building an insurmountable backlog of updates.
+ "parked-packet-limit": 128,
+
+ // List of global DHCP options that the Kea DHCPv4 server assigns to
+ // clients.
+ "option-data": [
+ {
+ // Boolean flag indicating whether the given option is always
+ // sent in response or only when requested. The default
+ // value of false indicates that it is only sent when
+ // requested.
+ "always-send": false,
+
+ // Option code. It is not required if the option name is
+ // provided.
+ "code": 6,
+
+ // Boolean value indicating whether the option data specified
+ // in the "data" field is specified as a string of hexadecimal
+ // digits or in human-readable CSV format.
+ "csv-format": true,
+
+ // Option data to be stored in the option payload.
+ "data": "192.0.3.1, 192.0.3.2",
+
+ // Option name. It is not required if the option code is
+ // provided.
+ "name": "domain-name-servers",
+
+ // Boolean flag indicating whether the given option is never
+ // sent in response. The default value of false indicates
+ // that it is sent when it should be. When true, the option
+ // is not sent despite any other setting, i.e. it is
+ // a final flag.
+ "never-send": false,
+
+ // Option space. The default is the "dhcp4" option space which
+ // groups top-level DHCPv4 options.
+ "space": "dhcp4"
+ }
+ ],
+
+ // List of global option definitions, i.e. option formats, that the
+ // Kea DHCPv4 server is using.
+ "option-def": [
+ {
+ // Boolean flag indicating whether the option definition comprises
+ // an array of values of some type, e.g. an array of IPv4 addresses.
+ // The default value of false means that the option does not
+ // comprise an array of values.
+ "array": false,
+
+ // Option code.
+ "code": 6,
+
+ // Holds a name of the option space encapsulated by this option.
+ // All options that belong to this option space will be sent
+ // as sub-options of this option. An empty string means that this
+ // option doesn't encapsulate any option.
+ "encapsulate": "",
+
+ // Option name.
+ "name": "my-option",
+
+ // Specifies the types of fields within the option if the option
+ // is said to be a "record" (see "type"). In this particular example
+ // this option comprises two fields, 1 byte and 2 bytes long.
+ "record-types": "uint8, uint16",
+
+ // Name of the option space to which this option belongs.
+ "space": "my-space",
+
+ // Option type. All possible types are listed in the Kea
+ // Administrator Reference Manual.
+ "type": "record"
+ }
+ ],
+
+ // Global value for the rebind timer, i.e. the time after which the
+ // DHCP client enters the rebind state if it fails to renew the lease.
+ "rebind-timer": 40,
+
+ // Global value for the renew timer, i.e. the time after which the
+ // DHCP client renews the lease.
+ "renew-timer": 30,
+
+ // Global value to store extended information (e.g. relay agent
+ // information) with each lease.
+ "store-extended-info": true,
+
+ // Statistics keep some samples per observation point.
+ // There are two default values: maximum count and maximum age.
+ // Setting the maximum count to zero disables it.
+ "statistic-default-sample-count": 0,
+
+ // When the maximum count is 0 the maximum age (in seconds) applies.
+ "statistic-default-sample-age": 60,
+
+ // Multi-threading parameters.
+ "multi-threading": {
+ // By default, Kea processes packets on multiple threads if the hardware permits.
+ "enable-multi-threading": true,
+
+ // When multi-threading is enabled, Kea will process packets on a
+ // number of multiple threads configurable through this option. The
+ // value must be a positive integer (0 means auto-detect).
+ "thread-pool-size": 0,
+
+ // When multi-threading is enabled, Kea will read packets from the
+ // interface and append a working item to the thread pool. This
+ // option configures the maximum number of items that can be queued.
+ // The value must be a positive integer (0 means unlimited).
+ "packet-queue-size": 0
+ },
+
+ // Governs how the Kea DHCPv4 server should deal with invalid
+ // data received from the client.
+ "sanity-checks": {
+ // Specifies how the Kea DHCPv4 server should behave when invalid
+ // data is read for a lease from the lease file. The following
+ // values are supported: "none" (don't attempt to correct the
+ // lease information), "warn" (print a warning for subnet-id
+ // related inconsistencies), "fix" (correct the subnet id by
+ // trying to find the suitable subnet), "fix-del" (similar
+ // to "fix" but delete the lease if no suitable subnet found),
+ // "del" (delete the lease if the lease has invalid subnet
+ // identifier value).
+ "lease-checks": "warn",
+
+ // Specifies how Kea DHCPv4 server should behave when invalid
+ // extended info is read for a lease from the lease file, or
+ // whether to upgrade from the old format. The following values
+ // are supported: "none" (don't attempt to correct or upgrade
+ // the extended info), "fix" (fix common inconsistencies and
+ // upgrade from the old format; this is the default), "strict"
+ // (fix inconsistencies with an impact on Leasequery),
+ // "pedantic" (enforce full Kea code format).
+ "extended-info-checks": "fix"
+ },
+
+ // List of shared networks used by the Kea DHCPv4 server. The shared
+ // networks group subnets together.
+ "shared-networks": [
+ {
+ // A flag selecting an IP address allocation strategy for all
+ // subnets in this shared network.
+ "allocator": "random",
+
+ // Shared-network level authoritative flag.
+ "authoritative": false,
+
+ // Shared-network level bootfile name.
+ "boot-file-name": "/dev/null",
+
+ // Restricts this shared network to allow only clients
+ // that belong to a particular client class. If an
+ // empty string is provided, no restriction is applied.
+ "client-class": "",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-generated-prefix": "myhost",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-override-client-update": false,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-override-no-update": false,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-qualifying-suffix": "",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-replace-client-name": "never",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-send-updates": true,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-update-on-renew": true,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-use-conflict-resolution": true,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-ttl-percent": 0.65,
+
+ // Shared-network level value. See description at the global level.
+ "hostname-char-replacement": "x",
+
+ // Shared-network level value. See description at the global level.
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+
+ // Specifies that this shared network is selected for
+ // requests received on a particular interface.
+ "interface": "eth0",
+
+ // Shared-network level flag specifying whether the client
+ // identifier should be used for identifying clients.
+ "match-client-id": true,
+
+ // Shared network name.
+ "name": "my-secret-network",
+
+ // Shared-network level specification of the next server
+ // to be sent in 'siaddr'.
+ "next-server": "192.0.2.123",
+
+ // If greater than zero, it is the lifetime of leases temporarily allocated
+ // on DISCOVER. When zero (the default), leases are not allocated on DISCOVER.
+ "offer-lifetime" : 60,
+
+ // List of shared network-specific DHCP options.
+ "option-data": [],
+
+ // List of IPv4 relay addresses for which this shared
+ // network is selected.
+ "relay": {
+ "ip-addresses": []
+ },
+
+ // Shared-network level rebind timer.
+ "rebind-timer": 41,
+
+ // Shared-network level renew timer.
+ "renew-timer": 31,
+
+ // Shared-network level compute T1 and T2 timers.
+ "calculate-tee-times": true,
+
+ // T1 = valid lifetime * .5.
+ "t1-percent": .5,
+
+ // T2 = valid lifetime * .75.
+ "t2-percent": .75,
+
+ // Cache threshold = valid lifetime * .25.
+ "cache-threshold": .25,
+
+ // Cache maximum: when the client last-transmission time
+ // is close enough, the lease is not renewed and the current
+ // lease is returned as it was "cached".
+ "cache-max-age": 1000,
+
+ // Enumeration specifying the server's mode of operation when it
+ // fetches host reservations.
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and "reservations-out-of-pool"
+ // parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ // If specified, it is inherited by "subnet4" levels.
+ "reservations-out-of-pool": false,
+
+ // List of client classes which must be evaluated when this shared
+ // network is selected for client assignments.
+ "require-client-classes": [ "late" ],
+
+ // Turn off storage of extended information (e.g. relay agent
+ // information) with each lease for this shared network.
+ "store-extended-info": false,
+
+ // Shared-network level server hostname set in 'sname' field.
+ "server-hostname": "",
+
+ // List of IPv4 subnets belonging to this shared network.
+ "subnet4": [
+ {
+ // Interface name matched against inbound interface name.
+ // Used in DHCPv4o6. See RFC 7341.
+ "4o6-interface": "",
+
+ // Interface ID option value. See RFC 7341.
+ "4o6-interface-id": "",
+
+ // Prefix matched against source address. See RFC7341.
+ "4o6-subnet": "2001:db8:1:1::/64",
+
+ // A flag selecting an IP address allocation strategy for
+ // the subnet.
+ "allocator": "iterative",
+
+ // Subnet-level authoritative flag.
+ "authoritative": false,
+
+ // Subnet-level bootfile name, set in 'file' field.
+ "boot-file-name": "",
+
+ // Restricts this subnet to allow only clients that belong
+ // to a particular client class. If an empty string is
+ // provided, no restriction is applied.
+ "client-class": "",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-generated-prefix": "myhost",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-override-client-update": false,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-override-no-update": false,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-qualifying-suffix": "",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-replace-client-name": "never",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-send-updates": true,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-update-on-renew": true,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-use-conflict-resolution": true,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-ttl-percent": 0.55,
+
+ // Subnet-level value. See description at the global level.
+ "hostname-char-replacement": "x",
+
+ // Subnet-level value. See description at the global level.
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+
+ // Subnet unique identifier.
+ "id": 1,
+
+ // Specifies that this subnet is selected for requests
+ // received on a particular interface.
+ "interface": "eth0",
+
+ // Subnet-level flag specifying whether the client identifier
+ // should be used for identifying clients.
+ "match-client-id": true,
+
+ // Subnet-level specification of the next server to be sent
+ // in 'siaddr'.
+ "next-server": "0.0.0.0",
+
+ // If greater than zero, it is the lifetime of leases temporarily allocated
+ // on DISCOVER. When zero (the default), leases are not allocated on DISCOVER.
+ "offer-lifetime" : 60,
+
+ // Turn on storage of extended information (e.g. relay agent
+ // information) with each lease for this subnet.
+ "store-extended-info": true,
+
+ // Subnet-level list of DHCP options.
+ "option-data": [
+ {
+ // Boolean flag indicating whether the particular option
+ // should be always sent or sent only when requested.
+ "always-send": false,
+
+ // Option code.
+ "code": 3,
+
+ // Boolean flag indicating whether the option value specified
+ // in "data" is a string of hexadecimal values or human-readable
+ // CSV value.
+ "csv-format": true,
+
+ // Option data to be included in the option payload.
+ "data": "192.0.3.1",
+
+ // Option name.
+ "name": "routers",
+
+ // Boolean flag indicating whether the given option is never
+ // sent in response.
+ "never-send": false,
+
+ // Option space. The default value "dhcp4" designates the
+ // top-level option space.
+ "space": "dhcp4"
+ }
+ ],
+
+ // List of IP address pools belonging to the subnet.
+ "pools": [
+ {
+ // Restricts this pool to only be used for client
+ // requests belonging to a particular client class.
+ "client-class": "phones_server1",
+
+ // Pool-level list of DHCP options.
+ "option-data": [],
+
+ // Address range used for client assignments.
+ "pool": "192.1.0.1 - 192.1.0.200",
+
+ // List of client classes which must be evaluated when this pool
+ // is selected for client assignments.
+ "require-client-classes": [ "late" ]
+ },
+ {
+ // Restricts this pool to only be used for client
+ // requests belonging to a particular client class.
+ "client-class": "phones_server2",
+
+ // Pool-level list of DHCP options.
+ "option-data": [],
+
+ // Address range used for client assignments.
+ "pool": "192.3.0.1 - 192.3.0.200",
+
+ // List of client classes which must be evaluated when this pool
+ // is selected for client assignments.
+ "require-client-classes": [],
+
+ // Pool identifier used to enable statistics for this pool.
+ // The pool ID does not need to be unique within the subnet
+ // or across subnets.
+ // If not unconfigured, it defaults to 0. The statistics
+ // regarding this pool will be combined with the other statistics
+ // of all other pools with the same pool ID in this subnet.
+ "pool-id": 1
+ }
+ ],
+
+ // Subnet-level value of the rebind timer.
+ "rebind-timer": 40,
+
+ // List of IPv4 relay addresses for which this subnet is selected.
+ "relay": {
+ "ip-addresses": [
+ "192.168.56.1"
+ ]
+ },
+
+ // Subnet-level value of the renew timer.
+ "renew-timer": 30,
+
+ // Enumeration specifying the server's mode of operation when it
+ // fetches host reservations.
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and
+ // "reservations-out-of-pool" parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved
+ // addresses are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ "reservations-out-of-pool": false,
+
+ // Subnet-level compute T1 and T2 timers.
+ "calculate-tee-times": true,
+
+ // T1 = valid lifetime * .5.
+ "t1-percent": .5,
+
+ // T2 = valid lifetime * .75.
+ "t2-percent": .75,
+
+ // Cache threshold = valid lifetime * .25.
+ "cache-threshold": .25,
+
+ // Subnet-level cache maximum.
+ "cache-max-age": 1000,
+
+ // List of static IPv4 reservations assigned to clients belonging
+ // to this subnet. For a detailed example, see reservations.json.
+ "reservations": [
+ {
+ // Identifier used for client matching. Supported values are
+ // "hw-address", "client-id", "duid", "circuit-id", "flex-id".
+ "circuit-id": "01:11:22:33:44:55:66",
+
+ // Reserved IP address.
+ "ip-address": "192.0.2.204",
+
+ // Hostname.
+ "hostname": "foo.example.org",
+
+ // Reservation-specific option data.
+ "option-data": [
+ {
+ // Option name.
+ "name": "vivso-suboptions",
+
+ // Option data.
+ "data": "4491"
+ }
+ ]
+ }
+ ],
+
+ // List of client classes which must be evaluated when this subnet
+ // is selected for client assignments.
+ "require-client-classes": [ "late" ],
+
+ // Subnet-level server hostname set in 'sname' field.
+ "server-hostname": "",
+
+ // Subnet prefix.
+ "subnet": "192.0.0.0/8",
+
+ // Subnet-level (default) valid lifetime.
+ "valid-lifetime": 6000,
+
+ // Subnet-level min valid lifetime.
+ "min-valid-lifetime": 4000,
+
+ // Subnet-level max valid lifetime.
+ "max-valid-lifetime": 8000
+ }
+ ],
+
+ // Shared-network level (default) valid lifetime.
+ "valid-lifetime": 6001,
+
+ // Shared-network level min valid lifetime.
+ "min-valid-lifetime": 4001,
+
+ // Shared-network level max valid lifetime.
+ "max-valid-lifetime": 8001
+ }
+ ],
+
+ // Global server hostname set in the 'sname' field.
+ "server-hostname": "",
+
+ // List of IPv4 subnets which don't belong to any shared network.
+ "subnet4": [],
+
+ // Global valid lifetime value.
+ "valid-lifetime": 6000,
+
+ // Global min valid lifetime value.
+ "min-valid-lifetime": 4000,
+
+ // Global max valid lifetime value.
+ "max-valid-lifetime": 8000,
+
+ // Reservations (examples are in other files).
+ "reservations": [],
+
+ // Configuration control (currently not used, i.e. this syntax
+ // is already defined but the corresponding feature is not implemented).
+ "config-control": {
+ // Only the configuration databases entry is defined.
+ "config-databases": [
+ {
+ // Name of the database to connect to.
+ "name": "config",
+
+ // Type of database, e.g. "mysql", "postgresql".
+ "type": "mysql"
+ }
+ ],
+ // Interval between attempts to fetch configuration updates
+ // via the configuration backends used.
+ "config-fetch-wait-time": 30
+ },
+
+ // Server tag.
+ "server-tag": "my DHCPv4 server",
+
+ // DHCP queue-control parameters.
+ "dhcp-queue-control": {
+ // Enable queue is mandatory.
+ "enable-queue": true,
+
+ // Queue type is mandatory.
+ "queue-type": "kea-ring4",
+
+ // Capacity is optional.
+ "capacity": 64
+ },
+
+ // Fetches host reservations.
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and "reservations-out-of-pool" parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ // If specified, it is inherited by "shared-networks" and
+ // "subnet4" levels.
+ "reservations-out-of-pool": false,
+
+ // Global compute T1 and T2 timers.
+ "calculate-tee-times": true,
+
+ // T1 = valid lifetime * .5.
+ "t1-percent": .5,
+
+ // T2 = valid lifetime * .75.
+ "t2-percent": .75,
+
+ // Cache threshold = valid lifetime * .25.
+ "cache-threshold": .25,
+
+ // Global cache maximum.
+ "cache-max-age": 1000,
+
+ // String of zero or more characters with which to replace each
+ // invalid character in the hostname or Client FQDN. The default
+ // value is an empty string, which will cause invalid characters
+ // to be omitted rather than replaced.
+ "hostname-char-replacement": "x",
+
+ // Regular expression describing the invalid character set in
+ // the hostname or Client FQDN.
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+
+ // List of loggers used by the servers using this configuration file.
+ "loggers": [
+ {
+ // Debug level, a value between 0..99. The greater the value
+ // the more detailed the debug log.
+ "debuglevel": 99,
+
+ // Name of the logger.
+ "name": "kea-dhcp4",
+
+ // Configures how the log should be output.
+ "output-options": [
+ {
+ // Determines whether the log should be flushed to a file.
+ "flush": true,
+
+ // Specifies maximum filesize before the file is rotated.
+ "maxsize": 10240000,
+
+ // Specifies the maximum number of rotated files to be kept.
+ "maxver": 1,
+
+ // Specifies the logging destination.
+ "output": "stdout",
+
+ // Specifies log entry content
+ "pattern": "%D{%Y-%m-%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+ }
+ ],
+
+ // Specifies logging severity, i.e. "ERROR", "WARN", "INFO", "DEBUG".
+ "severity": "INFO"
+ }
+ ],
+
+ // If greater than zero, it is the lifetime of leases temporarily allocated
+ // on DISCOVER. When zero (the default), leases are not allocated on DISCOVER.
+ "offer-lifetime" : 60,
+
+ // Look at advanced examples for the use of user-contexts.
+ "user-context": { }
+ }
+}
diff --git a/doc/examples/kea4/all-keys.json b/doc/examples/kea4/all-keys.json
new file mode 100644
index 0000000..d5e0a02
--- /dev/null
+++ b/doc/examples/kea4/all-keys.json
@@ -0,0 +1,1277 @@
+// WARNING: This example configuration is not meant for production use.
+// The Kea DHCPv4 server will refuse this configuration because it contains
+// mutually exclusive configuration parameters.
+//
+// The primary purpose of the example file is to provide a comprehensive
+// list of parameters supported by the Kea DHCPv4 server, along with a brief
+// description of each parameter.
+//
+// This current version should be up to date, i.e. new keywords should be
+// added in this file at the same time as in the parser specification.
+{
+ // Kea DHCPv4 server configuration begins here.
+ "Dhcp4": {
+ // Global flag selecting an IP address allocation strategy for all
+ // subnets. Use "random" for a random allocation strategy.
+ "allocator": "iterative",
+
+ // Global authoritative flag to handle requests by clients for
+ // unknown IP addresses (ignore if disabled, NAK if enabled).
+ "authoritative": false,
+
+ // Global bootfile name to be set in the 'file' field.
+ "boot-file-name": "/dev/null",
+
+ // Ordered list of client classes used by the DHCPv4 server.
+ "client-classes": [
+ {
+ // Class-specific bootfile name to be set in the 'file' field.
+ "boot-file-name": "/tmp/bootfile.efi",
+
+ // Class name.
+ "name": "phones_server1",
+
+ // Class-specific next server address to use in bootstrap, which
+ // is set in 'siaddr' field.
+ "next-server": "10.2.3.4",
+
+ // Class-specific DHCPv4 options list.
+ "option-data": [],
+
+ // Class-specific DHCPv4 option definitions, i.e. custom formats
+ // specified for non-standard options.
+ "option-def": [],
+
+ // Class-specific optional server hostname, which is set in
+ // 'sname' field.
+ "server-hostname": "",
+
+ // Class selection expression. The DHCP packet is assigned to this
+ // class when the given expression evaluates to true.
+ "test": "member('HA_server1')",
+
+ // Class valid lifetime.
+ "valid-lifetime": 6000,
+
+ // Class min valid lifetime.
+ "min-valid-lifetime": 4000,
+
+ // Class max valid lifetime.
+ "max-valid-lifetime": 8000,
+
+ // If greater than zero, it is the lifetime of leases temporarily allocated
+ // on DISCOVER. When zero (the default), leases are not allocated on DISCOVER.
+ "offer-lifetime" : 65
+ },
+ {
+ // Default value of the class-specific bootfile name. An empty name
+ // means that the bootfile name is unspecified.
+ "boot-file-name": "",
+
+ // Second class name.
+ "name": "phones_server2",
+
+ // Default value of the class-specific next server address. The
+ // zero IPv4 address means that it is unspecified.
+ "next-server": "0.0.0.0",
+
+ // Class-specific DHCPv4 options list.
+ "option-data": [],
+
+ // Class-specific DHCPv4 option definitions, i.e. custom formats
+ // specified for non-standard options.
+ "option-def": [],
+
+ // Class-specific optional server hostname, which is set in
+ // 'sname' field.
+ "server-hostname": "",
+
+ // Class selection expression. The DHCP packet is assigned to this
+ // class when the given expression evaluates to true.
+ "test": "member('HA_server2')"
+ },
+ {
+ // Third class name.
+ "name": "late",
+
+ // Boolean flag indicating whether the class expression is only evaluated
+ // when the class is required, e.g. the selected address pool configuration
+ // includes this class name in its "require-client-classes" list. The
+ // default value false means that the class test expression must
+ // always be evaluated.
+ "only-if-required": true,
+
+ // Class selection expression.
+ "test": "member('ALL')"
+ },
+ {
+ // Fourth class name.
+ "name": "my-template-class",
+
+ // Template class flag that holds the expression used to generate the names for all
+ // the spawned subclasses. In this case, the classes are named after the client ID.
+ "template-test": "substring(option[61].hex, 0, all)"
+ }
+ ],
+
+ // Parameters for triggering behaviors compatible with broken or
+ // non-compliant clients, relays, or other agents
+ "compatibility": {
+ // Ignore DHCP Server Identifier option if set to true.
+ // Enabling this will cause Kea to accept any query, even
+ // if the address in the option belongs to another server,
+ // instead of dropping it. This config option defaults to
+ // false, as enabling it breaks RFC compliance.
+ "ignore-dhcp-server-identifier": false,
+
+ // Ignore Relay Agent Information Link Selection suboption if set
+ // to true. Enabling this will cause Kea to use normal subnet
+ // selection logic instead of attempting to use the subnet
+ // specified in the suboption. This config option defaults to
+ // false, as enabling it breaks RFC compliance.
+ "ignore-rai-link-selection": false,
+
+ // Parse options more leniently where fields can be deduced
+ // deterministically, even if against RFC or common practice.
+ "lenient-option-parsing": true,
+
+ // Boolean flag indicating whether .0 and .255 addresses
+ // must be considered as never free in subnets with a prefix length
+ // of 24 or less. The default is false, as these addresses are not
+ // special; only the first and the last addresses are.
+ "exclude-first-last-24": false
+ },
+
+ // Command control socket configuration parameters for the Kea DHCPv4 server.
+ "control-socket": {
+ // Location of the UNIX domain socket file the DHCPv4 server uses
+ // to receive control commands from the Kea Control Agent or the
+ // local server administrator.
+ "socket-name": "/tmp/kea4-ctrl-socket",
+
+ // Control socket type used by the Kea DHCPv4 server. The 'unix'
+ // socket is currently the only supported type.
+ "socket-type": "unix"
+ },
+
+ // Specifies a prefix to be prepended to the generated Client FQDN.
+ // It may be specified at the global, shared-network, and subnet levels.
+ "ddns-generated-prefix": "myhost",
+
+ // Boolean flag indicating whether the server should ignore DHCP client
+ // wishes to update DNS on its own. With that flag set to true,
+ // the server will send DNS updates for both forward and
+ // reverse DNS data. The default value is false, which indicates
+ // that the server will delegate a DNS update to the client when
+ // requested. It may be specified at the global, shared-network,
+ // and subnet levels.
+ "ddns-override-client-update": false,
+
+ // Boolean flag indicating whether the server should override the DHCP
+ // client's wish to not update the DNS. With this parameter
+ // set to true, the server will send a DNS update even when
+ // the client requested no update. It may be specified at the
+ // global, shared-network, and subnet levels.
+ "ddns-override-no-update": false,
+
+ // Suffix appended to the partial name sent to the DNS. The
+ // default value is an empty string, which indicates that no
+ // suffix is appended. It may be specified at the global,
+ // shared-network, and subnet levels.
+ "ddns-qualifying-suffix": "",
+
+ // Enumeration specifying whether the server should honor
+ // the hostname or Client FQDN sent by the client or replace
+ // this name. The acceptable values are: "never" (use the
+ // name the client sent), "always" (replace the name the
+ // client sent), "when-present" (replace the name the client
+ // sent, but do not generate one when the client didn't send
+ // the name), "when-not-present" (generate the name when
+ // client didn't send one, otherwise leave the name the
+ // client sent). The default value is "never". It may be
+ // specified at the global, shared-network, and subnet levels.
+ "ddns-replace-client-name": "never",
+
+ // Boolean flag which enables or disables DDNS updating. It
+ // defaults to true. It may be specified at the global, shared-
+ // network, and subnet levels. It works in conjunction with
+ // dhcp-ddns:enable-updates, which must be true to enable connectivity
+ // to kea-dhcp-ddns.
+ "ddns-send-updates": true,
+
+ // Boolean flag, which when true instructs the server to always
+ // update DNS when leases are renewed, even if the DNS information
+ // has not changed. The server's default behavior (i.e. flag is false)
+ // is to only update DNS if the DNS information has changed. It
+ // may be specified at the global, shared-network, and subnet levels.
+ "ddns-update-on-renew": true,
+
+ // Boolean flag which is passed to kea-dhcp-ddns with each DDNS
+ // update request, to indicate whether DNS update conflict
+ // resolution as described in RFC 4703 should be employed for the
+ // given update request. The default value for this flag is true.
+ // It may be specified at the global, shared-network, and subnet levels.
+ // This field has been replaced by ddns-conflict-resolution-mode.
+ // Parsing is maintained only for backwards compatibility.
+ // "ddns-use-conflict-resolution": true,
+
+ // Enumeration, which is passed to kea-dhcp-ddns with each DDNS
+ // update request to indicate the mode used for resolving conflicts
+ // while performing DDNS updates. The acceptable values are:
+ // check-with-dhcid (this includes adding a DHCID record and checking
+ // that record via conflict detection as per RFC 4703,
+ // no-check-with-dhcid (this will ignore conflict detection but add
+ // a DHCID record when creating/updating an entry),
+ // check-exists-with-dhcid (this will check if there is an existing
+ // DHCID record but does not verify the value of the record matches
+ // the update. This will also update the DHCID record for the entry),
+ // no-check-without-dhcid (this ignores conflict detection and will
+ // not add a DHCID record when creating/updating a DDNS entry).
+ // The default value is "check-with-dhcid". It may be
+ // specified at the global, shared-network and subnet levels.
+ "ddns-conflict-resolution-mode": "check-with-dhcid",
+
+ // When greater than 0.0, it is the percent of the lease's lifetime
+ // to use for the DNS TTL.
+ "ddns-ttl-percent": 0.75,
+
+ // Time in seconds specifying how long a declined lease should be
+ // excluded from DHCP assignments. The default value is 86400 (24 hours).
+ "decline-probation-period": 86400,
+
+ // Name Change Request forwarding configuration for the Kea DHCPv4 server.
+ // NCRs are sent to the Kea D2 module to update DNS upon allocation of
+ // DHCP leases.
+ "dhcp-ddns": {
+ // Boolean flag indicating whether Kea DHCPv4 server should connect to
+ // kea-dhcp-ddns. This must be true for NCRs to be created and
+ // sent to kea-dhcp-ddns. By default, NCRs are not generated.
+ "enable-updates": false,
+
+ // Specifies maximum number of NCRs to queue waiting to be sent
+ // to the Kea D2 server.
+ "max-queue-size": 1024,
+
+ // Packet format to use when sending NCRs to the Kea D2 server.
+ // Currently, only JSON format is supported.
+ "ncr-format": "JSON",
+
+ // Socket protocol to use when sending NCRs to D2. Currently,
+ // only UDP is supported.
+ "ncr-protocol": "UDP",
+
+ // IP address that the Kea DHCPv4 server should use to send
+ // NCRs to D2. The default value of zero indicates that Kea
+ // should pick a suitable address.
+ "sender-ip": "0.0.0.0",
+
+ // Port number that the Kea DHCPv4 server should use to send
+ // NCRs to D2. The default value of zero indicates that Kea
+ // should pick a suitable port.
+ "sender-port": 0,
+
+ // IP address on which D2 listens for NCRs.
+ "server-ip": "127.0.0.1",
+
+ // Port number on which D2 listens for NCRs.
+ "server-port": 53001,
+
+ // The following parameters are DEPRECATED. They have been
+ // replaced with parameters that may be set at the global,
+ // shared-network, and subnet4 scopes. They are listed here
+ // as configuration parsing still accepts them. Eventually
+ // support for them will be removed.
+ "generated-prefix": "myhost",
+ "hostname-char-replacement": "x",
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+ "override-client-update": false,
+ "override-no-update": false,
+ "qualifying-suffix": "",
+ "replace-client-name": "never"
+ },
+
+ // Specifies the first of the two consecutive ports of the UDP
+ // sockets used for communication between DHCPv6 and DHCPv4
+ // servers. See RFC 7341.
+ "dhcp4o6-port": 6767,
+
+ // Boolean flag indicating whether the Kea DHCPv4 server
+ // should send back the Client Identifier option in its responses.
+ // The default value is true, which indicates that the option
+ // must be sent back if the client included it. The false
+ // value instructs the server to not send this option for
+ // backward compatibility with older DHCP specifications, which
+ // stated that Client Identifier must not be sent back.
+ "echo-client-id": true,
+
+ // Collection of Kea DHCPv4 server parameters configuring how
+ // the server should process expired DHCP leases.
+ "expired-leases-processing": {
+ // Specifies the number of seconds since the last removal of
+ // the expired leases, when the next removal should occur.
+ // If both "flush-reclaimed-timer-wait-time" and
+ // "hold-reclaimed-time" are not 0, when the client sends a release
+ // message the lease is expired instead of being deleted from
+ // lease storage.
+ "flush-reclaimed-timer-wait-time": 25,
+
+ // Specifies the length of time in seconds to keep expired
+ // leases in the lease database (lease affinity).
+ // If both "flush-reclaimed-timer-wait-time" and
+ // "hold-reclaimed-time" are not 0, when the client sends a release
+ // message the lease is expired instead of being deleted from
+ // lease storage.
+ "hold-reclaimed-time": 3600,
+
+ // Specifies the maximum number of expired leases that can be
+ // processed in a single attempt to clean up expired leases
+ // from the lease database. If there are more
+ // expired leases, they will be processed during the next
+ // cleanup attempt.
+ "max-reclaim-leases": 100,
+
+ // Specifies the maximum time in milliseconds that a single attempt
+ // to clean up expired leases from the lease database may take.
+ "max-reclaim-time": 250,
+
+ // Specifies the length of time in seconds since the last attempt
+ // to process expired leases before initiating the next attempt.
+ "reclaim-timer-wait-time": 10,
+
+ // Specifies the maximum number of expired lease-processing cycles
+ // which didn't result in full cleanup of exired leases from the
+ // lease database, after which a warning message is issued.
+ "unwarned-reclaim-cycles": 5
+ },
+
+ // List of hook libraries and their specific configuration parameters
+ // to be loaded by Kea DHCPv4 server.
+ "hooks-libraries": [
+ {
+ // Location of the hook library to be loaded.
+ "library": "/opt/lib/kea/hooks/libdhcp_lease_cmds.so",
+
+ // Hook library-specific configuration parameters.
+ "parameters": { }
+ }
+ ],
+
+ // List of access credentials to external sources of IPv4 reservations,
+ "hosts-databases": [
+ {
+ // Name of the database to connect to.
+ "name": "keatest",
+
+ // Host on which the database resides.
+ "host": "localhost",
+
+ // Database password.
+ "password": "keatest",
+
+ // Port on which the database is available.
+ "port": 3306,
+
+ // Type of database, e.g. "mysql", "postgresql".
+ "type": "mysql",
+
+ // Username to be used to access the database.
+ "user": "keatest",
+
+ // Read-only mode.
+ "readonly": false,
+
+ // The next entries are for OpenSSL support in MySQL.
+
+ // Trust anchor aka certificate authority file or directory.
+ "trust-anchor": "my-ca",
+
+ // Client certificate file name.
+ "cert-file": "my-cert",
+
+ // Private key file name.
+ "key-file": "my-key",
+
+ // Cipher list (see the OpenSSL ciphers command manual).
+ "cipher-list": "AES",
+
+ // Connection reconnect wait time.
+ // This parameter governs how long Kea waits before attempting
+ // to reconnect. Expressed in milliseconds. The default is 0
+ // (disabled) for MySQL and PostgreSQL.
+ "reconnect-wait-time": 3000,
+
+ // Connection maximum reconnect tries.
+ "max-reconnect-tries": 3,
+
+ // Action to take when connection recovery fails.
+ // Supported values: stop-retry-exit, serve-retry-exit,
+ // serve-retry-continue
+ "on-fail": "stop-retry-exit",
+
+ // Flag which indicates if the DB recovery should be attempted
+ // at server startup and on reconfiguration events.
+ "retry-on-startup": false,
+
+ // Connection connect timeout in seconds.
+ "connect-timeout": 100,
+
+ // Timeout of database read operations in seconds.
+ "read-timeout": 120,
+
+ // Timeout of database write operations in seconds.
+ "write-timeout": 180
+ },
+ {
+ // Name of the database to connect to.
+ "name": "keatest",
+
+ // Host on which the database resides.
+ "host": "localhost",
+
+ // Database password.
+ "password": "keatest",
+
+ // Port on which the database is available.
+ "port": 5432,
+
+ // Type of database, e.g. "mysql", "postgresql".
+ "type": "postgresql",
+
+ // Username to be used to access the database.
+ "user": "keatest",
+
+ // TCP user timeout while communicating with the database.
+ // It is specified in seconds.
+ "tcp-user-timeout": 100
+ }
+ ],
+
+ // List of host reservation identifier types to be used by the
+ // Kea DHCPv4 server to fetch static reservations for
+ // DHCP clients. All identifiers are used by default, which
+ // means that the server will issue multiple queries to the
+ // database to find if there is a reservation for a particular
+ // client. If a particular deployment uses only a subset, e.g.
+ // one identifier type, this identifier should be only listed
+ // here to prevent unnecessary queries to the database.
+ "host-reservation-identifiers": [
+ "hw-address",
+ "duid",
+ "circuit-id",
+ "client-id",
+ "flex-id"
+ ],
+
+ // Specifies configuration of interfaces on which the Kea DHCPv4
+ // server is listening to the DHCP queries.
+ "interfaces-config": {
+ // Specifies whether the server should use "udp" sockets or
+ // "raw" sockets to listen to DHCP traffic. The "raw"
+ // sockets are useful when direct DHCP traffic is being
+ // received.
+ "dhcp-socket-type": "udp",
+
+ // Specifies a list of interfaces on which the Kea DHCPv4
+ // server should listen to DHCP requests.
+ "interfaces": [
+ "eth0"
+ ],
+
+ // Enumeration which indicates what interface should be used
+ // to send DHCP responses to the client. The default value is
+ // "same-as-inbound", which indicates that the response should
+ // be sent via the interface on which the client's query
+ // was received. The "use-routing" value indicates that the
+ // Kea server should use the kernel's routing table to find a
+ // suitable interface.
+ "outbound-interface": "same-as-inbound",
+
+ // Boolean flag indicating whether the available interfaces should
+ // be re-detected upon server reconfiguration. The default value
+ // is true, which means that the interfaces are always
+ // re-detected.
+ "re-detect": true,
+
+ // Kea tries to bind the service sockets during initialization, but it may
+ // fail due to a port being already opened or a misconfiguration. Kea can
+ // suppress these errors and only log them. This flag prevents starting
+ // the DHCP server without binding all sockets. If unspecified, it
+ // defaults to false.
+ "service-sockets-require-all": true,
+
+ // Kea tries to bind the service sockets during initialization. This
+ // option specifies how many times binding to interface will be retried.
+ // The default value is 0, which means that the operation will not be
+ // repeated.
+ "service-sockets-max-retries": 5,
+
+ // The time interval in milliseconds to wait before the next attempt to
+ // retry opening a service socket.
+ "service-sockets-retry-wait-time": 5000
+ },
+
+ // Boolean parameter which controls whether an early global host
+ // reservations lookup should be performed. This lookup takes place
+ // before subnet selection and when a global reservation is found
+ // with some client classes, it triggers a second phase classification.
+ // It can also be used to drop queries using host reservations as a
+ // decision table indexed by reservation identifiers.
+ "early-global-reservations-lookup": true,
+
+ // Boolean parameter which controls the DHCP server's behavior with respect
+ // to creating host reservations for the same IP address. By default
+ // this flag is set to true, in which case the server prevents creation
+ // of multiple host reservations for the same IP address. When this
+ // parameter is set to false, the server allows for creating multiple
+ // reservations for the same IP address within a subnet. This setting
+ // is useful in deployments in which a given host may be communicating
+ // with a DHCP server over multiple interfaces and, depending on the
+ // chosen interface, a different MAC address (or other identifier) will
+ // be used to identify the host. Note that some host backends do not
+ // support the mode in which multiple reservations for the same IP
+ // address are used. If these backends are in use and this setting
+ // is attempted, a configuration error will occur. The MySQL and
+ // PostgreSQL backends do support this mode.
+ "ip-reservations-unique": true,
+
+ // Boolean parameter which controls whether host reservations lookup
+ // should be performed before lease lookup. This parameter has effect
+ // only when multi-threading is disabled. When multi-threading is
+ // enabled, host reservations lookup is always performed first to avoid
+ // lease-lookup resource locking.
+ "reservations-lookup-first": true,
+
+ // Specifies credentials to access lease database.
+ "lease-database": {
+ // memfile backend-specific parameter specifying the interval
+ // in seconds at which the lease file should be cleaned up (outdated
+ // lease entries are removed to prevent the lease file from growing
+ // infinitely).
+ "lfc-interval": 3600,
+
+ // Maximum number of lease-file read errors allowed before
+ // loading the file is abandoned. Defaults to 0 (no limit).
+ "max-row-errors": 100,
+
+ // Name of the lease file. In the case of a database it specifies the
+ // database name.
+ "name": "/tmp/kea-dhcp4.csv",
+
+ // memfile-specific parameter indicating whether leases should
+ // be saved on persistent storage (disk) or not. The true value
+ // is the default and it indicates that leases are stored in
+ // persistent storage. This setting must be used in production.
+ // The false value should only be used for testing purposes
+ // because non-stored leases will be lost upon Kea server restart.
+ "persist": true,
+
+ // Lease database backend type, i.e. "memfile", "mysql" or
+ // "postgresql".
+ "type": "memfile"
+ },
+
+ // Boolean value indicating whether the Kea DHCPv4 server should use the client
+ // identifier value sent by the client or ignore it. The default value
+ // is true, which indicates that the server should use the client identifier
+ // and that it takes precedence over the client's MAC address. In deployments
+ // where MAC address should take precedence, this value can be set to
+ // false, in which case the clients will be identified by MAC address.
+ // This is specifically useful when clients don't generate unique
+ // identifiers or these identifiers are not stable, etc.
+ "match-client-id": false,
+
+ // Global value of the next server address set in 'siaddr' field.
+ // The global value may be overridden in lower-level configuration
+ // scopes.
+ "next-server": "192.0.2.123",
+
+ // Global value which limits the number of client packets (e.g.
+ // DHCPREQUESTs) that may be parked while waiting for hook library
+ // work to complete, prior to a response (e.g. DHCPACK) being sent
+ // back to the client. A typical example is when kea-dhcp4 parks a
+ // DHCPREQUEST while it sends the lease update(s) to its HA peer(s).
+ // The packet is unparked once the update(s) have been acknowledged.
+ // This value limits the number of packets that can be held pending
+ // the updates. In times of heavy client traffic, this value can keep
+ // kea-dhcp4 from building an insurmountable backlog of updates.
+ "parked-packet-limit": 128,
+
+ // List of global DHCP options that the Kea DHCPv4 server assigns to
+ // clients.
+ "option-data": [
+ {
+ // Boolean flag indicating whether the given option is always
+ // sent in response or only when requested. The default
+ // value of false indicates that it is only sent when
+ // requested.
+ "always-send": false,
+
+ // Option code. It is not required if the option name is
+ // provided.
+ "code": 6,
+
+ // Boolean value indicating whether the option data specified
+ // in the "data" field is specified as a string of hexadecimal
+ // digits or in human-readable CSV format.
+ "csv-format": true,
+
+ // Option data to be stored in the option payload.
+ "data": "192.0.3.1, 192.0.3.2",
+
+ // Option name. It is not required if the option code is
+ // provided.
+ "name": "domain-name-servers",
+
+ // Boolean flag indicating whether the given option is never
+ // sent in response. The default value of false indicates
+ // that it is sent when it should be. When true, the option
+ // is not sent despite any other setting, i.e. it is
+ // a final flag.
+ "never-send": false,
+
+ // Option space. The default is the "dhcp4" option space which
+ // groups top-level DHCPv4 options.
+ "space": "dhcp4"
+ }
+ ],
+
+ // List of global option definitions, i.e. option formats, that the
+ // Kea DHCPv4 server is using.
+ "option-def": [
+ {
+ // Boolean flag indicating whether the option definition comprises
+ // an array of values of some type, e.g. an array of IPv4 addresses.
+ // The default value of false means that the option does not
+ // comprise an array of values.
+ "array": false,
+
+ // Option code.
+ "code": 6,
+
+ // Holds a name of the option space encapsulated by this option.
+ // All options that belong to this option space will be sent
+ // as sub-options of this option. An empty string means that this
+ // option doesn't encapsulate any option.
+ "encapsulate": "",
+
+ // Option name.
+ "name": "my-option",
+
+ // Specifies the types of fields within the option if the option
+ // is said to be a "record" (see "type"). In this particular example
+ // this option comprises two fields, 1 byte and 2 bytes long.
+ "record-types": "uint8, uint16",
+
+ // Name of the option space to which this option belongs.
+ "space": "my-space",
+
+ // Option type. All possible types are listed in the Kea
+ // Administrator Reference Manual.
+ "type": "record"
+ }
+ ],
+
+ // Global value for the rebind timer, i.e. the time after which the
+ // DHCP client enters the rebind state if it fails to renew the lease.
+ "rebind-timer": 40,
+
+ // Global value for the renew timer, i.e. the time after which the
+ // DHCP client renews the lease.
+ "renew-timer": 30,
+
+ // Global value to store extended information (e.g. relay agent
+ // information) with each lease.
+ "store-extended-info": true,
+
+ // Statistics keep some samples per observation point.
+ // There are two default values: maximum count and maximum age.
+ // Setting the maximum count to zero disables it.
+ "statistic-default-sample-count": 0,
+
+ // When the maximum count is 0 the maximum age (in seconds) applies.
+ "statistic-default-sample-age": 60,
+
+ // Multi-threading parameters.
+ "multi-threading": {
+ // By default, Kea processes packets on multiple threads if the hardware permits.
+ "enable-multi-threading": true,
+
+ // When multi-threading is enabled, Kea will process packets on a
+ // number of multiple threads configurable through this option. The
+ // value must be a positive integer (0 means auto-detect).
+ "thread-pool-size": 0,
+
+ // When multi-threading is enabled, Kea will read packets from the
+ // interface and append a working item to the thread pool. This
+ // option configures the maximum number of items that can be queued.
+ // The value must be a positive integer (0 means unlimited).
+ "packet-queue-size": 0
+ },
+
+ // Governs how the Kea DHCPv4 server should deal with invalid
+ // data received from the client.
+ "sanity-checks": {
+ // Specifies how the Kea DHCPv4 server should behave when invalid
+ // data is read for a lease from the lease file. The following
+ // values are supported: "none" (don't attempt to correct the
+ // lease information), "warn" (print a warning for subnet-id
+ // related inconsistencies), "fix" (correct the subnet id by
+ // trying to find the suitable subnet), "fix-del" (similar
+ // to "fix" but delete the lease if no suitable subnet found),
+ // "del" (delete the lease if the lease has invalid subnet
+ // identifier value).
+ "lease-checks": "warn",
+
+ // Specifies how Kea DHCPv4 server should behave when invalid
+ // extended info is read for a lease from the lease file, or
+ // whether to upgrade from the old format. The following values
+ // are supported: "none" (don't attempt to correct or upgrade
+ // the extended info), "fix" (fix common inconsistencies and
+ // upgrade from the old format; this is the default), "strict"
+ // (fix inconsistencies with an impact on Leasequery),
+ // "pedantic" (enforce full Kea code format).
+ "extended-info-checks": "fix"
+ },
+
+ // List of shared networks used by the Kea DHCPv4 server. The shared
+ // networks group subnets together.
+ "shared-networks": [
+ {
+ // A flag selecting an IP address allocation strategy for all
+ // subnets in this shared network.
+ "allocator": "random",
+
+ // Shared-network level authoritative flag.
+ "authoritative": false,
+
+ // Shared-network level bootfile name.
+ "boot-file-name": "/dev/null",
+
+ // Restricts this shared network to allow only clients
+ // that belong to a particular client class. If an
+ // empty string is provided, no restriction is applied.
+ "client-class": "",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-generated-prefix": "myhost",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-override-client-update": false,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-override-no-update": false,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-qualifying-suffix": "",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-replace-client-name": "never",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-send-updates": true,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-update-on-renew": true,
+
+ // Shared-network level value. See description at the global level.
+ // This field has been replaced by ddns-conflict-resolution-mode.
+ // Parsing is maintained only for backwards compatibility.
+ // "ddns-use-conflict-resolution": true,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-conflict-resolution-mode": "check-with-dhcid",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-ttl-percent": 0.65,
+
+ // Shared-network level value. See description at the global level.
+ "hostname-char-replacement": "x",
+
+ // Shared-network level value. See description at the global level.
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+
+ // Specifies that this shared network is selected for
+ // requests received on a particular interface.
+ "interface": "eth0",
+
+ // Shared-network level flag specifying whether the client
+ // identifier should be used for identifying clients.
+ "match-client-id": true,
+
+ // Shared network name.
+ "name": "my-secret-network",
+
+ // Shared-network level specification of the next server
+ // to be sent in 'siaddr'.
+ "next-server": "192.0.2.123",
+
+ // If greater than zero, it is the lifetime of leases temporarily allocated
+ // on DISCOVER. When zero (the default), leases are not allocated on DISCOVER.
+ "offer-lifetime" : 60,
+
+ // List of shared network-specific DHCP options.
+ "option-data": [],
+
+ // List of IPv4 relay addresses for which this shared
+ // network is selected.
+ "relay": {
+ "ip-addresses": []
+ },
+
+ // Shared-network level rebind timer.
+ "rebind-timer": 41,
+
+ // Shared-network level renew timer.
+ "renew-timer": 31,
+
+ // Shared-network level compute T1 and T2 timers.
+ "calculate-tee-times": true,
+
+ // T1 = valid lifetime * .5.
+ "t1-percent": .5,
+
+ // T2 = valid lifetime * .75.
+ "t2-percent": .75,
+
+ // Cache threshold = valid lifetime * .25.
+ "cache-threshold": .25,
+
+ // Cache maximum: when the client last-transmission time
+ // is close enough, the lease is not renewed and the current
+ // lease is returned as it was "cached".
+ "cache-max-age": 1000,
+
+ // Enumeration specifying the server's mode of operation when it
+ // fetches host reservations.
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and "reservations-out-of-pool"
+ // parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ // If specified, it is inherited by "subnet4" levels.
+ "reservations-out-of-pool": false,
+
+ // List of client classes which must be evaluated when this shared
+ // network is selected for client assignments.
+ "require-client-classes": [ "late" ],
+
+ // Turn off storage of extended information (e.g. relay agent
+ // information) with each lease for this shared network.
+ "store-extended-info": false,
+
+ // Shared-network level server hostname set in 'sname' field.
+ "server-hostname": "",
+
+ // List of IPv4 subnets belonging to this shared network.
+ "subnet4": [
+ {
+ // Interface name matched against inbound interface name.
+ // Used in DHCPv4o6. See RFC 7341.
+ "4o6-interface": "",
+
+ // Interface ID option value. See RFC 7341.
+ "4o6-interface-id": "",
+
+ // Prefix matched against source address. See RFC7341.
+ "4o6-subnet": "2001:db8:1:1::/64",
+
+ // A flag selecting an IP address allocation strategy for
+ // the subnet.
+ "allocator": "iterative",
+
+ // Subnet-level authoritative flag.
+ "authoritative": false,
+
+ // Subnet-level bootfile name, set in 'file' field.
+ "boot-file-name": "",
+
+ // Restricts this subnet to allow only clients that belong
+ // to a particular client class. If an empty string is
+ // provided, no restriction is applied.
+ "client-class": "",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-generated-prefix": "myhost",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-override-client-update": false,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-override-no-update": false,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-qualifying-suffix": "",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-replace-client-name": "never",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-send-updates": true,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-update-on-renew": true,
+
+ // Shared-network level value. See description at the global level.
+ // This field has been replaced by ddns-conflict-resolution-mode.
+ // Parsing is maintained only for backwards compatibility.
+ // "ddns-use-conflict-resolution": true,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-conflict-resolution-mode": "check-with-dhcid",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-ttl-percent": 0.55,
+
+ // Subnet-level value. See description at the global level.
+ "hostname-char-replacement": "x",
+
+ // Subnet-level value. See description at the global level.
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+
+ // Subnet unique identifier.
+ "id": 1,
+
+ // Specifies that this subnet is selected for requests
+ // received on a particular interface.
+ "interface": "eth0",
+
+ // Subnet-level flag specifying whether the client identifier
+ // should be used for identifying clients.
+ "match-client-id": true,
+
+ // Subnet-level specification of the next server to be sent
+ // in 'siaddr'.
+ "next-server": "0.0.0.0",
+
+ // If greater than zero, it is the lifetime of leases temporarily allocated
+ // on DISCOVER. When zero (the default), leases are not allocated on DISCOVER.
+ "offer-lifetime" : 60,
+
+ // Turn on storage of extended information (e.g. relay agent
+ // information) with each lease for this subnet.
+ "store-extended-info": true,
+
+ // Subnet-level list of DHCP options.
+ "option-data": [
+ {
+ // Boolean flag indicating whether the particular option
+ // should be always sent or sent only when requested.
+ "always-send": false,
+
+ // Option code.
+ "code": 3,
+
+ // Boolean flag indicating whether the option value specified
+ // in "data" is a string of hexadecimal values or human-readable
+ // CSV value.
+ "csv-format": true,
+
+ // Option data to be included in the option payload.
+ "data": "192.0.3.1",
+
+ // Option name.
+ "name": "routers",
+
+ // Boolean flag indicating whether the given option is never
+ // sent in response.
+ "never-send": false,
+
+ // Option space. The default value "dhcp4" designates the
+ // top-level option space.
+ "space": "dhcp4"
+ }
+ ],
+
+ // List of IP address pools belonging to the subnet.
+ "pools": [
+ {
+ // Restricts this pool to only be used for client
+ // requests belonging to a particular client class.
+ "client-class": "phones_server1",
+
+ // Pool-level list of DHCP options.
+ "option-data": [],
+
+ // Address range used for client assignments.
+ "pool": "192.1.0.1 - 192.1.0.200",
+
+ // List of client classes which must be evaluated when this pool
+ // is selected for client assignments.
+ "require-client-classes": [ "late" ]
+ },
+ {
+ // Restricts this pool to only be used for client
+ // requests belonging to a particular client class.
+ "client-class": "phones_server2",
+
+ // Pool-level list of DHCP options.
+ "option-data": [],
+
+ // Address range used for client assignments.
+ "pool": "192.3.0.1 - 192.3.0.200",
+
+ // List of client classes which must be evaluated when this pool
+ // is selected for client assignments.
+ "require-client-classes": [],
+
+ // Pool identifier used to enable statistics for this pool.
+ // The pool ID does not need to be unique within the subnet
+ // or across subnets.
+ // If not unconfigured, it defaults to 0. The statistics
+ // regarding this pool will be combined with the other statistics
+ // of all other pools with the same pool ID in this subnet.
+ "pool-id": 1
+ }
+ ],
+
+ // Subnet-level value of the rebind timer.
+ "rebind-timer": 40,
+
+ // List of IPv4 relay addresses for which this subnet is selected.
+ "relay": {
+ "ip-addresses": [
+ "192.168.56.1"
+ ]
+ },
+
+ // Subnet-level value of the renew timer.
+ "renew-timer": 30,
+
+ // Enumeration specifying the server's mode of operation when it
+ // fetches host reservations.
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and
+ // "reservations-out-of-pool" parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved
+ // addresses are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ "reservations-out-of-pool": false,
+
+ // Subnet-level compute T1 and T2 timers.
+ "calculate-tee-times": true,
+
+ // T1 = valid lifetime * .5.
+ "t1-percent": .5,
+
+ // T2 = valid lifetime * .75.
+ "t2-percent": .75,
+
+ // Cache threshold = valid lifetime * .25.
+ "cache-threshold": .25,
+
+ // Subnet-level cache maximum.
+ "cache-max-age": 1000,
+
+ // List of static IPv4 reservations assigned to clients belonging
+ // to this subnet. For a detailed example, see reservations.json.
+ "reservations": [
+ {
+ // Identifier used for client matching. Supported values are
+ // "hw-address", "client-id", "duid", "circuit-id", "flex-id".
+ "circuit-id": "01:11:22:33:44:55:66",
+
+ // Reserved IP address.
+ "ip-address": "192.0.2.204",
+
+ // Hostname.
+ "hostname": "foo.example.org",
+
+ // Reservation-specific option data.
+ "option-data": [
+ {
+ // Option name.
+ "name": "vivso-suboptions",
+
+ // Option data.
+ "data": "4491"
+ }
+ ]
+ }
+ ],
+
+ // List of client classes which must be evaluated when this subnet
+ // is selected for client assignments.
+ "require-client-classes": [ "late" ],
+
+ // Subnet-level server hostname set in 'sname' field.
+ "server-hostname": "",
+
+ // Subnet prefix.
+ "subnet": "192.0.0.0/8",
+
+ // Subnet-level (default) valid lifetime.
+ "valid-lifetime": 6000,
+
+ // Subnet-level min valid lifetime.
+ "min-valid-lifetime": 4000,
+
+ // Subnet-level max valid lifetime.
+ "max-valid-lifetime": 8000
+ }
+ ],
+
+ // Shared-network level (default) valid lifetime.
+ "valid-lifetime": 6001,
+
+ // Shared-network level min valid lifetime.
+ "min-valid-lifetime": 4001,
+
+ // Shared-network level max valid lifetime.
+ "max-valid-lifetime": 8001
+ }
+ ],
+
+ // Global server hostname set in the 'sname' field.
+ "server-hostname": "",
+
+ // List of IPv4 subnets which don't belong to any shared network.
+ "subnet4": [],
+
+ // Global valid lifetime value.
+ "valid-lifetime": 6000,
+
+ // Global min valid lifetime value.
+ "min-valid-lifetime": 4000,
+
+ // Global max valid lifetime value.
+ "max-valid-lifetime": 8000,
+
+ // Reservations (examples are in other files).
+ "reservations": [],
+
+ // Configuration control (currently not used, i.e. this syntax
+ // is already defined but the corresponding feature is not implemented).
+ "config-control": {
+ // Only the configuration databases entry is defined.
+ "config-databases": [
+ {
+ // Name of the database to connect to.
+ "name": "config",
+
+ // Type of database, e.g. "mysql", "postgresql".
+ "type": "mysql"
+ }
+ ],
+ // Interval between attempts to fetch configuration updates
+ // via the configuration backends used.
+ "config-fetch-wait-time": 30
+ },
+
+ // Server tag.
+ "server-tag": "my DHCPv4 server",
+
+ // DHCP queue-control parameters.
+ "dhcp-queue-control": {
+ // Enable queue is mandatory.
+ "enable-queue": true,
+
+ // Queue type is mandatory.
+ "queue-type": "kea-ring4",
+
+ // Capacity is optional.
+ "capacity": 64
+ },
+
+ // Fetches host reservations.
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and "reservations-out-of-pool" parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ // If specified, it is inherited by "shared-networks" and
+ // "subnet4" levels.
+ "reservations-out-of-pool": false,
+
+ // Global compute T1 and T2 timers.
+ "calculate-tee-times": true,
+
+ // T1 = valid lifetime * .5.
+ "t1-percent": .5,
+
+ // T2 = valid lifetime * .75.
+ "t2-percent": .75,
+
+ // Cache threshold = valid lifetime * .25.
+ "cache-threshold": .25,
+
+ // Global cache maximum.
+ "cache-max-age": 1000,
+
+ // String of zero or more characters with which to replace each
+ // invalid character in the hostname or Client FQDN. The default
+ // value is an empty string, which will cause invalid characters
+ // to be omitted rather than replaced.
+ "hostname-char-replacement": "x",
+
+ // Regular expression describing the invalid character set in
+ // the hostname or Client FQDN.
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+
+ // List of loggers used by the servers using this configuration file.
+ "loggers": [
+ {
+ // Debug level, a value between 0..99. The greater the value
+ // the more detailed the debug log.
+ "debuglevel": 99,
+
+ // Name of the logger.
+ "name": "kea-dhcp4",
+
+ // Configures how the log should be output.
+ "output-options": [
+ {
+ // Determines whether the log should be flushed to a file.
+ "flush": true,
+
+ // Specifies maximum filesize before the file is rotated.
+ "maxsize": 10240000,
+
+ // Specifies the maximum number of rotated files to be kept.
+ "maxver": 1,
+
+ // Specifies the logging destination.
+ "output": "stdout",
+
+ // Specifies log entry content
+ "pattern": "%D{%Y-%m-%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+ }
+ ],
+
+ // Specifies logging severity, i.e. "ERROR", "WARN", "INFO", "DEBUG".
+ "severity": "INFO"
+ }
+ ],
+
+ // If greater than zero, it is the lifetime of leases temporarily allocated
+ // on DISCOVER. When zero (the default), leases are not allocated on DISCOVER.
+ "offer-lifetime" : 60,
+
+ // Look at advanced examples for the use of user-contexts.
+ "user-context": { }
+ }
+}
diff --git a/doc/examples/kea4/all-options.json b/doc/examples/kea4/all-options.json
new file mode 100644
index 0000000..f74dfcf
--- /dev/null
+++ b/doc/examples/kea4/all-options.json
@@ -0,0 +1,1889 @@
+// This example configuration file for DHCPv4 server in Kea contains:
+//
+// - data for all the standard options
+// - custom option definitions at global level along with some associated
+// option data
+// - custom option data with standardized option spaces other than "dhcp4"
+// - custom option spaces
+// - option embedding examples
+//
+// The reader is strongly encouraged to take a look at the option formats
+// documented in the Kea ARM:
+// https://kea.readthedocs.io/en/latest/arm/dhcp4-srv.html?highlight=list%20of%20standard%20dhcpv4#id2
+
+{
+ "Dhcp4": {
+ /*
+ Data for all standard option definitions
+ */
+ // Option data defined globally
+ "option-data": [
+ /*
+ Code Len Time Offset
+ +-----+-----+-----+-----+-----+-----+
+ | 2 | 4 | n1 | n2 | n3 | n4 |
+ +-----+-----+-----+-----+-----+-----+
+ */
+ // Type: int32
+ {
+ "code": 2,
+ "data": "-25200",
+ "name": "time-offset"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 3 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 3,
+ "data": "192.0.2.2, 192.0.2.3",
+ "name": "routers"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 4 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 4,
+ "data": "192.0.2.4, 192.0.2.5",
+ "name": "time-servers"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 5 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 5,
+ "data": "192.0.2.6, 192.0.2.7",
+ "name": "name-servers"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 6 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 6,
+ "data": "192.0.2.8, 192.0.2.9",
+ "name": "domain-name-servers"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 7 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 7,
+ "data": "192.0.2.10, 192.0.2.11",
+ "name": "log-servers"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 8 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 8,
+ "data": "192.0.2.12, 192.0.2.13",
+ "name": "cookie-servers"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 9 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 9,
+ "data": "192.0.2.14, 192.0.2.15",
+ "name": "lpr-servers"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 10 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 10,
+ "data": "192.0.2.16, 192.0.2.17",
+ "name": "impress-servers"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 11 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 11,
+ "data": "192.0.2.18, 192.0.2.19",
+ "name": "resource-location-servers"
+ },
+
+ /*
+ Code Len File Size
+ +-----+-----+-----+-----+
+ | 13 | 2 | l1 | l2 |
+ +-----+-----+-----+-----+
+ */
+ // Type: uint16
+ {
+ "code": 13,
+ "data": "1024",
+ "name": "boot-size"
+ },
+
+ /*
+ Code Len Dump File Pathname
+ +-----+-----+-----+-----+-----+-----+---
+ | 14 | n | n1 | n2 | n3 | n4 | ...
+ +-----+-----+-----+-----+-----+-----+---
+ */
+ // Type: string
+ {
+ "code": 14,
+ "data": "/etc/crash-dump.img",
+ "name": "merit-dump"
+ },
+
+ /*
+ Code Len Domain Name
+ +-----+-----+-----+-----+-----+-----+--
+ | 15 | n | d1 | d2 | d3 | d4 | ...
+ +-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: FQDN
+ {
+ "code": 15,
+ "data": "my.example.org",
+ "name": "domain-name"
+ },
+
+ /*
+ Code Len Swap Server Address
+ +-----+-----+-----+-----+-----+-----+
+ | 16 | n | a1 | a2 | a3 | a4 |
+ +-----+-----+-----+-----+-----+-----+
+ */
+ // Type: IPv4 address
+ {
+ "code": 16,
+ "data": "192.0.2.20",
+ "name": "swap-server"
+ },
+
+ /*
+ Code Len Root Disk Pathname
+ +-----+-----+-----+-----+-----+-----+---
+ | 17 | n | n1 | n2 | n3 | n4 | ...
+ +-----+-----+-----+-----+-----+-----+---
+ */
+ // Type: string
+ {
+ "code": 17,
+ "data": "/path/to/root",
+ "name": "root-path"
+ },
+
+ /*
+ Code Len Extensions Pathname
+ +-----+-----+-----+-----+-----+-----+---
+ | 18 | n | n1 | n2 | n3 | n4 | ...
+ +-----+-----+-----+-----+-----+-----+---
+ */
+ // Type: string
+ {
+ "code": 18,
+ "data": "/path/to/extensions",
+ "name": "extensions-path"
+ },
+
+ /*
+ Code Len Value
+ +-----+-----+-----+
+ | 19 | 1 | 0/1 |
+ +-----+-----+-----+
+ */
+ // Type: boolean
+ {
+ "code": 19,
+ "data": "true",
+ "name": "ip-forwarding"
+ },
+
+ /*
+ Code Len Value
+ +-----+-----+-----+
+ | 20 | 1 | 0/1 |
+ +-----+-----+-----+
+ */
+ // Type: boolean
+ {
+ "code": 20,
+ "data": "true",
+ "name": "non-local-source-routing"
+ },
+
+ /*
+ Code Len Address 1 Mask 1
+ +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+ | 21 | n | a1 | a2 | a3 | a4 | m1 | m2 | m3 | m4 |
+ +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+ Address 2 Mask 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+---
+ | a1 | a2 | a3 | a4 | m1 | m2 | m3 | m4 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+---
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 21,
+ "data": "10.229.0.128, 255.255.255.128, 10.27.129.0, 255.255.255.0",
+ "name": "policy-filter"
+ },
+
+ /*
+ Code Len Size
+ +-----+-----+-----+-----+
+ | 22 | 2 | s1 | s2 |
+ +-----+-----+-----+-----+
+ */
+ // Type: uint16
+ {
+ "code": 22,
+ "data": "2048",
+ "name": "max-dgram-reassembly"
+ },
+
+ /*
+ Code Len TTL
+ +-----+-----+-----+
+ | 23 | 1 | ttl |
+ +-----+-----+-----+
+ */
+ // Type: uint8
+ {
+ "code": 23,
+ "data": "248",
+ "name": "default-ip-ttl"
+ },
+
+ /*
+ Code Len Timeout
+ +-----+-----+-----+-----+-----+-----+
+ | 24 | 4 | t1 | t2 | t3 | t4 |
+ +-----+-----+-----+-----+-----+-----+
+ */
+ // Type: uint32
+ {
+ "code": 24,
+ "data": "131072",
+ "name": "path-mtu-aging-timeout"
+ },
+
+ /*
+ Code Len Size 1 Size 2
+ +-----+-----+-----+-----+-----+-----+---
+ | 25 | n | s1 | s2 | s1 | s2 | ...
+ +-----+-----+-----+-----+-----+-----+---
+ */
+ // Type: array of {uint16}
+ {
+ "code": 25,
+ "data": "3072, 4096",
+ "name": "path-mtu-plateau-table"
+ },
+
+ /*
+ Code Len MTU
+ +-----+-----+-----+-----+
+ | 26 | 2 | m1 | m2 |
+ +-----+-----+-----+-----+
+ */
+ // Type: uint16
+ {
+ "code": 26,
+ "data": "5120",
+ "name": "interface-mtu"
+ },
+
+ /*
+ Code Len Value
+ +-----+-----+-----+
+ | 27 | 1 | 0/1 |
+ +-----+-----+-----+
+ */
+ // Type: boolean
+ {
+ "code": 27,
+ "data": "true",
+ "name": "all-subnets-local"
+ },
+
+ /*
+ Code Len Broadcast Address
+ +-----+-----+-----+-----+-----+-----+
+ | 28 | 4 | b1 | b2 | b3 | b4 |
+ +-----+-----+-----+-----+-----+-----+
+ */
+ // Type: IPv4 address
+ {
+ "code": 28,
+ "data": "192.0.2.255",
+ "name": "broadcast-address"
+ },
+
+ /*
+ Code Len Value
+ +-----+-----+-----+
+ | 29 | 1 | 0/1 |
+ +-----+-----+-----+
+ */
+ // Type: boolean
+ {
+ "code": 29,
+ "data": "true",
+ "name": "perform-mask-discovery"
+ },
+
+ /*
+ Code Len Value
+ +-----+-----+-----+
+ | 30 | 1 | 0/1 |
+ +-----+-----+-----+
+ */
+ // Type: boolean
+ {
+ "code": 30,
+ "data": "true",
+ "name": "mask-supplier"
+ },
+
+ /*
+ Code Len Value
+ +-----+-----+-----+
+ | 31 | 1 | 0/1 |
+ +-----+-----+-----+
+ */
+ // Type: boolean
+ {
+ "code": 31,
+ "data": "true",
+ "name": "router-discovery"
+ },
+
+ /*
+ Code Len Address
+ +-----+-----+-----+-----+-----+-----+
+ | 32 | 4 | a1 | a2 | a3 | a4 |
+ +-----+-----+-----+-----+-----+-----+
+ */
+ // Type: IPv4 address
+ {
+ "code": 32,
+ "data": "192.0.2.23",
+ "name": "router-solicitation-address"
+ },
+
+ /*
+ Code Len Destination 1 Router 1
+ +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+ | 33 | n | d1 | d2 | d3 | d4 | r1 | r2 | r3 | r4 |
+ +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+ Destination 2 Router 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+---
+ | d1 | d2 | d3 | d4 | r1 | r2 | r3 | r4 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+---
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 33,
+ "data": "192.0.2.24, 192.0.2.25",
+ "name": "static-routes"
+ },
+
+ /*
+ Code Len Value
+ +-----+-----+-----+
+ | 34 | 1 | 0/1 |
+ +-----+-----+-----+
+ */
+ // Type: boolean
+ {
+ "code": 34,
+ "data": "true",
+ "name": "trailer-encapsulation"
+ },
+
+ /*
+ Code Len Time
+ +-----+-----+-----+-----+-----+-----+
+ | 35 | 4 | t1 | t2 | t3 | t4 |
+ +-----+-----+-----+-----+-----+-----+
+ */
+ // Type: uint32
+ {
+ "code": 35,
+ "data": "196608",
+ "name": "arp-cache-timeout"
+ },
+
+ /*
+ Code Len Value
+ +-----+-----+-----+
+ | 36 | 1 | 0/1 |
+ +-----+-----+-----+
+ */
+ // Type: boolean
+ {
+ "code": 36,
+ "data": "true",
+ "name": "ieee802-3-encapsulation"
+ },
+
+ /*
+ Code Len TTL
+ +-----+-----+-----+
+ | 37 | 1 | n |
+ +-----+-----+-----+
+ */
+ // Type: uint8
+ {
+ "code": 37,
+ "data": "124",
+ "name": "default-tcp-ttl"
+ },
+
+ /*
+ Code Len Time
+ +-----+-----+-----+-----+-----+-----+
+ | 38 | 4 | t1 | t2 | t3 | t4 |
+ +-----+-----+-----+-----+-----+-----+
+ */
+ // Type: uint32
+ {
+ "code": 38,
+ "data": "262144",
+ "name": "tcp-keepalive-interval"
+ },
+
+ /*
+ Code Len Value
+ +-----+-----+-----+
+ | 39 | 1 | 0/1 |
+ +-----+-----+-----+
+ */
+ // Type: boolean
+ {
+ "code": 39,
+ "data": "true",
+ "name": "tcp-keepalive-garbage"
+ },
+
+ /*
+ Code Len NIS Domain Name
+ +-----+-----+-----+-----+-----+-----+---
+ | 40 | n | n1 | n2 | n3 | n4 | ...
+ +-----+-----+-----+-----+-----+-----+---
+ */
+ // Type: string
+ {
+ "code": 40,
+ "data": "nis.example.org",
+ "name": "nis-domain"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 41 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 41,
+ "data": "192.0.2.26, 192.0.2.27",
+ "name": "nis-servers"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 42 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 42,
+ "data": "192.0.2.28, 192.0.2.29",
+ "name": "ntp-servers"
+ },
+
+ /*
+ Code Len Vendor-specific information
+ +-----+-----+-----+-----+---
+ | 43 | n | i1 | i2 | ...
+ +-----+-----+-----+-----+---
+
+ Code Len Data item Code Len Data item Code
+ +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+ | T1 | n | d1 | d2 | ... | T2 | n | D1 | D2 | ... | ... |
+ +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+ */
+ // Type: empty
+ {
+ "code": 43,
+ "name": "vendor-encapsulated-options"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+ | 44 | n | a1 | a2 | a3 | a4 | b1 | b2 | b3 | b4 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 44,
+ "data": "192.0.2.30, 192.0.2.31",
+ "name": "netbios-name-servers"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+ | 45 | n | a1 | a2 | a3 | a4 | b1 | b2 | b3 | b4 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 45,
+ "data": "192.0.2.32, 192.0.2.33",
+ "name": "netbios-dd-server"
+ },
+
+ /*
+ Value Node Type
+ ----- ---------
+ 0x1 B-node
+ 0x2 P-node
+ 0x4 M-node
+ 0x8 H-node
+
+ Code Len Node Type
+ +-----+-----+-----------+
+ | 46 | 1 | see above |
+ +-----+-----+-----------+
+ */
+ // Type: uint8
+ {
+ "code": 46,
+ "data": "0x1",
+ "name": "netbios-node-type"
+ },
+
+ /*
+ Code Len NetBIOS Scope
+ +-----+-----+-----+-----+-----+-----+----
+ | 47 | n | s1 | s2 | s3 | s4 | ...
+ +-----+-----+-----+-----+-----+-----+----
+ */
+ // Type: string
+ {
+ "code": 47,
+ "data": "scope42",
+ "name": "netbios-scope"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+---
+ | 48 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+---
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 48,
+ "data": "192.0.2.34, 192.0.2.35",
+ "name": "font-servers"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+---
+ | 49 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+---
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 49,
+ "data": "192.0.2.36, 192.0.2.37",
+ "name": "x-display-manager"
+ },
+
+ /*
+ Value Meaning
+ ----- --------
+ 1 the 'file' field is used to hold options
+ 2 the 'sname' field is used to hold options
+ 3 both fields are used to hold options
+
+ Code Len Value
+ +-----+-----+-----+
+ | 52 | 1 |1/2/3|
+ +-----+-----+-----+
+ */
+ // Type: uint8
+ {
+ "code": 52,
+ "data": "3",
+ "name": "dhcp-option-overload"
+ },
+
+ /*
+ Code Len Address
+ +-----+-----+-----+-----+-----+-----+
+ | 54 | 4 | a1 | a2 | a3 | a4 |
+ +-----+-----+-----+-----+-----+-----+
+ */
+ // Type: IPv4 address
+ {
+ "code": 54,
+ "data": "192.0.2.39",
+ "name": "dhcp-server-identifier"
+ },
+
+ /*
+ Code Len Text
+ +-----+-----+-----+-----+---
+ | 56 | n | c1 | c2 | ...
+ +-----+-----+-----+-----+---
+ */
+ // Type: string
+ {
+ "code": 56,
+ "data": "Error: here is a DHCPNAK!",
+ "name": "dhcp-message"
+ },
+
+ /*
+ Code Len Length
+ +-----+-----+-----+-----+
+ | 57 | 2 | l1 | l2 |
+ +-----+-----+-----+-----+
+ */
+ // Type: uint16
+ {
+ "code": 57,
+ "data": "1536",
+ "name": "dhcp-max-message-size"
+ },
+
+ /*
+ Code Len Vendor class Identifier
+ +-----+-----+-----+-----+---
+ | 60 | n | i1 | i2 | ...
+ +-----+-----+-----+-----+---
+ */
+ // Type: string
+ {
+ "code": 60,
+ "data": "ISC",
+ "name": "vendor-class-identifier"
+ },
+
+ /*
+ Code Len NetWare/IP Domain Name
+ +-----+-----+------+------+------+-----
+ | 62 | n | c1 | c2 | c3 | ...
+ +-----+-----+------+------+------+-----
+ */
+ // Type: string
+ {
+ "code": 62,
+ "data": "nwip.example.org",
+ "name": "nwip-domain-name"
+ },
+
+ /*
+ Code Len NetWare/IP General Info
+ +-----+-----+----+----+
+ | 63 | 11 | 2 | 0 |
+ +-----+-----+----+----+
+ NWIP_EXIST_IN_OPTIONS_AREA (length 0)
+
+ +----+----+----+
+ | 5 | 1 | 1 |
+ +----+----+----+
+ NSQ_BROADCAST_SERVER (length 1)
+ value is YES
+
+ +----+----+------------+
+ | 7 | 4 | IP address |
+ +----+----+------------+
+ NEAREST_NWIP_SERVER (length 4)
+ value is IP address of server
+ */
+ // Type: binary
+ {
+ "code": 63,
+ "data": "1A BB AD AB BA D0 00 00 00 00 00 00 00 00 CA FE",
+ "name": "nwip-suboptions"
+ },
+
+ /*
+ Code Len NIS Client Domain Name
+ +-----+-----+-----+-----+-----+-----+---
+ | 64 | n | n1 | n2 | n3 | n4 | ...
+ +-----+-----+-----+-----+-----+-----+---
+ */
+ // Type: string
+ {
+ "code": 64,
+ "data": "nisplus.example.org",
+ "name": "nisplus-domain-name"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 65 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: IPv4 address
+ {
+ "code": 65,
+ "data": "192.0.2.40",
+ "name": "nisplus-servers"
+ },
+
+ /*
+ Code Len TFTP server
+ +-----+-----+-----+-----+-----+---
+ | 66 | n | c1 | c2 | c3 | ...
+ +-----+-----+-----+-----+-----+---
+ */
+ // Type: string
+ {
+ "code": 66,
+ "data": "tftp.example.org",
+ "name": "tftp-server-name"
+ },
+
+ /*
+ Code Len Bootfile name
+ +-----+-----+-----+-----+-----+---
+ | 67 | n | c1 | c2 | c3 | ...
+ +-----+-----+-----+-----+-----+---
+ */
+ // Type: string
+ {
+ "code": 67,
+ "data": "boot-file.img",
+ "name": "boot-file-name"
+ },
+
+ /*
+ Code Len Home Agent Addresses (zero or more)
+ +-----+-----+-----+-----+-----+-----+--
+ | 68 | n | a1 | a2 | a3 | a4 | ...
+ +-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 68,
+ "data": "192.0.2.41, 192.0.2.42",
+ "name": "mobile-ip-home-agent"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 69 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 69,
+ "data": "192.0.2.43, 192.0.2.44",
+ "name": "smtp-server"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 70 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 70,
+ "data": "192.0.2.45, 192.0.2.46",
+ "name": "pop-server"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 71 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 71,
+ "data": "192.0.2.47, 192.0.2.48",
+ "name": "nntp-server"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 72 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 72,
+ "data": "192.0.2.49, 192.0.2.50",
+ "name": "www-server"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 73 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 73,
+ "data": "192.0.2.51, 192.0.2.52",
+ "name": "finger-server"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 74 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 74,
+ "data": "192.0.2.53, 192.0.2.54",
+ "name": "irc-server"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 75 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 75,
+ "data": "192.0.2.55, 192.0.2.56",
+ "name": "streettalk-server"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 76 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 76,
+ "data": "192.0.2.57, 192.0.2.58",
+ "name": "streettalk-directory-assistance-server"
+ },
+
+ /*
+ Code Len Value
+ +-----+-----+--------------------- . . . --+
+ | 77 | N | User Class Data ('Len' octets) |
+ +-----+-----+--------------------- . . . --+
+ */
+ // Type: binary
+ {
+ "code": 77,
+ "data": "1A BB AD AB BA D0 00 00 00 00 00 00 00 00 CA FE",
+ "name": "user-class"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Code = 78 | Length | Mandatory | a1 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | a2 | a3 | a4 | ...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ // Type: boolean, array of {IPv4 address}
+ {
+ "code": 78,
+ "data": "true, 192.0.2.59, 192.0.2.60",
+ "name": "slp-directory-agent"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Code = 79 | Length | Mandatory | <Scope List>...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ // Type: boolean, string
+ {
+ "code": 79,
+ "data": "true, slp-scope",
+ "name": "slp-service-scope"
+ },
+
+ // Option code 80 is not defined in Kea.
+ // Option code 83 is not defined in Kea.
+ // Option code 84 is unassigned.
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+--
+ | 85 | n | a1 | a2 | a3 | a4 | a1 | a2 | a3 | a4 | ...
+ +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of IPv4 address
+ {
+ "code": 85,
+ "data": "192.0.2.61, 192.0.2.62",
+ "name": "nds-servers"
+ },
+
+ /*
+ Code Len NDS Tree Name
+ +----+----+----+----+----+----+--
+ | 86 | n | c1 | c2 | c3 | c4 | ...
+ +----+----+----+----+----+----+--
+ */
+ // Type: string
+ {
+ "code": 86,
+ "data": "my-tree",
+ "name": "nds-tree-name"
+ },
+
+ /*
+ Code Len Initial NDS Context
+ +----+----+----+----+----+----+--
+ | 87 | n | c1 | c2 | c3 | c4 | ...
+ +----+----+----+----+----+----+--
+ */
+ // Type: string
+ {
+ "code": 87,
+ "data": "context",
+ "name": "nds-context"
+ },
+
+ /*
+ Code Len FQDN(s) of BCMCS Controller
+ +-----+-----+-----+-----+-----+-----+-----+--
+ | 88 | n | s1 | s2 | s3 | s4 | s5 | ...
+ +-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: FQDN
+ {
+ "code": 88,
+ "data": "bcms-controller.example.org",
+ "name": "bcms-controller-names"
+ },
+
+ /*
+ Code Len Address 1 Address 2
+ +-----+-----+-----+-----+-----+-----+-----+--
+ | 89 | n | a1 | a2 | a3 | a4 | a1 | ...
+ +-----+-----+-----+-----+-----+-----+-----+--
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 89,
+ "data": "192.0.2.63",
+ "name": "bcms-controller-address"
+ },
+
+ /*
+ Code Len 16-bit Type
+ +----+-----+-----+-----+
+ | 93 | n | n1 | n2 |
+ +----+-----+-----+-----+
+ */
+ // Type: array of uint16
+ {
+ "code": 93,
+ "data": "6144, 7168",
+ "name": "client-system"
+ },
+
+ /*
+ Code Len Type Major Minor
+ +----+-----+----+-----+-----+
+ | 94 | 3 | t | M | m |
+ +----+-----+----+-----+-----+
+ */
+ // Type: uint8, uint8, uint8
+ {
+ "code": 94,
+ "data": "0, 1, 0",
+ "name": "client-ndi"
+ },
+
+ // Option code 95 is unsupported.
+ // Option code 96 is unassigned.
+
+ /*
+ Code Len Type Machine Identifier
+ +----+-----+----+-----+ . . . +-----+
+ | 97 | n | t | | . . . | |
+ +----+-----+----+-----+ . . . +-----+
+ */
+ // Type: uint8, binary
+ {
+ "code": 97,
+ "data": "0, 1A BB AD AB BA D0 00 00 00 00 00 00 00 00 CA FE",
+ "name": "uuid-guid"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Code | Length | URL list
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Code 98
+
+ Length The length of the data field (i.e., URL list) in
+ bytes.
+
+ URL list A list of one or more URLs separated by the ASCII
+ space character (0x20).
+ */
+ // Type: string
+ {
+ "code": 98,
+ "data": "uap1.example.org uap2.example.org",
+ "name": "uap-servers"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | GEOCONF_CIVIC | N | what | country |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | code | civic address elements ...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Code GEOCONF_CIVIC: The code for this DHCP option is 99.
+
+ N: The length of this option is variable. The minimum length is 3
+ octets.
+
+ what: The 'what' element describes to which location the DHCP entry
+ refers. Currently, three options are defined: the location of the
+ DHCP server (a value of 0), the location of the network element
+ believed to be closest to the client (a value of 1), or the
+ location of the client (a value of 2). Option (2) SHOULD be used,
+ but may not be known. Options (0) and (1) SHOULD NOT be used
+ unless it is known that the DHCP client is in close physical
+ proximity to the server or network element.
+
+ country code: The two-letter ISO 3166 country code in capital ASCII
+ letters, e.g., DE or US. (Civic addresses always contain country
+ designations, suggesting the use of a fixed-format field to save
+ space.)
+
+ civic address elements: Zero or more elements comprising the civic
+ and/or postal address, with the format described below
+ (Section 3.3).
+ */
+ // Type: binary
+ {
+ "code": 99,
+ "data": "1A BB AD AB BA D0 00 00 00 00 00 00 00 00 CA FE",
+ "name": "geoconf-civic"
+ },
+
+ /*
+ PCode Len TZ-POSIX String
+ +-----+-----+------------------------------+
+ | 100 | N | IEEE 1003.1 String |
+ +-----+-----+------------------------------+
+ */
+ // Type: string
+ {
+ "code": 100,
+ // String options that have a comma in their values need to have
+ // it escaped (i.e. each comma is preceded by two backslashes).
+ // That's because commas are reserved for separating fields in
+ // compound options. At the same time, we need to be conformant
+ // with JSON spec, that does not allow "\,". Therefore the
+ // slightly uncommon double backslashes notation is needed.
+ // The value sent over the wire is:
+ // EST5EDT4,M3.2.0/02:00,M11.1.0/02:00
+ "data": "EST5EDT4\\,M3.2.0/02:00\\,M11.1.0/02:00",
+ "name": "pcode"
+ },
+
+ /*
+ TCode Len TZ-Database String
+ +-----+-----+------------------------------+
+ | 101 | N | Reference to the TZ Database |
+ +-----+-----+------------------------------+
+ */
+ // Type: string
+ {
+ "code": 101,
+ "data": "Europe/Zurich",
+ "name": "tcode"
+ },
+
+ // Option codes 102-107 are unassigned.
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Code | Length | Value |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Value (cont.) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Code: 8-bit identifier of the IPv6-Only Preferred option code as
+ assigned by IANA: 108. The client includes the Code in the
+ Parameter Request List in DHCPDISCOVER and DHCPREQUEST messages as
+ described in Section 3.2.
+
+ Length: 8-bit unsigned integer. The length of the option, excluding
+ the Code and Length Fields. The server MUST set the length field
+ to 4. The client MUST ignore the IPv6-Only Preferred option if
+ the length field value is not 4.
+
+ Value: 32-bit unsigned integer. The number of seconds for which the
+ client should disable DHCPv4 (V6ONLY_WAIT configuration variable).
+ If the server pool is explicitly configured with a V6ONLY_WAIT
+ timer, the server MUST set the field to that configured value.
+ Otherwise, the server MUST set it to zero. The client MUST
+ process that field as described in Section 3.2.
+ */
+ // Type: uint32
+ {
+ "code": 108,
+ "data": "3600",
+ "name": "v6-only-preferred"
+ },
+
+ // Option codes 109-111 are unassigned.
+
+ // Type: array of {IPv4 address}
+ {
+ "code": 112,
+ "data": "192.0.2.63, 192.0.2.64",
+ "name": "netinfo-server-address"
+ },
+
+ // Type: string
+ {
+ "code": 113,
+ "data": "server1",
+ "name": "netinfo-server-tag"
+ },
+
+ // Type: string
+ {
+ "code": 114,
+ "data": "https://default.example.org",
+ "name": "v4-captive-portal"
+ },
+
+ // Option code 115 is unassigned.
+
+ /*
+ Code Len Value
+ +-----+-----+-----+
+ | 116 | 1 | a |
+ +-----+-----+-----+
+ */
+ // Type: uint8
+ {
+ "code": 116,
+ "data": "1",
+ "name": "auto-config"
+ },
+
+ /*
+ Code Length Name Service Search Order in Sequence
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 117 | Len | ns1 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ns2 | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ // Type: array of {uint16}
+ {
+ "code": 117,
+ "data": "6, 41, 44, 65",
+ "name": "name-service-search"
+ },
+
+ /*
+ Code Len IPv4 Address
+ +-----+-----+-----+-----+-----+-----+
+ | 118 | 4 | A1 | A2 | A3 | A4 |
+ +-----+-----+-----+-----+-----+-----+
+ */
+ // Type: IPv4 address
+ {
+ "code": 118,
+ "data": "192.0.2.65",
+ "name": "subnet-selection"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 119 | Len | Searchstring...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Searchstring...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ // Type: array of {FQDN}
+ {
+ "code": 119,
+ "data": "example.com, example.org",
+ "name": "domain-search"
+ },
+
+ // Option code 120 is not defined in Kea.
+
+ /*
+ Code Len Destination 1 Router 1
+ +-----+---+----+-----+----+----+----+----+----+
+ | 121 | n | d1 | ... | dN | r1 | r2 | r3 | r4 |
+ +-----+---+----+-----+----+----+----+----+----+
+
+ Destination 2 Router 2
+ +----+-----+----+----+----+----+----+
+ | d1 | ... | dN | r1 | r2 | r3 | r4 |
+ +----+-----+----+----+----+----+----+
+
+ Destination 1...N Destination descriptors - describe the IP
+ subnet number and subnet mask of a particular
+ destination using a compact encoding. This
+ encoding consists of one octet describing
+ the width of the subnet mask, followed by all
+ the significant octets of the subnet number.
+
+ Router 1...N The IP address of the router that should
+ be used to reach that destination.
+ */
+ // Type: internal
+ {
+ "code": 121,
+ // please mind the convenience notation used:
+ // subnet1 - router1 IP addr, subnet2 - router2 IP addr, ..., subnetN - routerN IP addr
+ "data": "10.229.0.128/25 - 10.229.0.1, 10.198.122.47/32 - 10.198.122.1",
+ "name": "classless-static-route"
+ },
+
+ // Option codes 122-123 are not defined in Kea.
+
+ /*
+ 1 1 1 1 1 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | enterprise-number1 |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | data-len1 | |
+ +-+-+-+-+-+-+-+-+ |
+ / vendor-class-data1 /
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ----
+ | enterprise-number2 | ^
+ | | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ | data-len2 | | optional
+ +-+-+-+-+-+-+-+-+ | |
+ / vendor-class-data2 / |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ ~ ... ~ V
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ----
+
+ option-code OPTION_V-I_VENDOR_CLASS (124)
+
+ option-len total length of all following option data in
+ octets
+
+ enterprise-numberN The vendor's 32-bit Enterprise Number as
+ registered with IANA [3]
+
+ data-lenN Length of vendor-class-data field
+
+ vendor-class-dataN Details of the hardware configuration of the
+ host on which the client is running, or of
+ industry consortium compliance
+ */
+ // Type: uint32, binary
+ {
+ "code": 124,
+ "data": "4491, 0f BA AD AB BA D0 00 00 00 00 00 00 00 00 CA FE",
+ "name": "vivco-suboptions"
+ },
+
+ /*
+ 1 1 1 1 1 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | enterprise-number1 |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | data-len1 | |
+ +-+-+-+-+-+-+-+-+ option-data1 |
+ / /
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ----
+ | enterprise-number2 | ^
+ | | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ | data-len2 | | optional
+ +-+-+-+-+-+-+-+-+ option-data2 | |
+ / / |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ ~ ... ~ V
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ----
+
+ option-code OPTION_V-I_VENDOR_OPTS (125)
+
+ option-len total length of all following option data in
+ octets
+
+ enterprise-numberN The vendor's registered 32-bit Enterprise Number
+ as registered with IANA [3]
+
+ data-lenN Length of option-data field
+
+ option-dataN Vendor-specific options, described below
+ */
+ // Type: uint32
+ {
+ "code": 125,
+ "data": "4491",
+ "name": "vivso-suboptions"
+ },
+
+ // Option codes 126-127 are unassigned.
+ // Option codes 128-135 are not defined in Kea.
+
+ /*
+ 0 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ + PAA IPv4 Address +
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ Figure 1: PAA DHCPv4 option
+
+ option-code: OPTION_PANA_AGENT (136).
+
+ option-length: Length of the 'options' field in octets;
+ MUST be a multiple of four (4).
+
+ PAA IPv4 Address: IPv4 address of a PAA for the client to use.
+ The PAAs are listed in the order of preference
+ for use by the client.
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 136,
+ "data": "192.0.2.66, 192.0.2.67",
+ "name": "pana-agent"
+ },
+
+ /*
+ Code Len LoST Server Domain Name
+ +-----+-----+-----+-----+-----+-----+-----+----
+ | 137 | n | s1 | s2 | s3 | s4 | s5 | ...
+ +-----+-----+-----+-----+-----+-----+-----+----
+ */
+ // Type: FQDN
+ {
+ "code": 137,
+ "data": "lost.example.org",
+ "name": "v4-lost"
+ },
+
+ /*
+ 0 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ + AC IPv4 Address +
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_CAPWAP_AC_V4 (138)
+
+ option-length: Length of the 'options' field in octets; MUST be a
+ multiple of four (4).
+
+ AC IPv4 Address: IPv4 address of a CAPWAP AC that the WTP may use.
+ The ACs are listed in the order of preference for use by the WTP
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 138,
+ "data": "192.0.2.68, 192.0.2.69",
+ "name": "capwap-ac-v4"
+ },
+
+ // Option codes 139-140 are not defined in Kea.
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 141 | Len | Searchstring... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Searchstring... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ // Type: array of {FQDN}
+ {
+ "code": 141,
+ "data": "example.com, example.org",
+ "name": "sip-ua-cs-domains"
+ },
+
+ // Option code 142 is not defined in Kea.
+
+ /*
+ 0 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | option-code (143) | option-length |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ . .
+ . bootstrap-server-list (variable length) .
+ . .
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+ option-code: OPTION_V4_SZTP_REDIRECT (143)
+
+ option-length: The option length in octets.
+
+ bootstrap-server-list: A list of servers for the
+ client to attempt contacting, in order to obtain
+ further bootstrapping data. Each URI entry in the
+ bootstrap-server-list is structured as follows:
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+-+-+-+-+-+
+ | uri-length | URI |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+-+-+-+-+-+
+
+ uri-length: 2 octets long; specifies the length of the URI data.
+ URI: URI of the SZTP bootstrap server.
+ */
+ // Type: array of {tuple}
+ {
+ "code": 143,
+ "data": "https://sztp1.example.com:8443, https://sztp2.example.com:8444",
+ "name": "v4-sztp-redirect"
+ },
+
+ // Option codes 144-145 are not defined in Kea.
+
+ // Type: uint8, IPv4 address, IPv4 address, array of {FQDN}
+ {
+ "code": 146,
+ "data": "1, 192.0.2.70, 192.0.2.71, example.com, example.org",
+ "name": "rdnss-selection"
+ },
+
+ // Option codes 147-158 are not defined in Kea.
+
+ // Type: uint8, PSID
+ {
+ "code": 159,
+ "data": "2, 3/4",
+ "name": "v4-portparams"
+ },
+
+ // Option codes 160-161 are unassigned.
+
+ /*
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_V4_DNR | Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ~ DNR Instance Data #1 ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---
+ . ... . |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ optional
+ ~ DNR Instance Data #n ~ |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---
+
+ DNR Instance Data Format:
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | DNR Instance Data Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Service Priority |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ADN Length | |
+ +-+-+-+-+-+-+-+-+ |
+ ~ authentication-domain-name ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Addr Length | |
+ +-+-+-+-+-+-+-+-+ |
+ ~ IPv4 Address(es) ~
+ | +-+-+-+-+-+-+-+-+
+ | | |
+ +-+-+-+-+-+-+-+-+ |
+ ~Service Parameters (SvcParams) ~
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Code: OPTION_V4_DNR (162).
+
+ Length: Indicates the length of the enclosed data in octets.
+
+ DNR Instance Data: Includes the configuration data of an encrypted
+ DNS resolver. When several encrypted DNS resolvers are to be included, the "DNR
+ Instance Data" field is repeated.
+
+ DNR Instance Data Length: Length of all following data in octets.
+ This field is set to ('ADN Length' + 3) when only an ADN is
+ provided for a DNR instance.
+
+ Service Priority: The priority of this instance compared to other
+ DNR instances. This 16-bit unsigned integer is interpreted
+ following the rules specified in Section 2.4.1 of
+ [RFC9460].
+
+ ADN Length: Length of the authentication-domain-name in octets.
+
+ authentication-domain-name (variable length): The authentication
+ domain name of the encrypted DNS resolver. This field is
+ formatted as specified in Section 10 of [RFC8415].
+
+ Addr Length: Length of included IPv4 addresses in octets. When
+ present, it MUST be a multiple of 4.
+
+ IPv4 Address(es) (variable length): Indicates one or more IPv4
+ addresses to reach the encrypted DNS resolver. Both private and
+ public IPv4 addresses can be included in this field.
+
+ Service Parameters (SvcParams) (variable length): Specifies a set of
+ service parameters that are encoded following the rules in
+ Section 2.2 of [RFC9460].
+ The length of this field is ('DNR Instance Data Length' - 4 - 'ADN
+ Length' - 'Addr Length').
+
+ Note that "Addr Length", "IPv4 Address(es)", and "Service Parameters
+ (SvcParams)" fields are not present if the ADN-only mode is used.
+ */
+ // Type: internal
+ {
+ // DNR option may be configured using convenient notation. DNR Instances must be delimited with pipe "|" char.
+ // For each DNR Instance comma delimited fields must be provided:
+ // - service priority (mandatory),
+ // - ADN (mandatory),
+ // - IP address(es) (optional - if more than one - they must be space-separated)
+ // - SvcParams (optional - if more than one - they must be space-separated;
+ // to provide more than one alpn-id separate them with double-backslash escaped comma like in the
+ // example below).
+ // Note: whenever pipe "|" char needs to be used not as the delimiter, it must be escaped with
+ // double backslash, like in case of escaped commas in alpn-ids list.
+ // Basing on the config, Kea will encode the option according to RFC9463.
+ "code": 162,
+ "name": "v4-dnr",
+ "data": "1, resolver.example., 10.2.3.4 10.0.4.5, alpn=dot\\,doq\\,h2\\,h3 dohpath=/q{?dns} | 2, resolver.example., 10.0.5.6, alpn=dot port=8530 | 3, fooexp.resolver.example."
+ },
+
+ // Option codes 163-209 are unassigned.
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_6RD | option-length | IPv4MaskLen | 6rdPrefixLen |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | 6rdPrefix |
+ | (16 octets) |
+ | |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 6rdBRIPv4Address(es) |
+ . .
+ . .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_6RD (212)
+
+ option-length The length of the DHCP option in octets (22
+ octets with one BR IPv4 address).
+
+ IPv4MaskLen The number of high-order bits that are identical
+ across all CE IPv4 addresses within a given 6rd
+ domain. This may be any value between 0 and 32.
+ Any value greater than 32 is invalid.
+
+ 6rdPrefixLen The IPv6 prefix length of the SP's 6rd IPv6
+ prefix in number of bits. For the purpose of
+ bounds checking by DHCP option processing, the
+ sum of (32 - IPv4MaskLen) + 6rdPrefixLen MUST be
+ less than or equal to 128.
+
+ 6rdBRIPv4Address One or more IPv4 addresses of the 6rd Border
+ Relay(s) for a given 6rd domain.
+
+ 6rdPrefix The service provider's 6rd IPv6 prefix
+ represented as a 16-octet IPv6 address. The bits
+ in the prefix after the 6rdPrefixlen number of
+ bits are reserved and MUST be initialized to zero
+ by the sender and ignored by the receiver.
+ */
+ // Type: uint8, uint8, IPv6 address, array of {IPv4 address}
+ {
+ "code": 212,
+ "data": "24, 96, 2001:db8::f001, 192.0.2.72, 192.0.2.73",
+ "name": "option-6rd"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Code | Length | Access Network Domain Name .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ . Access Network Domain Name (cont.) .
+ . ... .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_V4_ACCESS_DOMAIN (213).
+
+ option-length: The length of the entire access network domain name
+ option in octets.
+
+ option-value: The domain name associated with the access network,
+ encoded as described in Section 3.1.
+ */
+ // Type: FQDN
+ {
+ "code": 213,
+ "data": "example.org",
+ "name": "v4-access-domain"
+ },
+
+ // Option codes 214-219 are unassigned.
+ // Option codes 220-221 are not defined in Kea.
+ // Option codes 222-254 are unassigned
+
+ /*
+ Custom option data
+ */
+ // See "option-def" below for the definitions.
+ {
+ "code": 1,
+ "name": "my-empty-option",
+ "space": "my-fancy-space"
+ },
+ {
+ "code": 224,
+ "data": "192.0.2.74, 3/4, 1, example.org, string",
+ "name": "my-lengthy-option",
+ "space": "my-fancy-space"
+ },
+ {
+ "code": 254,
+ "data": "127, 32767, 2147483647, 255, 65535, 4294967295, 192.0.2.75, 3/4, 1, example.org, string",
+ "name": "my-fancy-option",
+ "space": "my-fancy-space"
+ },
+ {
+ "code": 232,
+ "name": "my-encapsulating-option",
+ "space": "my-encapsulating-space"
+ }
+ ],
+
+ /*
+ Custom option definitions
+ */
+ // For kea-dhcp4, custom option definitions can be global or in a client
+ // class.
+ "option-def": [
+ // New option space allows for a new set of option codes.
+ // An empty option requires no "data" in "option-data". It's
+ // presence should be sufficient to trigger custom behavior.
+ {
+ "array": false,
+ "code": 1,
+ "encapsulate": "",
+ "name": "my-empty-option",
+ "record-types": "",
+ "space": "my-fancy-space",
+ "type": "empty"
+ },
+
+ // A custom type has "type" set to "record" and all data types (which need
+ // to be more than 1, otherwise you're better off using the type directly)
+ // are specified in "record-types". If "string" is part of them, it needs
+ // to be last.
+ {
+ "array": false,
+ "code": 224,
+ "encapsulate": "",
+ "name": "my-lengthy-option",
+ "record-types": "ipv4-address, psid, tuple, fqdn, string",
+ "space": "my-fancy-space",
+ "type": "record"
+ },
+
+ // Contains arrays of all types except strings since an array of strings
+ // is not a valid option definition.
+ {
+ "array": true,
+ "code": 254,
+ "encapsulate": "",
+ "name": "my-fancy-option",
+ "record-types": "int8, int16, int32, uint8, uint16, uint32, ipv4-address, psid, tuple, fqdn",
+ "space": "my-fancy-space",
+ "type": "record"
+ },
+
+ // A single encapsulating space can be used. An option containing any
+ // option from said space will now be unpacked successfully by Kea.
+ {
+ "array": false,
+ "code": 232,
+ "encapsulate": "my-fancy-space",
+ "name": "my-encapsulating-option",
+ "record-types": "",
+ "space": "my-encapsulating-space",
+ "type": "empty"
+ }
+ ],
+
+ "subnet4": [
+ /*
+ DOCSIS3 option data
+ */
+ // Headers are as defined in CL-SP-CANN-DHCP-Reg-I16-200715.
+ // "space" is required to be explicitly defined as "docsis3-v4"
+ {
+ "option-data": [
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | CL_V4OPTION_ORO| option-len | req-opt-code-1| req-opt-code-2|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code CL_V4OPTION_ORO (1).
+
+ option-len number of requested options.
+
+ req-opt-code-n The option code for an option requested by the client.
+
+ */
+ // Type: array of {uint8}
+ {
+ "code": 1,
+ "data": "32, 42",
+ "name": "oro",
+ "space": "docsis3-v4"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option code | option-len | IPv4 address of TFTP server 1 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | address of server 1 (cont.) | IPv4 address of TFTP server 2 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | address of server 2 (cont.) | ...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ . ... | IPv4 address of TFTP server n |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | address of server n (cont.) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option code CL_VV4OPTION_TFTP_SERVERS (2)
+
+ option len number of bytes for TFTP server IPv4 addresses (4*n for
+ n servers)
+ */
+ // Type: array of {IPv4 address}
+ {
+ "code": 2,
+ "data": "192.0.2.76, 192.0.2.77",
+ "name": "tftp-servers",
+ "space": "docsis3-v4"
+ }
+ ],
+ "id": 1,
+ "subnet": "192.0.2.0/24"
+ }
+ ]
+ }
+}
diff --git a/doc/examples/kea4/backends.json b/doc/examples/kea4/backends.json
new file mode 100644
index 0000000..62817c0
--- /dev/null
+++ b/doc/examples/kea4/backends.json
@@ -0,0 +1,107 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// It is a basic scenario with one IPv4 subnet configured. It demonstrates
+// how to configure Kea to use various backends to store leases:
+// - memfile
+// - MySQL
+// - PostgreSQL
+
+{ "Dhcp4":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify lease type. Exactly one lease-database section
+// should be present. Make sure you uncomment only one.
+
+// 1. memfile backend. Leases information will be stored in flat CSV file.
+// This is the easiest backend to use as it does not require any extra
+// dependencies or services running.
+// "lease-database": {
+// "type": "memfile",
+// "persist": true,
+// "lfc-interval": 3600
+// },
+
+// 2. MySQL backend. Leases will be stored in MySQL database. Make sure it
+// is up, running and properly initialized. See kea-admin documentation
+// for details on how to initialize the database. The only strictly required
+// parameters are type and name. If other parameters are not specified,
+// Kea will assume the database is available on localhost, that user and
+// password is not necessary to connect and that timeout is 5 seconds.
+// Kea must be compiled with --with-mysql option to use this backend.
+// "lease-database": {
+// "type": "mysql",
+// "name": "keatest",
+// "host": "localhost",
+// "port": 3306,
+// "user": "keatest",
+// "password": "secret1",
+// "reconnect-wait-time": 3000, // expressed in ms
+// "max-reconnect-tries": 3,
+// "on-fail": "stop-retry-exit",
+// "retry-on-startup": false,
+// "connect-timeout": 3
+// },
+
+// 3. PostgreSQL backend. Leases will be stored in PostgreSQL database. Make
+// sure it is up, running and properly initialized. See kea-admin documentation
+// for details on how to initialize the database. The only strictly required
+// parameters are type and name. If other parameters are not specified,
+// Kea will assume the database is available on localhost, that user and
+// password is not necessary to connect and that timeout is 5 seconds.
+// Kea must be compiled with --with-pgsql option to use this backend.
+// "lease-database": {
+// "type": "postgresql",
+// "name": "keatest",
+// "host": "localhost",
+// "port": 5432,
+// "user": "keatest",
+// "password": "secret1",
+// "reconnect-wait-time": 3000, // expressed in ms
+// "max-reconnect-tries": 3,
+// "on-fail": "stop-retry-exit",
+// "retry-on-startup": false,
+// "connect-timeout": 3
+// },
+
+// Addresses will be assigned with a lifetime of 4000 seconds.
+ "valid-lifetime": 4000,
+
+// Renew and rebind timers are commented out. This implies that options
+// 58 and 59 will not be sent to the client. In this case it is up to
+// the client to pick the timer values according to RFC2131. Uncomment the
+// timers to send these options to the client.
+// "renew-timer": 1000,
+// "rebind-timer": 2000,
+
+// The following list defines subnets. We have only one subnet
+// here. We tell Kea that it is directly available over local interface.
+ "subnet4": [
+ {
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "id":1 ,
+ "subnet": "192.0.2.0/24",
+ "interface": "eth0"
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/classify.json b/doc/examples/kea4/classify.json
new file mode 100644
index 0000000..9af06d8
--- /dev/null
+++ b/doc/examples/kea4/classify.json
@@ -0,0 +1,147 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// The purpose of this example is to showcase how clients can be classified.
+
+{ "Dhcp4":
+
+{
+
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// Let's use the simplest backend: memfile and use some reasonable values
+// for timers. They are of no concern for the classification demonstration.
+ "lease-database": { "type": "memfile" },
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+ "valid-lifetime": 4000,
+
+// This list defines several classes that incoming packets can be assigned to.
+// One packet can belong to zero or more classes.
+ "client-classes": [
+
+// The first class attempts to match the whole hardware address to a specific
+// value. All incoming packets with that MAC address will get a special
+// value of the option. If there are many hosts that require special
+// treatment, it is much better to use host reservations. However, doing
+// tricks with MAC addresses may prove useful in some cases, e.g.
+// by matching OUI to known values we can detect certain vendors.
+ {
+ "name": "special_snowflake",
+ "test": "pkt4.mac == 0x010203040506",
+ "option-data": [{
+ "name": "domain-name-servers",
+ "data": "127.0.0.1"
+ }]
+ },
+
+// Let's classify all incoming DISCOVER (message type 1) to a separate
+// class.
+ {
+ "name": "discovers",
+ "test": "pkt4.msgtype == 1"
+ },
+
+// Clients are supposed to set the transaction-id field to a random value.
+// Clients that send it with 0 are most likely broken. Let's mark them
+// as such.
+ {
+ "name": "broken",
+ "test": "pkt4.transid == 0"
+ },
+
+// Let's pick VoIP phones. Those that send their class identifiers
+// as Aastra, should belong to VoIP class. For a list of all options,
+// see www.iana.org/assignments/bootp-dhcp-parameters/.
+// In this particular class, we want to set specific values
+// of certain DHCPv4 fields. If the incoming packet matches the
+// test, those fields will be set in outgoing responses.
+// The option 43 is defined to encapsulate suboption in the aastra space.
+ {
+ "name": "VoIP",
+ "test": "substring(option[60].hex,0,6) == 'Aastra'",
+ "next-server": "192.0.2.254",
+ "server-hostname": "hal9000",
+ "boot-file-name": "/dev/null",
+ "option-def": [ {
+ "name": "vendor-encapsulated-options",
+ "code": 43,
+ "type": "empty",
+ "encapsulate": "aastra" } ]
+ }
+
+ ],
+
+// The following list defines subnets. For some subnets we defined
+// a class that is allowed in that subnet. If not specified,
+// everyone is allowed. When a class is specified, only packets belonging
+// to that class are allowed for that subnet.
+ "subnet4": [
+ // This one is for VoIP devices only.
+ {
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "client-class": "VoIP",
+ "interface": "eth0"
+ },
+
+ // This one doesn't have any client-class specified, so everyone
+ // is allowed in. The normal subnet selection rules still apply,
+ // though. There is also a static class reservation for a client
+ // using MAC address 1a:1b:1c:1d:1e:1f. This client will always
+ // be assigned to this class.
+ {
+ "pools": [ { "pool": "192.0.3.1 - 192.0.3.200" } ],
+ "id": 2,
+ "subnet": "192.0.3.0/24",
+ "reservations": [
+ {
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "client-classes": [ "VoIP" ]
+ } ],
+ "interface": "eth0"
+ },
+
+ // The following list defines a subnet with pools. For some pools
+ // we defined a class that is allowed in that pool. If not specified
+ // everyone is allowed. When a class is specified, only packets belonging
+ // to that class are allowed for that pool.
+ {
+ "pools": [
+ // This one is for VoIP devices only.
+ {
+ "pool": "192.0.4.1 - 192.0.4.200",
+ "client-class": "VoIP"
+ },
+
+ // This one doesn't have any client-class specified,
+ // so everyone is allowed in.
+ {
+ "pool": "192.0.5.1 - 192.0.5.200"
+ } ],
+
+ "subnet": "192.0.4.0/23",
+ "id": 3,
+ "interface": "eth1"
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/classify2.json b/doc/examples/kea4/classify2.json
new file mode 100644
index 0000000..3bb48d2
--- /dev/null
+++ b/doc/examples/kea4/classify2.json
@@ -0,0 +1,180 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// The purpose of this example is to showcase how clients can be classified
+// with advanced features.
+
+{ "Dhcp4":
+
+{
+
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// Let's use the simplest backend: memfile and use some reasonable values
+// for timers. They are of no concern for the classification demonstration.
+ "lease-database": { "type": "memfile" },
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+ "valid-lifetime": 4000,
+
+// This list defines several classes that incoming packets can be assigned to.
+// One packet can belong to zero or more classes.
+ "client-classes": [
+
+// This class is required by the second subnet and is evaluated only
+// if it is required. The test expression returns true.
+// Note it is not possible to depend on VoIP class because it is not yet
+// defined.
+ {
+ "name": "second_subnet",
+ "only-if-required": true,
+ "test": "member('ALL')",
+ "option-data": [{
+ "name": "domain-name-servers",
+ "data": "127.0.0.1"
+ }]
+ },
+
+// Let's classify all incoming DISCOVER (message type 1) to a separate
+// class.
+ {
+ "name": "discovers",
+ "test": "pkt4.msgtype == 1"
+ },
+
+// Clients are supposed to set the transaction-id field to a random value.
+// Clients that send it with 0 are most likely broken. Let's mark them
+// as such.
+ {
+ "name": "broken",
+ "test": "pkt4.transid == 0"
+ },
+
+// Let's pick VoIP phones. Those that send their class identifiers
+// as Aastra, should belong to VoIP class. For a list of all options,
+// see www.iana.org/assignments/bootp-dhcp-parameters/.
+// In this particular class, we want to set specific values
+// of certain DHCPv4 fields. If the incoming packet matches the
+// test, those fields will be set in outgoing responses.
+// The option 43 is defined to encapsulate suboption in the aastra space.
+ {
+ "name": "VoIP",
+ "test": "substring(option[60].hex,0,6) == 'Aastra'",
+ "next-server": "192.0.2.254",
+ "server-hostname": "hal9000",
+ "boot-file-name": "/dev/null",
+ "option-def": [ {
+ "name": "vendor-encapsulated-options",
+ "code": 43,
+ "type": "empty",
+ "encapsulate": "aastra" } ]
+ },
+
+// Both a VoIP phone (by evaluation or host reservation) and has a host
+// reservation.
+ {
+ "name": "VoIP_host",
+ "test": "member('VoIP') and member('KNOWN')",
+ "server-hostname": "hal9001"
+ }
+
+ ],
+
+// The following list defines subnets. For some subnets we defined
+// a class that is allowed in that subnet. If not specified,
+// everyone is allowed. When a class is specified, only packets belonging
+// to that class are allowed for that subnet.
+ "subnet4": [
+ {
+// This one is for VoIP devices only.
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "client-class": "VoIP",
+ "interface": "eth0"
+ },
+// This one doesn't have any client-class specified, so everyone
+// is allowed in. The normal subnet selection rules still apply,
+// though. There is also a static class reservation for a client
+// using MAC address 1a:1b:1c:1d:1e:1f. This client will always
+// be assigned to this class.
+ {
+ "pools": [ { "pool": "192.0.3.1 - 192.0.3.200" } ],
+ "id": 2,
+ "subnet": "192.0.3.0/24",
+ "reservations": [
+ {
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "client-classes": [ "VoIP" ]
+ } ],
+ "interface": "eth0",
+ "require-client-classes": [ "second_subnet" ]
+ },
+
+// The following list defines a subnet with pools. For some pools
+// we defined a class that is allowed in that pool. If not specified
+// everyone is allowed. When a class is specified, only packets belonging
+// to that class are allowed for that pool.
+ {
+ "pools": [
+ {
+// This one is for VoIP devices only.
+ "pool": "192.0.4.1 - 192.0.4.200",
+ "client-class": "VoIP"
+ },
+// This one doesn't have any client-class specified, so everyone
+// is allowed in.
+ {
+ "pool": "192.0.5.1 - 192.0.5.200"
+ } ],
+ "id": 3,
+ "subnet": "192.0.4.0/23",
+ "interface": "eth1"
+ },
+// This subnet is divided in two pools for unknown and known
+// (i.e. which have a reservation) clients. The built-in KNOWN and
+// UNKNOWN classes are set or not at host reservation lookup (KNOWN if
+// this returns something, UNKNOWN if this finds nothing) and client
+// classes depending on it are evaluated.
+// This happens after subnet selection and before address allocation
+// from pools.
+ {
+ "pools": [
+ {
+ "pool": "192.0.8.100 - 192.0.8.200",
+ "client-class": "UNKNOWN"
+ },
+ {
+ "pool": "192.0.9.100 - 192.0.9.200",
+ "client-class": "KNOWN"
+ }
+ ],
+ "id": 4,
+ "subnet": "192.0.8.0/23",
+ "reservations": [
+ { "hw-address": "00:00:00:11:22:33", "hostname": "h1" },
+ { "hw-address": "00:00:00:44:55:66", "hostname": "h4" },
+ { "hw-address": "00:00:00:77:88:99", "hostname": "h7" },
+ { "hw-address": "00:00:00:aa:bb:cc", "hostname": "ha" }
+ ]
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/comments.json b/doc/examples/kea4/comments.json
new file mode 100644
index 0000000..a5cfbdc
--- /dev/null
+++ b/doc/examples/kea4/comments.json
@@ -0,0 +1,113 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// It uses embedded (i.e., which will be included in configuration objects
+// and not stripped by at lexical analysis) comments.
+
+{ "Dhcp4":
+
+{
+ // Global scope
+ "comment": "A DHCPv4 server",
+
+ // In interface config
+ "interfaces-config": {
+ "comment": "Use wildcard",
+ "interfaces": [ "*" ] },
+
+ // In option definitions
+ "option-def": [ {
+ "comment": "An option definition",
+ "name": "foo",
+ "code": 100,
+ "type": "ipv4-address",
+ "space": "isc"
+ } ],
+
+ // In option data
+ "option-data": [ {
+ "comment": "Set option value",
+ "name": "dhcp-message",
+ "data": "ABCDEF0105",
+ "csv-format": false
+ } ],
+
+ // In client classes
+ "client-classes": [
+ {
+ "comment": "match all",
+ "name": "all",
+ "test": "'' == ''"
+ },
+ // Of course comments are optional
+ {
+ "name": "none"
+ },
+ // A comment and a user-context can be specified
+ {
+ "comment": "a comment",
+ "name": "both",
+ "user-context": {
+ "version": 1
+ }
+ }
+ ],
+
+ // In control socket (more for the agent)
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket",
+ "user-context": { "comment": "Indirect comment" }
+ },
+
+ // In shared networks
+ "shared-networks": [ {
+ "comment": "A shared network",
+ "name": "foo",
+
+ // In subnets
+ "subnet4": [
+ {
+ "comment": "A subnet",
+ "subnet": "192.0.1.0/24",
+ "id": 100,
+
+ // In pools
+ "pools": [
+ {
+ "comment": "A pool",
+ "pool": "192.0.1.1-192.0.1.10"
+ }
+ ],
+
+ // In host reservations
+ "reservations": [
+ {
+ "comment": "A host reservation",
+ "hw-address": "AA:BB:CC:DD:EE:FF",
+ "hostname": "foo.example.com",
+
+ // Again in an option data
+ "option-data": [ {
+ "comment": "An option in a reservation",
+ "name": "domain-name",
+ "data": "example.com"
+ } ]
+ }
+ ]
+ }
+ ]
+ } ],
+
+ // In dhcp ddns
+ "dhcp-ddns": {
+ "comment": "No dynamic DNS",
+ "enable-updates": false
+ },
+
+ // In loggers
+ "loggers": [ {
+ "comment": "A logger",
+ "name": "kea-dhcp4"
+ } ]
+}
+
+}
diff --git a/doc/examples/kea4/config-backend.json b/doc/examples/kea4/config-backend.json
new file mode 100644
index 0000000..82d36fb
--- /dev/null
+++ b/doc/examples/kea4/config-backend.json
@@ -0,0 +1,91 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// It demonstrates how to enable Kea Configuration Backend using MySQL.
+// It requires that libdhcp_mysql_cb.so library is available and
+// optionally libdhcp_cb_cmds.so hook library.
+
+{ "Dhcp4":
+
+{
+ // Set the server tag for the configuration backend. This instance will
+ // be named server1. Every configuration element that is applicable to
+ // either "all" or "server1" will be used by this instance.
+ "server-tag": "server1",
+
+ // Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+ // Use memfile lease database backend.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+ // This parameter controls how the server accesses the configuration
+ // database. Currently only one database type is available - "mysql".
+ // It requires that the libdhcp_mysql_cb.so hook library is loaded.
+ "config-control": {
+ // A list of database backends to connect to. Currently, it is limited
+ // to a single backend.
+ "config-databases": [
+ {
+ "type": "mysql",
+ "reconnect-wait-time": 3000, // expressed in ms
+ "max-reconnect-tries": 3,
+ "name": "kea",
+ "user": "kea",
+ "password": "kea",
+ "host": "localhost",
+ "port": 3306
+ }
+ ],
+ // Controls how often the server polls the database for the
+ // configuration updates. The setting below implies that it
+ // will take up to approx. 20 seconds for the server to
+ // discover and fetch configuration changes.
+ "config-fetch-wait-time": 20
+ },
+
+ // This defines a control socket. If defined, Kea will open a UNIX socket
+ // and will listen for incoming commands. See section 17 of the Kea ARM for
+ // details.
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Hooks libraries that enable configuration backend are loaded.
+ "hooks-libraries": [
+ // The libdhcp_mysql_cb.so is required to use MySQL Configuration
+ // Backend.
+ {
+ "library": "/usr/local/lib/kea/hooks/libdhcp_mysql_cb.so"
+ }
+ // The libdhcp_cb_cmds.so is optional. It allows for managing the
+ // configuration in the database. If this library is not loaded,
+ // the configuration can be managed directly using available
+ // tools that work directly with the MySQL database.
+ // ,{
+ // "library": "/usr/local/lib/kea/hooks/libdhcp_cb_cmds.so"
+ // }
+ ],
+
+ // The following configures logging. It assumes that messages with at
+ // least informational level (info, warn, error and fatal) should be
+ // logged to stdout. Alternatively, you can specify stderr here, a filename
+ // or 'syslog', which will store output messages via syslog.
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/dhcpv4-over-dhcpv6.json b/doc/examples/kea4/dhcpv4-over-dhcpv6.json
new file mode 100644
index 0000000..c622c42
--- /dev/null
+++ b/doc/examples/kea4/dhcpv4-over-dhcpv6.json
@@ -0,0 +1,48 @@
+// This is an example configuration file for the DHCPv4 server of
+// DHCPv4-over-DHCPv6 tests in Kea.
+
+{
+
+// DHCPv4 conf
+"Dhcp4":
+{
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+ "lease-database": {
+ "type": "memfile",
+ "name": "/tmp/kea-dhcp4.csv",
+ "lfc-interval": 3600
+ },
+
+ "valid-lifetime": 4000,
+
+ "subnet4": [
+ {
+ "id": 100,
+ "subnet": "10.10.10.0/24",
+// Don't forget the "4o6-" before "interface" here!
+ "4o6-interface": "eth0",
+ "4o6-subnet": "2001:db8:1:1::/64",
+ "pools": [ { "pool": "10.10.10.100 - 10.10.10.199" } ] }
+ ],
+
+// This enables DHCPv4-over-DHCPv6 support
+ "dhcp4o6-port": 6767,
+
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "/tmp/kea-dhcp4.log"
+ }
+ ],
+ "severity": "DEBUG",
+ "debuglevel": 0
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/global-reservations.json b/doc/examples/kea4/global-reservations.json
new file mode 100644
index 0000000..37cba50
--- /dev/null
+++ b/doc/examples/kea4/global-reservations.json
@@ -0,0 +1,178 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// It demonstrates how global host reservations can be configured.
+// The global reservations are not associated with any subnet. They
+// are assigned regardless of the subnet to which the DHCP client belongs.
+// Global reservations are assigned to the DHCP clients using the
+// same host identifier types as subnet specific reservations. This file
+// contains multiple examples of host reservations using different
+// identifier types, e.g. MAC address, client identifier etc.
+{ "Dhcp4":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of June
+// 2022, three database backends are supported: MySQL, PostgreSQL and
+// the in-memory database, Memfile. We'll use memfile because it doesn't
+// require any prior set up.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+// Addresses will be assigned with a lifetime of 4000 seconds.
+ "valid-lifetime": 4000,
+
+// Renew and rebind timers are commented out. This implies that options
+// 58 and 59 will not be sent to the client. In this case it is up to
+// the client to pick the timer values according to RFC2131. Uncomment the
+// timers to send these options to the client.
+// "renew-timer": 1000,
+// "rebind-timer": 2000,
+
+// Kea supports reservations by several different types of identifiers:
+// hw-address (hardware/MAC address of the client), duid (DUID inserted by the
+// client), client-id (client identifier inserted by the client), circuit-id
+// (circuit identifier inserted by the relay agent) and flex-id (flexible
+// identifier available when flex_id hook library is loaded). When told to do
+// so, Kea can check for all of those identifier types, but it takes a costly
+// database lookup to do so. It is therefore useful from a performance
+// perspective to use only the reservation types that are actually used in a
+// given network.
+
+// The example below is not optimal from a performance perspective, but it
+// nicely showcases the host reservation capabilities. Please use the minimum
+// set of identifier types used in your network.
+ "host-reservation-identifiers": [ "circuit-id", "hw-address", "duid",
+ "client-id", "flex-id" ],
+
+// This directive tells Kea that reservations are global. Note that this
+// can also be specified at shared network and/or subnet level.
+// "reservation-mode": "global",
+// It is replaced by the "reservations-global", "reservations-in-subnet", and
+// "reservations-out-of-pool" parameters.
+
+// Specify whether the server should look up global reservations.
+ "reservations-global": true,
+
+// Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": false,
+
+// Specify whether the server can assume that all reserved addresses
+// are out-of-pool.
+// Ignored when reservations-in-subnet is false.
+// If specified, it is inherited by "shared-networks" and "subnet4" levels.
+ "reservations-out-of-pool": false,
+
+// Define several global host reservations.
+ "reservations": [
+
+// This is a reservation for a specific hardware/MAC address. It's a very
+// simple reservation: just an address and nothing else.
+// Note it is not recommended but still allowed to reverse addresses at
+// the global scope: as it breaks the link between the reservation and
+// the subnet it can lead to a client localized in another subnet than
+// its address belongs to.
+ {
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "ip-address": "192.0.2.201"
+ },
+
+// This is a reservation for a specific client-id. It also shows
+// the this client will get a reserved hostname. A hostname can be defined
+// for any identifier type, not just client-id. Either a hostname or
+// an address is required.
+ {
+ "client-id": "01:11:22:33:44:55:66",
+ "hostname": "special-snowflake"
+ },
+
+// The third reservation is based on DUID. This reservation also
+// defines special option values for this particular client. If
+// the domain-name-servers option would have been defined on a global,
+// subnet or class level, the host specific values take precedence for
+// this particular DHCP client.
+ {
+ "duid": "01:02:03:04:05",
+ "ip-address": "192.0.2.203",
+ "option-data": [ {
+ "name": "domain-name-servers",
+ "data": "10.1.1.202,10.1.1.203"
+ } ]
+ },
+
+// The fourth reservation is based on circuit-id. This is an option inserted
+// by the relay agent that forwards the packet from client to the server.
+// In this example the host is also assigned vendor specific options.
+ {
+ "circuit-id": "01:11:22:33:44:55:66",
+ "ip-address": "192.0.2.204",
+ "option-data": [
+ {
+ "name": "vivso-suboptions",
+ "data": "4491"
+ },
+ {
+ "name": "tftp-servers",
+ "space": "vendor-4491",
+ "data": "10.1.1.202,10.1.1.203"
+ }
+ ]
+ },
+
+// This reservation is for a client that needs specific DHCPv4 fields to be
+// set. Three supported fields are next-server, server-hostname and
+// boot-file-name
+ {
+ "client-id": "01:0a:0b:0c:0d:0e:0f",
+ "ip-address": "192.0.2.205",
+ "next-server": "192.0.2.1",
+ "server-hostname": "hal9000",
+ "boot-file-name": "/dev/null"
+ },
+
+// This reservation is using flexible identifier. Instead of relying
+// on specific field, sysadmin can define an expression similar to what
+// is used for client classification,
+// e.g. substring(relay[0].option[17],0,6). Then, based on the value of
+// that expression for incoming packet, the reservation is matched.
+// Expression can be specified either as hex or plain text using single
+// quotes.
+// Note: flexible identifier requires flex_id hook library to be
+// loaded to work.
+ {
+ "flex-id": "'s0mEVaLue'",
+ "ip-address": "192.0.2.206"
+ }
+ ],
+
+ // Define a subnet.
+ "subnet4": [
+ {
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "interface": "eth0"
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json b/doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json
new file mode 100644
index 0000000..9191216
--- /dev/null
+++ b/doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json
@@ -0,0 +1,284 @@
+// This is an example configuration of the Kea DHCPv4 server. It uses High
+// Availability hook library and Lease Commands hook library to enable
+// High Availability function for the DHCP server. Note that almost exactly
+// the same configuration must be used on the second server (partner).
+// The only difference is that "this-server-name" must be set to "server2"
+// on this other server. Also, the interface configuration and location of TLS
+// specific files depend on the network settings and configuration of the
+// particular machine.
+//
+// The servers using this configuration work in load balancing mode.
+{
+
+// DHCPv4 configuration starts here.
+"Dhcp4": {
+ // Add names of your network interfaces to listen on.
+ "interfaces-config": {
+ // The DHCPv4 server listens on this interface.
+ "interfaces": [ "eth0" ]
+ },
+
+ // Control socket is required for communication between the Control
+ // Agent and the DHCP server. High Availability with MT does not require
+ // Control Agent to be running because lease updates are sent over the
+ // RESTful API between the HA peers using the server dedicated listener.
+ // The Control Agent is used only to handle user commands.
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Multi-threading parameters.
+ "multi-threading": {
+ // By default, Kea processes packets on multiple threads if the hardware permits.
+ "enable-multi-threading": true,
+
+ // When multi-threading is enabled, Kea will process packets on a
+ // number of multiple threads configurable through this option.
+ "thread-pool-size": 4,
+
+ // When multi-threading is enabled, Kea will read packets from the
+ // interface and append a working item to the thread pool. This
+ // option configures the maximum number of items that can be queued.
+ "packet-queue-size": 64
+ },
+
+ // Use Memfile lease database backend to store leases in a CSV file.
+ // Depending on how Kea was compiled, it may also support SQL databases
+ // (MySQL and/or PostgreSQL). Those database backends require more
+ // parameters, like name, host and possibly user and password.
+ // There are dedicated examples for each backend. See Section 7.2.2 "Lease
+ // Storage" for details.
+ "lease-database": {
+ // Memfile is the simplest and easiest backend to use. It's an in-memory
+ "type": "memfile"
+ },
+
+ // Client classes will associate address pools with certain servers taking
+ // part in providing High Availability.
+ "client-classes": [
+ // phones class
+ {
+ "name": "phones",
+ "test": "substring(option[60].hex,0,6) == 'Aastra'"
+ },
+ // laptops are everything but phones.
+ {
+ "name": "laptops",
+ "test": "not member('phones')"
+ },
+ // Some phones will be handled by server1. Whether the HA_server1
+ // or HA_server2 is assigned for the client is a matter of load
+ // balancing performed by the HA hook library.
+ {
+ "name": "phones_server1",
+ "test": "member('phones') and member('HA_server1')"
+ },
+ // Some phones will be handled by server2.
+ {
+ "name": "phones_server2",
+ "test": "member('phones') and member('HA_server2')"
+ },
+ // Some laptops will be handled by server1.
+ {
+ "name": "laptops_server1",
+ "test": "member('laptops') and member('HA_server1')"
+ },
+ // Some laptops will be handled by server2.
+ {
+ "name": "laptops_server2",
+ "test": "member('laptops') and member('HA_server2')"
+ }
+ ],
+
+ // HA requires two hook libraries to be loaded: libdhcp_lease_cmds.so and
+ // libdhcp_ha.so. The former handles incoming lease updates from the HA peers.
+ // The latter implements high availability feature for Kea.
+ "hooks-libraries": [
+ // The lease_cmds library must be loaded because HA makes use of it to
+ // deliver lease updates to the server as well as synchronize the
+ // lease database after failure.
+ {
+ "library": "/opt/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ },
+ {
+ // The HA hook library should be loaded.
+ "library": "/opt/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ // High Availability configuration is specified for the HA hook library.
+ // Each server should have the same HA configuration, except for the
+ // "this-server-name" parameter.
+ "high-availability": [ {
+ // This parameter points to this server instance. The respective
+ // HA peers must have this parameter set to their own names.
+ "this-server-name": "server1",
+ // The HA mode is set to load-balancing. In this mode, the active
+ // servers share the traffic (50/50).
+ "mode": "load-balancing",
+ // Heartbeat is to be sent every 10 seconds if no other control
+ // commands are transmitted.
+ "heartbeat-delay": 10000,
+ // Maximum time for partner's response to a heartbeat, after which
+ // failure detection is started. This is specified in milliseconds.
+ "max-response-delay": 60000,
+ // The following parameters control how the server detects the
+ // partner's failure. The ACK delay sets the threshold for the
+ // 'secs' field of the received discovers. This is specified in
+ // milliseconds.
+ "max-ack-delay": 5000,
+ // This specifies the number of clients which send messages to
+ // the partner but appear to not receive any response.
+ "max-unacked-clients": 5,
+ // This specifies the maximum timeout (in milliseconds) for the server
+ // to complete sync. If you have a large deployment (high tens or
+ // hundreds of thausands of clients), you may need to increase it
+ // further. The default value is 60000ms (60 seconds).
+ "sync-timeout": 60000,
+ // To not experience performance degradation when the Kea server is
+ // processing packets on multiple threads, the High Availability module
+ // must have multi-threading enabled.
+ "multi-threading": {
+ // Enable High Availability to benefit from multi-threading. Default: true.
+ "enable-multi-threading": true,
+ // When running in MT mode, the dedicated listener is used to handle
+ // lease updates.
+ "http-dedicated-listener": true,
+ // The number of threads used to handle incoming requests.
+ // A value of 0 instructs the server to use the same number of
+ // threads that the Kea core is using for DHCP multi-threading.
+ "http-listener-threads": 0,
+ // The number of threads used to handle outgoing requests.
+ // A value of 0 instructs the server to use the same number of
+ // threads that the Kea core is using for DHCP multi-threading.
+ "http-client-threads": 0
+ },
+ "peers": [
+ // This is the configuration of this server instance.
+ {
+ "name": "server1",
+ // This specifies the URL of this server instance. The
+ // Control Agent is not required to run along with this DHCPv4 server
+ // instance if multi-threading is enabled.
+ // The "http-host" and "http-port" values must be set to different
+ // values then the ones used by the Control Agent.
+ "url": "http://192.168.56.33:8000/",
+ // Trust anchor aka certificate authority file or directory.
+ "trust-anchor": "/usr/lib/kea/CA.pem",
+ // Client certificate file name.
+ "cert-file": "/usr/lib/kea/server1_cert.pem",
+ // Private key file name.
+ "key-file": "/usr/lib/kea/server1_key.pem",
+ // Client certificates are required and verified.
+ "require-client-certs": true,
+ // This server is primary. The other one must be
+ // secondary.
+ "role": "primary"
+ },
+ // This is the configuration of the HA peer.
+ {
+ "name": "server2",
+ // Specifies the URL on which the partner's control
+ // channel can be reached. The Control Agent is not required
+ // to run on the partner's machine if multi-threading is enabled.
+ // The "http-host" and "http-port" values must be set to different
+ // values then the ones used by the Control Agent.
+ "url": "http://192.168.56.66:8000/",
+ // Trust anchor aka certificate authority file or directory.
+ "trust-anchor": "/usr/lib/kea/CA.pem",
+ // Client certificate file name.
+ "cert-file": "/usr/lib/kea/server2_cert.pem",
+ // Private key file name.
+ "key-file": "/usr/lib/kea/server2_key.pem",
+ // Client certificates are required and verified.
+ "require-client-certs": true,
+ // The partner is secondary. This server is primary.
+ "role": "secondary"
+ }
+ ]
+ } ]
+ }
+ }
+ ],
+
+ // This example contains a single subnet declaration.
+ "subnet4": [
+ {
+ // Subnet id.
+ "id": 1,
+
+ // Subnet prefix.
+ "subnet": "192.0.3.0/24",
+
+ // Specify four address pools.
+ "pools": [
+ {
+ "pool": "192.0.3.100 - 192.0.3.125",
+ "client-class": "phones_server1"
+ },
+ {
+ "pool": "192.0.3.126 - 192.0.3.150",
+ "client-class": "laptops_server1"
+ },
+ {
+ "pool": "192.0.3.200 - 192.0.3.225",
+ "client-class": "phones_server2"
+ },
+ {
+ "pool": "192.0.3.226 - 192.0.3.250",
+ "client-class": "laptops_server2"
+ }
+ ],
+
+ // These are options that are subnet specific. In most cases,
+ // you need to define at least routers option, as without this
+ // option your clients will not be able to reach their default
+ // gateway and will not have Internet connectivity.
+ "option-data": [
+ {
+ // For each IPv4 subnet you most likely need to specify at
+ // least one router.
+ "name": "routers",
+ "data": "192.0.3.1"
+ }
+ ],
+
+ // This subnet will be selected for queries coming from the following
+ // IP address.
+ "relay": { "ip-address": "192.168.56.1" }
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout. Alternatively, you can specify stderr here, a filename
+// or 'syslog', which will store output messages via syslog.
+ "loggers": [
+ {
+ // This section affects kea-dhcp4, which is the base logger for DHCPv4
+ // component. It tells DHCPv4 server to write all log messages (on
+ // severity INFO or more) to a file.
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0
+ },
+ {
+ // This section specifies configuration of the HA hook library-specific
+ // logger.
+ "name": "kea-dhcp4.ha-hooks",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 99
+ }
+ ]
+}
+}
diff --git a/doc/examples/kea4/ha-load-balancing-server2-mt.json b/doc/examples/kea4/ha-load-balancing-server2-mt.json
new file mode 100644
index 0000000..ab31cfa
--- /dev/null
+++ b/doc/examples/kea4/ha-load-balancing-server2-mt.json
@@ -0,0 +1,267 @@
+// This is an example configuration of the Kea DHCPv4 server. It uses High
+// Availability hook library and Lease Commands hook library to enable
+// High Availability function for the DHCP server. Note that almost exactly
+// the same configuration must be used on the second server (partner).
+// The only difference is that "this-server-name" must be set to "server1"
+// on this other server. Also, the interface configuration depends on the
+// network settings of the particular machine.
+//
+// The servers using this configuration work in load balancing mode.
+{
+
+// DHCPv4 configuration starts here.
+"Dhcp4": {
+ // Add names of your network interfaces to listen on.
+ "interfaces-config": {
+ // The DHCPv4 server listens on this interface.
+ "interfaces": [ "eth0" ]
+ },
+
+ // Control socket is required for communication between the Control
+ // Agent and the DHCP server. High Availability with MT does not require
+ // Control Agent to be running because lease updates are sent over the
+ // RESTful API between the HA peers using the server dedicated listener.
+ // The Control Agent is used only to handle user commands.
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Multi-threading parameters.
+ "multi-threading": {
+ // By default, Kea processes packets on multiple threads if the hardware permits.
+ "enable-multi-threading": true,
+
+ // When multi-threading is enabled, Kea will process packets on a
+ // number of multiple threads configurable through this option.
+ "thread-pool-size": 4,
+
+ // When multi-threading is enabled, Kea will read packets from the
+ // interface and append a working item to the thread pool. This
+ // option configures the maximum number of items that can be queued.
+ "packet-queue-size": 64
+ },
+
+ // Use Memfile lease database backend to store leases in a CSV file.
+ // Depending on how Kea was compiled, it may also support SQL databases
+ // (MySQL and/or PostgreSQL). Those database backends require more
+ // parameters, like name, host and possibly user and password.
+ // There are dedicated examples for each backend. See Section 7.2.2 "Lease
+ // Storage" for details.
+ "lease-database": {
+ // Memfile is the simplest and easiest backend to use. It's an in-memory
+ "type": "memfile"
+ },
+
+ // Client classes will associate address pools with certain servers taking
+ // part in providing High Availability.
+ "client-classes": [
+ // phones class
+ {
+ "name": "phones",
+ "test": "substring(option[60].hex,0,6) == 'Aastra'"
+ },
+ // laptops are everything but phones.
+ {
+ "name": "laptops",
+ "test": "not member('phones')"
+ },
+ // Some phones will be handled by server1. Whether the HA_server1
+ // or HA_server2 is assigned for the client is a matter of load
+ // balancing performed by the HA hook library.
+ {
+ "name": "phones_server1",
+ "test": "member('phones') and member('HA_server1')"
+ },
+ // Some phones will be handled by server2.
+ {
+ "name": "phones_server2",
+ "test": "member('phones') and member('HA_server2')"
+ },
+ // Some laptops will be handled by server1.
+ {
+ "name": "laptops_server1",
+ "test": "member('laptops') and member('HA_server1')"
+ },
+ // Some laptops will be handled by server2.
+ {
+ "name": "laptops_server2",
+ "test": "member('laptops') and member('HA_server2')"
+ }
+ ],
+
+ // HA requires two hook libraries to be loaded: libdhcp_lease_cmds.so and
+ // libdhcp_ha.so. The former handles incoming lease updates from the HA peers.
+ // The latter implements high availability feature for Kea.
+ "hooks-libraries": [
+ // The lease_cmds library must be loaded because HA makes use of it to
+ // deliver lease updates to the server as well as synchronize the
+ // lease database after failure.
+ {
+ "library": "/opt/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ },
+ {
+ // The HA hook library should be loaded.
+ "library": "/opt/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ // High Availability configuration is specified for the HA hook library.
+ // Each server should have the same HA configuration, except for the
+ // "this-server-name" parameter.
+ "high-availability": [ {
+ // This parameter points to this server instance. The respective
+ // HA peers must have this parameter set to their own names.
+ "this-server-name": "server2",
+ // The HA mode is set to load-balancing. In this mode, the active
+ // servers share the traffic (50/50).
+ "mode": "load-balancing",
+ // Heartbeat is to be sent every 10 seconds if no other control
+ // commands are transmitted.
+ "heartbeat-delay": 10000,
+ // Maximum time for partner's response to a heartbeat, after which
+ // failure detection is started. This is specified in milliseconds.
+ "max-response-delay": 60000,
+ // The following parameters control how the server detects the
+ // partner's failure. The ACK delay sets the threshold for the
+ // 'secs' field of the received discovers. This is specified in
+ // milliseconds.
+ "max-ack-delay": 5000,
+ // This specifies the number of clients which send messages to
+ // the partner but appear to not receive any response.
+ "max-unacked-clients": 5,
+ // This specifies the maximum timeout (in milliseconds) for the server
+ // to complete sync. If you have a large deployment (high tens or
+ // hundreds of thausands of clients), you may need to increase it
+ // further. The default value is 60000ms (60 seconds).
+ "sync-timeout": 60000,
+ // To not experience performance degradation when the Kea server is
+ // processing packets on multiple threads, the High Availability module
+ // must have multi-threading enabled.
+ "multi-threading": {
+ // Enable High Availability to benefit from multi-threading. Default: true.
+ "enable-multi-threading": true,
+ // When running in MT mode, the dedicated listener is used to handle
+ // lease updates.
+ "http-dedicated-listener": true,
+ // The number of threads used to handle incoming requests.
+ // A value of 0 instructs the server to use the same number of
+ // threads that the Kea core is using for DHCP multi-threading.
+ "http-listener-threads": 0,
+ // The number of threads used to handle outgoing requests.
+ // A value of 0 instructs the server to use the same number of
+ // threads that the Kea core is using for DHCP multi-threading.
+ "http-client-threads": 0
+ },
+ "peers": [
+ // This is the configuration of the HA peer.
+ {
+ "name": "server1",
+ // Specifies the URL on which the partner's control
+ // channel can be reached. The Control Agent is not required
+ // to run on the partner's machine if multi-threading is enabled.
+ // The "http-host" and "http-port" values must be set to different
+ // values then the ones used by the Control Agent.
+ "url": "http://192.168.56.33:8000/",
+ // The partner is primary. This server is secondary.
+ "role": "primary"
+ },
+ // This is the configuration of this server instance.
+ {
+ "name": "server2",
+ // This specifies the URL of this server instance. The
+ // Control Agent is not required to run along with this DHCPv4 server
+ // instance if multi-threading is enabled.
+ // The "http-host" and "http-port" values must be set to different
+ // values then the ones used by the Control Agent.
+ "url": "http://192.168.56.66:8000/",
+ // This server is secondary. The other one must be
+ // primary.
+ "role": "secondary"
+ }
+ ]
+ } ]
+ }
+ }
+ ],
+
+ // This example contains a single subnet declaration.
+ "subnet4": [
+ {
+ // Subnet id.
+ "id": 1,
+
+ // Subnet prefix.
+ "subnet": "192.0.3.0/24",
+
+ // Specify four address pools.
+ "pools": [
+ {
+ "pool": "192.0.3.100 - 192.0.3.125",
+ "client-class": "phones_server1"
+ },
+ {
+ "pool": "192.0.3.126 - 192.0.3.150",
+ "client-class": "laptops_server1"
+ },
+ {
+ "pool": "192.0.3.200 - 192.0.3.225",
+ "client-class": "phones_server2"
+ },
+ {
+ "pool": "192.0.3.226 - 192.0.3.250",
+ "client-class": "laptops_server2"
+ }
+ ],
+
+ // These are options that are subnet specific. In most cases,
+ // you need to define at least routers option, as without this
+ // option your clients will not be able to reach their default
+ // gateway and will not have Internet connectivity.
+ "option-data": [
+ {
+ // For each IPv4 subnet you most likely need to specify at
+ // least one router.
+ "name": "routers",
+ "data": "192.0.3.1"
+ }
+ ],
+
+ // This subnet will be selected for queries coming from the following
+ // IP address.
+ "relay": { "ip-address": "192.168.56.1" }
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout. Alternatively, you can specify stderr here, a filename
+// or 'syslog', which will store output messages via syslog.
+ "loggers": [
+ {
+ // This section affects kea-dhcp4, which is the base logger for DHCPv4
+ // component. It tells DHCPv4 server to write all log messages (on
+ // severity INFO or more) to a file.
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0
+ },
+ {
+ // This section specifies configuration of the HA hook library-specific
+ // logger.
+ "name": "kea-dhcp4.ha-hooks",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 99
+ }
+ ]
+}
+}
diff --git a/doc/examples/kea4/hooks-radius.json b/doc/examples/kea4/hooks-radius.json
new file mode 100644
index 0000000..164e6f8
--- /dev/null
+++ b/doc/examples/kea4/hooks-radius.json
@@ -0,0 +1,224 @@
+// This is an example configuration file for the DHCPv4 server in Kea
+// illustrating the configuration of the RADIUS and Host Cache hook libraries.
+//
+// It is not intended to be used as is. It tries to showcase some of the
+// parameters available.
+//
+// To use this configuration file, you need to have both RADIUS and
+// Host Cache hooks. These are currently available to support customers only.
+//
+// clients get a wine name (option AOP code 250) divided into red and white.
+// Expensive brands have a host entry, i.e. a reserved address.
+//
+// Names
+//
+// brouilly (red)
+// chablis (white)
+// chambertin (red, expensive)
+// chinon (red)
+// chiroubles (red)
+// condrieu (white)
+// cornas (red)
+// corton (red)
+// fleurie (red)
+// givry (red)
+// margaux (red, expensive)
+// meursault (white)
+// montrachet (white, expensive)
+// morgon (red)
+// muscadet (white)
+// petrus (red, expensive)
+// riesling (white)
+// romanee (red, expensive)
+// sylvaner (white)
+// yquem (white, expensive)
+//
+// Address space is 192.0.2.0/24 with 10-99 for reds and 110-199 for whites.
+//
+// Reservations are given here in Kea/JSON style but they must be
+// in the RADIUS server configuration:
+//
+// {
+// "flex-id": "'chambertin'",
+// "ip-address": "192.0.2.10"
+// },
+// {
+// "flex-id": "'margaux'",
+// "ip-address": "192.0.2.11"
+// },
+// {
+// "flex-id": "'petrus'",
+// "ip-address": "192.0.2.12"
+// },
+// {
+// "flex-id": "'romanee'",
+// "ip-address": "192.0.2.13"
+// },
+// {
+// "flex-id": "'montrachet'",
+// "ip-address": "192.0.2.110"
+// },
+// {
+// "flex-id": "'yquem'",
+// "ip-address": "192.0.2.111"
+// }
+//
+
+{"Dhcp4":
+
+{
+ // Kea is told to listen on specific interfaces only.
+ "interfaces-config": {
+ // You should probably list your network interfaces here (e.g. "eth1961")
+ "interfaces": [ "eth1961" ]
+ },
+
+ // Set up the storage for leases.
+ "lease-database": {
+ "type": "memfile"
+ },
+
+ // Note there is hosts-database defined. RADIUS and Host Cache libraries
+ // will create them dynamically.
+
+ // RADIUS uses flex-id reservations, so restrict Kea to use flex-id only.
+ "host-reservation-identifiers": [ "flex-id" ],
+
+ // Define the AOP option.
+ "option-def": [ {
+ "name": "AOP",
+ "code": 250,
+ "type": "string" } ],
+
+ // Define red and white client classes.
+ // If they are not defined we can get spurious warnings.
+ "client-classes": [
+ { "name": "red" },
+ { "name": "white" } ],
+
+ // Define a subnet.
+ "subnet4": [ {
+ // Set the subnet ID (aka RADIUS NAS port).
+ "id": 14,
+ "subnet": "192.0.2.0/24",
+ "interface": "eth1961",
+ "pools": [
+ {
+ // Red pool (10-19 are for reservations)
+ "pool": "192.0.2.20-192.0.2.99",
+ "client-class": "red"
+ },
+ {
+ // White pool (110-119 are for reservations)
+ "pool": "192.0.2.120-192.0.2.199",
+ "client-class": "white"
+ }
+
+ // Note there are not pools available to anyone. This is
+ // important to note. This means that to get an address, the
+ // client needs to belong to red class, to white class or
+ // have an address reserved.
+ ]
+ } ],
+
+ // Set up the hook libraries.
+ "hooks-libraries": [
+ {
+ // Load the flex-id hook library.
+ "library": "/usr/local/lib/kea/hooks/libdhcp_flex_id.so",
+
+ "parameters": {
+ // Take the ID from the AOP option.
+ "identifier-expression": "option[250].text",
+
+ // Replace the client ID in queries by the flex-id.
+ // Currently required by access code.
+ // Required for accounting as it will become the lease ID too.
+ "replace-client-id": true
+ }
+ },
+ {
+ // Load the host cache hook library. It is needed by the RADIUS
+ // library to keep the attributes from authorization to later user
+ // for accounting.
+ "library": "/usr/local/lib/kea/hooks/libdhcp_host_cache.so"
+ },
+ {
+ // Load the RADIUS hook library.
+ "library": "/usr/local/lib/kea/hooks/libdhcp_radius.so",
+
+ "parameters": {
+ // If do not use RFC 4361
+ // "extract-duid": false,
+
+ // If have conflicting subnets
+ // "reselect-subnet-pool": true,
+
+ // Strip the 0 type added by flex-id
+ "client-id-pop0": true,
+
+ // flex Id is printable (far easier for the RADIUS server config)
+ // Without this it will be in hexadecimal...
+ "client-id-printable": true,
+
+ // Use the flex-id.
+ "identifier-type4": "flex-id",
+
+ // Configure an access (aka authentication/authorization) server.
+ "access": {
+
+ // This starts the list of access servers
+ "servers": [
+ {
+ // These are parameters for the first (and only) access server
+ "name": "127.0.0.1",
+ "port": 1812,
+ "secret": "secret"
+ }
+ // Additional access servers could be specified here
+ ],
+
+ // This define a list of additional attributes Kea will send to each
+ // access server in Access-Request.
+ "attributes": [
+ {
+ // This attribute is identified by name (must be present in the
+ // dictionary) and has static value (i.e. the same value will be
+ // sent to every server for every packet)
+ "name": "Password",
+ "data": "mysecretpassword"
+ },
+ {
+ // It's also possible to specify an attribute using its type,
+ // rather than a name. 77 is Connect-Info. The value is specified
+ // using hex. Again, this is a static value. It will be sent the
+ // same for every packet and to every server.
+ "type": 77,
+ "raw": "65666a6a71"
+ },
+ {
+ // This example shows how an expression can be used to send dynamic
+ // value. The expression (see Section 13) may take any value from
+ // the incoming packet or even its metadata (e.g. the interface
+ // it was received over from)
+ "name": "Configuration-Token",
+ "expr": "pkt.iface"
+ }
+ ] // End of attributes
+ },
+
+ // Configure an accounting server.
+ "accounting": {
+ "servers": [ {
+ "name": "127.0.0.1",
+ "port": 1813,
+ "secret": "secret"
+ }
+ ]
+ }
+ }
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/hooks.json b/doc/examples/kea4/hooks.json
new file mode 100644
index 0000000..d82db2b
--- /dev/null
+++ b/doc/examples/kea4/hooks.json
@@ -0,0 +1,50 @@
+// This is an example configuration file for the DHCPv4 server in Kea
+// illustrating the configuration of hook libraries. It uses a basic scenario
+// of one IPv4 subnet configured with the default values for all parameters.
+
+{"Dhcp4":
+
+{
+// Kea is told to listen on the eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// Set up the storage for leases.
+ "lease-database": {
+ "type": "memfile"
+ },
+
+ "valid-lifetime": 1800,
+
+// Define a single subnet.
+ "subnet4": [
+ {
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "interface": "eth0"
+ }
+ ],
+
+// Set up the hook libraries. For this example, we assume that two libraries
+// are loaded, called "security" and "charging". Note that order is important:
+// "security" is specified first so if both libraries supply a hook function
+// for a given hook, the function in "security" will be called before that in
+// "charging".
+
+ "hooks-libraries": [
+ {
+ "library": "/opt/lib/security.so"
+ },
+ {
+ "library": "/opt/lib/charging.so",
+ "parameters": {
+ "path": "/var/lib/kea",
+ "base-name": "kea-forensic6"
+ }
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/leases-expiration.json b/doc/examples/kea4/leases-expiration.json
new file mode 100644
index 0000000..427eb40
--- /dev/null
+++ b/doc/examples/kea4/leases-expiration.json
@@ -0,0 +1,76 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// It provides parameters controlling processing of expired leases,
+// a.k.a. leases reclamation.
+
+{ "Dhcp4":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+// Note, we're setting the maximum number of row read errors to 100,
+// (defaults to 0, meaning unlimited).
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600,
+ "max-row-errors": 100
+ },
+
+// The following parameters control processing expired leases. Expired
+// leases will be reclaimed periodically according to the
+// "reclaim-timer-wait-time" parameter. Reclaimed leases will be held in
+// the database for 1800s to facilitate lease affinity. After this
+// period the leases will be removed. The frequency of removal is
+// controlled by the "flush-reclaimed-timer-wait-time" parameter. The
+// lease reclamation routine will process at most 500 leases or will
+// last for at most 100ms, during a single run. If there are still some
+// unreclaimed leases after 10 attempts, a warning message is issued.
+// If both "flush-reclaimed-timer-wait-time" and "hold-reclaimed-time" are not
+// 0, when the client sends a release message the lease is expired instead of
+// being deleted from lease storage.
+ "expired-leases-processing": {
+ "reclaim-timer-wait-time": 5,
+ "hold-reclaimed-time": 1800,
+ "flush-reclaimed-timer-wait-time": 10,
+ "max-reclaim-leases": 500,
+ "max-reclaim-time": 100,
+ "unwarned-reclaim-cycles": 10
+ },
+
+// Addresses will be assigned with a lifetime of 4000 seconds.
+ "valid-lifetime": 4000,
+
+// The following list defines subnets. We have only one subnet
+// here. We tell Kea that it is directly available over local interface.
+ "subnet4": [
+ {
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "interface": "eth0"
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/multiple-options.json b/doc/examples/kea4/multiple-options.json
new file mode 100644
index 0000000..d8495b0
--- /dev/null
+++ b/doc/examples/kea4/multiple-options.json
@@ -0,0 +1,187 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// It demonstrates simple configuration of the options for a subnet.
+
+{ "Dhcp4":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile"
+ },
+
+// Addresses will be assigned with a lifetime of 4000 seconds.
+ "valid-lifetime": 4000,
+
+// Renew and rebind timers are commented out. This implies that options
+// 58 and 59 will not be sent to the client. In this case it is up to
+// the client to pick the timer values according to RFC2131. Uncomment the
+// timers to send these options to the client.
+// "renew-timer": 1000,
+// "rebind-timer": 2000,
+
+// Defining a subnet. There are some DHCP options returned to the
+// clients connected to this subnet. The first and third options are
+// clients connected to this subnet. The first two options are
+// identified by the name. The third option is identified by the
+// option code.
+// There is an address pool defined within this subnet. Pool
+// specific value for option domain-name-servers is defined
+// for the pool.
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "option-data": [
+ // When specifying options, you typically need to specify
+ // one of (name or code) and data. The full option specification
+ // covers name, code, space, csv-format and data.
+ // space defaults to "dhcp4" which is usually correct, unless you
+ // use encapsulate options. csv-format defaults to "true", so
+ // this is also correct, unless you want to specify the whole
+ // option value as long hex string. For example, to specify
+ // domain-name-servers you could do this:
+ // {
+ // "name": "domain-name-servers",
+ // "code": 6,
+ // "csv-format": true,
+ // "space": "dhcp4",
+ // "data": "192.0.2.1, 192.0.2.2"
+ // }
+ // but it's a lot of writing, so it's easier to do this instead:
+ {
+ "name": "domain-name-servers",
+ "data": "192.0.2.1, 192.0.2.2"
+ },
+ // Note the Kea provides some of the options on its own. In
+ // particular:
+
+ // - IP address lease time (option 51) is governed by
+ // valid-lifetime parameter, so you don't need to specify
+ // it as option.
+ // - Subnet mask (option 1) is calculated automatically from the
+ // subnet parameter specified for each "subnet4" entry.
+ // - renewal-timer (option 58) is calculated from renew-timer
+ // parameter
+ // - rebind timer (option 59) is calculated from rebind-timer
+ // parameter
+
+ // For each IPv4 subnet you most likely need to specify at least
+ // one router.
+ {
+ "name": "routers",
+ "data": "192.0.2.1"
+ },
+
+ // Typically people prefer to refer to options by their
+ // names, so they don't need to remember the code names.
+ // However, some people like to use numerical values. For
+ // example, option "domain-name" uses option code 15, so you
+ // can reference to it either by
+ // "name": "domain-name" or "code": 15.
+ {
+ "code": 15,
+ "data": "example.org"
+ },
+ // Domain search is also a popular option. It tells the client to
+ // attempt to resolve names within those specified domains. For
+ // example, name "foo" would be attempted to be resolved as
+ // foo.mydomain.example.com and if it fails, then as
+ // foo.example.com
+
+ {
+ "name": "domain-search",
+ "data": "mydomain.example.com, example.com"
+ },
+
+ // Options can also be specified using hexadecimal format.
+ // This should be avoided if possible, because Kea ability to
+ // validate correctness is limited when using hex values.
+ {
+ "name": "broadcast-address",
+ "csv-format": false,
+ "data": "ffff8000"
+ },
+
+ // String options that have a comma in their values need to have
+ // it escaped (i.e. each comma is preceded by two backslashes).
+ // That's because commas are reserved for separating fields in
+ // compound options. At the same time, we need to be conformant
+ // with JSON spec, that does not allow "\,". Therefore the
+ // slightly uncommon double backslashes notation is needed.
+
+ // Legal JSON escapes are \ followed by "\/bfnrt character
+ // or \u followed by 4 hexa-decimal numbers (currently Kea
+ // supports only \u0000 to \u00ff code points).
+ // CSV processing translates '\\' into '\' and '\,' into ','
+ // only so for instance '\x' is translated into '\x'. But
+ // as it works on a JSON string value each of these '\'
+ // characters must be doubled on JSON input.
+ {
+ "name": "boot-file-name",
+ "data": "EST5EDT4\\,M3.2.0/02:00\\,M11.1.0/02:00"
+
+ },
+ // Options that take integer values can either be specified in
+ // dec or hex format. Hex format could be either plain (e.g. abcd)
+ // or prefixed with 0x (e.g. 0xabcd).
+ {
+ "name": "default-ip-ttl",
+ "data": "0xf0"
+ },
+ // At a few exceptions options are added to response only when
+ // the client requests them. The always-send flag should be used
+ // to enforce a particular option.
+ {
+ "name": "vendor-class-identifier",
+ "data": "isc",
+ "always-send": true
+ }
+ ],
+
+ // Now we define pools. There are two pools here.
+ "pools": [ {
+ // This is the first pool. Nothing spectacular here, just a range
+ // of addresses.
+ "pool": "192.0.2.10 - 192.0.2.100"
+
+ }, {
+ // This second pool is more interesting. Anyone who gets an
+ // address from this pool will also get this specific option
+ // value if asks for DNS servers configuration. This value,
+ // being more specific, overrides any values that were specified
+ // on either global or subnet scope.
+ "pool": "192.0.2.101 - 192.0.2.200",
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "data": "192.0.2.3, 192.0.2.4"
+ }
+ ]
+ } ]
+ } ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/mysql-reservations.json b/doc/examples/kea4/mysql-reservations.json
new file mode 100644
index 0000000..e8c0c22
--- /dev/null
+++ b/doc/examples/kea4/mysql-reservations.json
@@ -0,0 +1,103 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// It contains configuration of the MySQL host database backend, used
+// to retrieve reserved addresses, host names, DHCPv4 message fields
+// and DHCP options from MySQL database.
+{ "Dhcp4":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+// Addresses will be assigned with a lifetime of 4000 seconds.
+ "valid-lifetime": 4000,
+
+// Renew and rebind timers are commented out. This implies that options
+// 58 and 59 will not be sent to the client. In this case it is up to
+// the client to pick the timer values according to RFC2131. Uncomment the
+// timers to send these options to the client.
+// "renew-timer": 1000,
+// "rebind-timer": 2000,
+
+
+// Kea supports reservations by several different types of
+// identifiers: hw-address (hardware/MAC address of the client), duid
+// (DUID inserted by the client), client-id (client identifier inserted
+// by the client) and circuit-id (circuit identifier inserted by the
+// relay agent). When told to do so, Kea can check for all of those
+// identifier types, but it takes a costly database lookup to do so. It
+// is therefore useful from a performance perspective to use only the
+// reservation types that are actually used in a given network.
+
+// The example below is not optimal from a performance perspective, but it
+// nicely showcases the host reservation capabilities. Please use the minimum
+// set of identifier types used in your network.
+ "host-reservation-identifiers":
+ [ "circuit-id", "hw-address", "duid", "client-id" ],
+
+// Specify connection to the database holding host reservations. The type
+// specifies that the MySQL database is used. user and password are the
+// credentials used to connect to the database. host and name specify
+// location of the host where the database instance is running, and the
+// name of the database to use. The server processing a packet will first
+// check if there are any reservations specified for this client in the
+// reservations list, within the subnet (configuration file). If there are
+// no reservations there, the server will try to retrieve reservations
+// from this database.
+ "hosts-database": {
+ "type": "mysql",
+ "reconnect-wait-time": 3000, // expressed in ms
+ "max-reconnect-tries": 3,
+ "name": "keatest",
+ "user": "keatest",
+ "password": "keatest",
+ "host": "localhost",
+ "port": 3306,
+ "trust-anchor": "my-ca",
+ "cert-file": "my-cert",
+ "key-file": "my-key",
+ "cipher-list": "AES"
+ },
+
+// Define a subnet with a single pool of dynamic addresses. Addresses from
+// this pool will be assigned to clients which don't have reservations in the
+// database. Subnet identifier is equal to 1. If this subnet is selected for
+// the client, this subnet id will be used to search for the reservations
+// within the database.
+ "subnet4": [
+ {
+ "pools": [ { "pool": "192.0.2.10 - 192.0.2.200" } ],
+ "subnet": "192.0.2.0/24",
+ "interface": "eth0",
+ "id": 1
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/pgsql-reservations.json b/doc/examples/kea4/pgsql-reservations.json
new file mode 100644
index 0000000..6941fd0
--- /dev/null
+++ b/doc/examples/kea4/pgsql-reservations.json
@@ -0,0 +1,101 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// It contains configuration of the PostgreSQL host database backend, used
+// to retrieve reserved addresses, host names, DHCPv4 message fields
+// and DHCP options from PostgreSQL database.
+{ "Dhcp4":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile"
+ },
+
+// Addresses will be assigned with a lifetime of 4000 seconds.
+ "valid-lifetime": 4000,
+
+// Renew and rebind timers are commented out. This implies that options
+// 58 and 59 will not be sent to the client. In this case it is up to
+// the client to pick the timer values according to RFC2131. Uncomment the
+// timers to send these options to the client.
+// "renew-timer": 1000,
+// "rebind-timer": 2000,
+
+
+// Kea supports reservations by several different types of
+// identifiers: hw-address (hardware/MAC address of the client), duid
+// (DUID inserted by the client), client-id (client identifier inserted
+// by the client) and circuit-id (circuit identifier inserted by the
+// relay agent). When told to do so, Kea can check for all of those
+// identifier types, but it takes a costly database lookup to do so. It
+// is therefore useful from a performance perspective to use only the
+// reservation types that are actually used in a given network.
+
+// The example below is not optimal from a performance perspective, but it
+// nicely showcases the host reservation capabilities. Please use the minimum
+// set of identifier types used in your network.
+ "host-reservation-identifiers":
+ [ "circuit-id", "hw-address", "duid", "client-id" ],
+
+// Specify connection to the database holding host reservations. The type
+// specifies that the PostgreSQL database is used. user and password are the
+// credentials used to connect to the database. host and name specify
+// location of the host where the database instance is running, and the
+// name of the database to use. The server processing a packet will first
+// check if there are any reservations specified for this client in the
+// reservations list, within the subnet (configuration file). If there are
+// no reservations there, the server will try to retrieve reservations
+// from this database.
+// The database specification can go into one hosts-database entry for
+// backward compatibility or be listed in hosts-databases list.
+ "hosts-databases": [
+ {
+ "type": "postgresql",
+ "reconnect-wait-time": 3000, // expressed in ms
+ "max-reconnect-tries": 3,
+ "name": "keatest",
+ "user": "keatest",
+ "password": "keatest",
+ "host": "localhost"
+ }
+ ],
+
+// Define a subnet with a single pool of dynamic addresses. Addresses from
+// this pool will be assigned to clients which don't have reservations in the
+// database. Subnet identifier is equal to 1. If this subnet is selected for
+// the client, this subnet id will be used to search for the reservations
+// within the database.
+ "subnet4": [
+ {
+ "pools": [ { "pool": "192.0.2.10 - 192.0.2.200" } ],
+ "subnet": "192.0.2.0/24",
+ "interface": "eth0",
+ "id": 1
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/reservations.json b/doc/examples/kea4/reservations.json
new file mode 100644
index 0000000..da44c1b
--- /dev/null
+++ b/doc/examples/kea4/reservations.json
@@ -0,0 +1,183 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// It contains one subnet in which there are two static address reservations
+// for the clients identified by the MAC addresses.
+{ "Dhcp4":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of April
+// 2022, three database backends are supported: MySQL, PostgreSQL, and the
+// in-memory database, Memfile. We'll use memfile because it doesn't
+// require any prior set up.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+// Addresses will be assigned with a lifetime of 4000 seconds.
+ "valid-lifetime": 4000,
+
+// Renew and rebind timers are commented out. This implies that options
+// 58 and 59 will not be sent to the client. In this case it is up to
+// the client to pick the timer values according to RFC2131. Uncomment the
+// timers to send these options to the client.
+// "renew-timer": 1000,
+// "rebind-timer": 2000,
+
+// Kea supports reservations by several different types of identifiers:
+// hw-address (hardware/MAC address of the client), duid (DUID inserted by the
+// client), client-id (client identifier inserted by the client), circuit-id
+// (circuit identifier inserted by the relay agent) and flex-id (flexible
+// identifier available when flex_id hook library is loaded). When told to do
+// so, Kea can check for all of those identifier types, but it takes a costly
+// database lookup to do so. It is therefore useful from a performance
+// perspective to use only the reservation types that are actually used in a
+// given network.
+
+// The example below is not optimal from a performance perspective, but it
+// nicely showcases the host reservation capabilities. Please use the minimum
+// set of identifier types used in your network.
+"host-reservation-identifiers": [ "circuit-id", "hw-address", "duid",
+ "client-id", "flex-id" ],
+
+// Define a subnet with four reservations. Some of the reservations belong
+// to the dynamic pool. Kea is able to handle this case, but it is not
+// recommended from a performance perspective, as Kea would not only need to
+// check if a given address is free, but also whether it is reserved.
+// To avoid this check, one can change reservation-mode to out-of-pool, rather
+// than 'all'. If a subnet does not have reservations at all, the reservation
+// lookup can be skipped altogether (reservation-mode is set to 'disabled').
+// The reservation-mode has been replaced by reservations-global,
+// reservations-in-subnet and reservations-out-of-pool.
+
+// Note that the second reservation is for an address which is within the
+// range of the pool of the dynamically allocated address. The server will
+// exclude this address from this pool and only assign it to the client which
+// has a reservation for it.
+ "subnet4": [
+ {
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "interface": "eth0",
+ // This directive tells Kea that reservations may be made both in-pool
+ // and out-of-pool. For improved performance, you may move all reservations
+ // out of the dynamic pool and change reservation-mode to "out-of-pool".
+ // Kea will then be able to skip querying for host reservations when
+ // assigning leases from dynamic pool.
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global", "reservations-in-subnet"
+ // and "reservations-out-of-pool" parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ // If specified, it is inherited by "shared-networks" and
+ // "subnet4" levels.
+ "reservations-out-of-pool": false,
+
+ "reservations": [
+
+// This is a reservation for a specific hardware/MAC address. It's a very
+// simple reservation: just an address and nothing else.
+ {
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "ip-address": "192.0.2.201"
+ },
+
+// This is a reservation for a specific client-id. It also shows
+// the this client will get a reserved hostname. A hostname can be defined
+// for any identifier type, not just client-id.
+ {
+ "client-id": "01:11:22:33:44:55:66",
+ "ip-address": "192.0.2.202",
+ "hostname": "special-snowflake"
+ },
+
+// The third reservation is based on DUID. This reservation also
+// defines special option values for this particular client. If
+// the domain-name-servers option would have been defined on a global,
+// subnet or class level, the host specific values take preference.
+ {
+ "duid": "01:02:03:04:05",
+ "ip-address": "192.0.2.203",
+ "option-data": [ {
+ "name": "domain-name-servers",
+ "data": "10.1.1.202,10.1.1.203"
+ } ]
+ },
+
+// The fourth reservation is based on circuit-id. This is an option inserted
+// by the relay agent that forwards the packet from client to the server.
+// In this example the host is also assigned vendor specific options.
+ {
+ "circuit-id": "01:11:22:33:44:55:66",
+ "ip-address": "192.0.2.204",
+ "option-data": [
+ {
+ "name": "vivso-suboptions",
+ "data": "4491"
+ },
+ {
+ "name": "tftp-servers",
+ "space": "vendor-4491",
+ "data": "10.1.1.202,10.1.1.203"
+ }
+ ]
+ },
+// This reservation is for a client that needs specific DHCPv4 fields to be
+// set. Three supported fields are next-server, server-hostname and
+// boot-file-name
+ {
+ "client-id": "01:0a:0b:0c:0d:0e:0f",
+ "ip-address": "192.0.2.205",
+ "next-server": "192.0.2.1",
+ "server-hostname": "hal9000",
+ "boot-file-name": "/dev/null"
+ },
+
+// This reservation is using flexible identifier. Instead of relying
+// on specific field, sysadmin can define an expression similar to what
+// is used for client classification,
+// e.g. substring(relay[0].option[17],0,6). Then, based on the value of
+// that expression for incoming packet, the reservation is matched.
+// Expression can be specified either as hex or plain text using single
+// quotes.
+// Note: flexible identifier requires flex_id hook library to be
+// loaded to work.
+ {
+ "flex-id": "'s0mEVaLue'",
+ "ip-address": "192.0.2.206"
+ }
+
+ ]
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/several-subnets.json b/doc/examples/kea4/several-subnets.json
new file mode 100644
index 0000000..6a9e1f5
--- /dev/null
+++ b/doc/examples/kea4/several-subnets.json
@@ -0,0 +1,86 @@
+// This is an example configuration file for DHCPv4 server in Kea.
+// It's a basic scenario with three IPv4 subnets configured. In each
+// subnet, there's a smaller pool of dynamic addresses.
+
+{ "Dhcp4":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile"
+ },
+
+// Addresses will be assigned with a lifetime of 4000 seconds.
+// The client is told to start renewing after 1000 seconds. If the server
+// does not respond within 2000 seconds of the lease being granted, client
+// is supposed to start REBIND procedure (emergency renewal that allows
+// switching to a different server).
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+// RFC6842 says that the server is supposed to echo back client-id option.
+// However, some older clients do not support this and are getting confused
+// when they get their own client-id. Kea can disable RFC6842 support.
+ "echo-client-id": false,
+
+// Some clients don't use stable client identifier, but rather generate them
+// during each boot. This may cause a client that reboots frequently to get
+// multiple leases, which may not be desirable. As such, sometimes admins
+// prefer to tell their DHCPv4 server to ignore client-id value altogether
+// and rely exclusively on MAC address. This is a parameter that is defined
+// globally, but can be overridden on a subnet level.
+ "match-client-id": true,
+
+ // By default, Kea ignores requests by clients for unknown IP addresses,
+ // because other non-cooperating DHCP servers could reside on the same
+ // network (RFC 2131). This parameter is defined globally, but can be
+ // overridden on a subnet level
+ "authoritative": false,
+
+// The following list defines subnets. Each subnet consists of at
+// least subnet and pool entries.
+ "subnet4": [
+ {
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "id": 1,
+ "subnet": "192.0.2.0/24"
+ },
+ {
+// This particular subnet has match-client-id value changed.
+ "pools": [ { "pool": "192.0.3.100 - 192.0.3.200" } ],
+ "id": 2,
+ "subnet": "192.0.3.0/24",
+ "match-client-id": false
+ },
+ {
+ "pools": [ { "pool": "192.0.4.1 - 192.0.4.254" } ],
+ "id": 3,
+ "subnet": "192.0.4.0/24"
+ } ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/shared-network.json b/doc/examples/kea4/shared-network.json
new file mode 100644
index 0000000..4b5a474
--- /dev/null
+++ b/doc/examples/kea4/shared-network.json
@@ -0,0 +1,164 @@
+// This is an example configuration file for DHCPv4 server in Kea.
+// It demonstrates an advanced feature called shared network. Typically, for
+// each physical link there is one IPv4 subnet that the server is expected
+// to manage. However, in some cases there is a need to configure more subnets
+// in the same physical location. The most common use case is an existing
+// subnet that grew past its original assumptions and ran out of addresses,
+// so the sysadmin needs to add another subnet on top of existing one.
+{
+ "Dhcp4": {
+
+ // As with any other configuration, you need to tell Kea the interface
+ // names, so it would listen to incoming traffic.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+ // You also need to tell where to store lease information.
+ // memfile is the backend that is easiest to set up.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+ // The shared networks definition starts here. shared-networks can
+ // contain a list of shared networks. There are many parameters
+ // that can be specified here, so this example may be overwhelming
+ // at first, but the only mandatory parameter for each shared
+ // network is name. It must be unique. Typically, each shared
+ // network also needs to have at least two subnets to be functional,
+ // but if you really want to, you can define a degraded shared
+ // network that has 1 or even 0 subnets. This may come in handy
+ // when migrating between regular subnets and shared networks
+ // or when debugging a problem. It is not recommended to use
+ // 1 subnet per shared network, as there is extra processing
+ // overhead for shared networks.
+ "shared-networks": [
+ {
+ // Name of the shared network. It may be an arbitrary string
+ // and it must be unique among all shared networks.
+ "name": "frog",
+
+ // You may specify interface name if the shared network is
+ // reachable directly from the server.
+ "interface": "eth1",
+
+ // You can specify many parameters that are allowed in subnet scope
+ // here. It's useful to put them here if they apply to all subnets
+ // in this shared network. It's likely that the most common
+ // parameter here will be option values defined with option-data.
+ "match-client-id": false,
+ "option-data": [ ],
+ "rebind-timer": 150,
+ "authoritative": true,
+
+ // If all the traffic coming from that shared network is reachable
+ // via relay and that relay always use the same IP address, you
+ // can specify that relay address here. Since this example shows
+ // a shared network reachable directly, we put 0.0.0.0 here.
+ // It would be better to skip the relay scope altogether, but
+ // it was left here for demonstration purposes.
+ "relay": {
+ "ip-address": "0.0.0.0"
+ },
+
+ // Timer values can be overridden here.
+ "renew-timer": 100,
+
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and "reservations-out-of-pool"
+ // parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ // If specified, it is inherited by "subnet4" levels.
+ "reservations-out-of-pool": false,
+
+ // This starts a list of subnets allowed in this shared network.
+ // In our example, there are two subnets.
+ "subnet4": [
+ {
+ "id": 1,
+ "match-client-id": true,
+ "next-server": "0.0.0.0",
+ "server-hostname": "",
+ "boot-file-name": "",
+ "option-data": [ ],
+ "pools": [ ],
+ "rebind-timer": 20,
+
+ // You can override the value inherited from shared-network
+ // here if your relay uses different IP addresses for
+ // each subnet.
+ "relay": {
+ "ip-address": "0.0.0.0"
+ },
+ "renew-timer": 10,
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and "reservations-out-of-pool"
+ // parameters.
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ "reservations-out-of-pool": false,
+ "subnet": "10.0.0.0/8",
+ "valid-lifetime": 30
+ },
+ {
+ "id": 2,
+ "match-client-id": true,
+ "next-server": "0.0.0.0",
+ "server-hostname": "",
+ "boot-file-name": "",
+ "option-data": [ ],
+ "pools": [ ],
+ "rebind-timer": 20,
+ "renew-timer": 10,
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and "reservations-out-of-pool"
+ // parameters.
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ "reservations-out-of-pool": false,
+ "subnet": "192.0.2.0/24",
+ "valid-lifetime": 30
+ }
+ ],
+ "valid-lifetime": 200
+ } ], // end of shared-networks
+
+ // It is likely that in your network you'll have a mix of regular,
+ // "plain" subnets and shared networks. It is perfectly valid to mix
+ // them in the same config file.
+
+ // This is regular subnet. It's not part of any shared-network.
+ "subnet4": [
+ {
+ "pools": [ { "pool": "192.0.3.1 - 192.0.3.200" } ],
+ "subnet": "192.0.3.0/24",
+ "interface": "eth0",
+ "id": 3
+ }
+ ]
+
+ } // end of Dhcp4
+}
diff --git a/doc/examples/kea4/single-subnet.json b/doc/examples/kea4/single-subnet.json
new file mode 100644
index 0000000..4a2880b
--- /dev/null
+++ b/doc/examples/kea4/single-subnet.json
@@ -0,0 +1,60 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// It is a basic scenario with one IPv4 subnet configured. The subnet
+// contains a single pool of dynamically allocated addresses.
+
+{ "Dhcp4":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+// Addresses will be assigned with a lifetime of 4000 seconds.
+ "valid-lifetime": 4000,
+
+// Renew and rebind timers are commented out. This implies that options
+// 58 and 59 will not be sent to the client. In this case it is up to
+// the client to pick the timer values according to RFC2131. Uncomment the
+// timers to send these options to the client.
+// "renew-timer": 1000,
+// "rebind-timer": 2000,
+
+// The following list defines subnets. We have only one subnet
+// here. We tell Kea that it is directly available over local interface.
+ "subnet4": [
+ {
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "interface": "eth0"
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout. Alternatively, you can specify stderr here, a filename
+// or 'syslog', which will store output messages via syslog.
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea4/vendor-specific.json b/doc/examples/kea4/vendor-specific.json
new file mode 100644
index 0000000..83b40fd
--- /dev/null
+++ b/doc/examples/kea4/vendor-specific.json
@@ -0,0 +1,96 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// The purpose of this example is to showcase how configure
+// Vendor Specific Information (code 43) RFC 2132 section 8.4
+{
+ "Dhcp4": {
+ "option-def": [
+ {
+// Two options that we are planning to include in option 43 as suboptions
+// should be defined on global level
+ "array": false,
+ "code": 2,
+ "name": "vlanid",
+// suboptions should have space configured and it can't start with 'vendor-'
+// otherwise those will be included in 125 option not 43
+ "space": "339",
+ "type": "uint32"
+ },
+ {
+ "array": false,
+ "code": 3,
+ "name": "dls",
+ "space": "339",
+ "type": "string"
+ }
+ ],
+ "client-classes": [
+ {
+
+// Kea needs classification based on option 60, you can either use name:
+// VENDOR_CLASS_ + option 60 content (test parameter is not required than)
+// or use any name and add "test" parameter accordingly e.g.
+// "test": "substring(option[60].hex,0,9) == 'partial-content-of-option-60'"
+ "name": "VENDOR_CLASS_339",
+ "option-def": [
+ {
+// Vendor-specific option has to be defined on the class level, if we're planning
+// to send a single value, then define its type accordingly. If this option
+// should encapsulate other suboptions, the "space" parameter should be the same
+// as included suboptions and "type" set to empty
+ "code": 43,
+// Using the "encapsulate" direction, Kea is told to include options from
+// the "339" namespace. We have defined several such options earlier.
+// This way, the sub-options are "glued" to this option 43.
+ "encapsulate": "339",
+ "name": "vendor-encapsulated-options",
+ "type": "empty"
+ }
+ ],
+ "option-data": [
+ {
+// vendor-encapsulated-options and defined option on global level should
+// be also configured with proper "data" parameters in "option-data" list.
+// Because Kea will send only option that client ask for, and there is no way
+// to ask for suboptions, parameter "always-send" with value set
+// to true has also be included in all custom suboptions
+ "name": "vendor-encapsulated-options"
+ },
+ {
+ "always-send": true,
+ "data": "123",
+ "name": "vlanid",
+ "space": "339"
+ },
+ {
+ "always-send": true,
+ "data": "sdlp://192.0.2.11:18443",
+ "name": "dls",
+ "space": "339"
+ }
+ ]
+ }
+ ],
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": ["eth0"]
+ },
+// We need to specify the database used to store leases.
+ "lease-database": {
+ "type": "memfile"
+ },
+// The following list defines subnets. We have only one subnet
+// here. We tell Kea that it is directly available over local interface.
+ "subnet4": [
+ {
+ "interface": "eth0",
+ "pools": [
+ {
+ "pool": "192.0.2.50-192.0.2.50"
+ }
+ ],
+ "subnet": "192.0.2.0/24",
+ "id": 1
+ }
+ ]
+ }
+}
diff --git a/doc/examples/kea4/vivso.json b/doc/examples/kea4/vivso.json
new file mode 100644
index 0000000..f69f189
--- /dev/null
+++ b/doc/examples/kea4/vivso.json
@@ -0,0 +1,90 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// The purpose of this example is to showcase how configure
+// Vendor-Identifying Vendor-specific Information option
+// (code 125) RFC 3925
+
+
+{
+ "Dhcp4": {
+// If we want to send suboptions in option 125 first those have to be defined
+// on global level
+ "option-def": [
+ {
+ "array": false,
+ "code": 2,
+ "name": "vlanid",
+// In case of suboption of option 125 space has to start with prefix "vendor-"
+// in this case it's "vendor-" + vendor id from option 60 sent by client
+// 339 is Siemens Industry Inc.
+ "space": "vendor-339",
+ "type": "uint32"
+ },
+ {
+ "array": false,
+ "code": 3,
+ "name": "dls",
+ "space": "vendor-339",
+ "type": "string"
+ }
+ ],
+ "client-classes": [
+ {
+// Kea needs classification based on option 60, you can either use name:
+// VENDOR_CLASS_ + option 60 content (test parameter is not required than)
+// or use any name and add "test" parameter accordingly e.g.
+// "test": "substring(option[60].hex,0,9) == 'partial-content-of-option-60'"
+ "name": "VENDOR_CLASS_339",
+ "option-data": [
+ {
+// In "option-data" list we have to configure option 125 with data parameter equal
+// to vendor-id we are expecting, also it will tell Kea which vendor space
+// encapsulate in suboptions.
+ "data": "339",
+ "name": "vivso-suboptions"
+ },
+ {
+// And additionally we have to configure all previously defined suboptions
+// with "space" parameter same as in option-def.
+// Because Kea will send only option that client ask for, and there is no way
+// to ask for suboptions parameter "always-send" with value set
+// to true has also be included in all custom suboptions.
+ "always-send": true,
+ "data": "123",
+ "name": "vlanid",
+ "space": "vendor-339"
+ },
+ {
+ "always-send": true,
+ "data": "sdlp://192.0.2.11:18443",
+ "name": "dls",
+ "space": "vendor-339"
+ }
+ ]
+ }
+ ],
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [
+ "eth0"
+ ]
+ },
+// We need to specify the database used to store leases.
+ "lease-database": {
+ "type": "memfile"
+ },
+// The following list defines subnets. We have only one subnet
+// here. We tell Kea that it is directly available over local interface.
+ "subnet4": [
+ {
+ "id": 1,
+ "interface": "eth0",
+ "pools": [
+ {
+ "pool": "192.0.2.50-192.0.2.50"
+ }
+ ],
+ "subnet": "192.0.2.0/24"
+ }
+ ]
+ }
+}
diff --git a/doc/examples/kea4/with-ddns.json b/doc/examples/kea4/with-ddns.json
new file mode 100644
index 0000000..f479c57
--- /dev/null
+++ b/doc/examples/kea4/with-ddns.json
@@ -0,0 +1,85 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// It is a basic scenario with one IPv4 subnet configured and with DDNS
+// enabled.
+
+{ "Dhcp4":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+// Addresses will be assigned with a lifetime of 4000 seconds.
+ "valid-lifetime": 4000,
+
+// Renew and rebind timers are commented out. This implies that options
+// 58 and 59 will not be sent to the client. In this case it is up to
+// the client to pick the timer values according to RFC2131. Uncomment the
+// timers to send these options to the client.
+// "renew-timer": 1000,
+// "rebind-timer": 2000,
+
+// The following list defines subnets. We have only one subnet
+// here. We tell Kea that it is directly available over local interface.
+ "subnet4": [
+ {
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "subnet": "192.0.2.0/24",
+ "interface": "eth0",
+ "id": 1
+ }
+ ],
+
+// Enable connectivity with kea-dhcp-ddns
+// (Required for dynamic DNS updates)
+ "dhcp-ddns" : {
+ "enable-updates" : true,
+ "server-ip" : "192.0.2.0",
+ "server-port" : 3432,
+ "sender-ip" : "192.0.2.1",
+ "sender-port" : 3433,
+ "max-queue-size" : 2048,
+ "ncr-protocol" : "UDP",
+ "ncr-format" : "JSON"
+ },
+
+// Enable DDNS updates and configure DDNS update behavior
+ "ddns-send-updates" : true,
+ "ddns-override-no-update" : true,
+ "ddns-override-client-update" : true,
+ "ddns-replace-client-name" : "when-present",
+ "ddns-generated-prefix" : "test.prefix",
+ "ddns-qualifying-suffix" : "test.suffix.",
+ "ddns-update-on-renew" : false,
+ "ddns-conflict-resolution-mode": "check-with-dhcid",
+ "ddns-ttl-percent" : 0.75,
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+ "hostname-char-replacement": "x",
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/advanced.json b/doc/examples/kea6/advanced.json
new file mode 100644
index 0000000..02ff310
--- /dev/null
+++ b/doc/examples/kea6/advanced.json
@@ -0,0 +1,189 @@
+// This is an example configuration file for DHCPv6 server in Kea.
+// It attempts to showcase some of the more advanced features.
+// Topology wise, it's a basic scenario with one IPv6 subnet configured.
+// It is assumed that one subnet (2001:db8:1::/64) is available directly
+// over eth0 interface.
+//
+// The following features are currently showcased here:
+// 1. Configuration of MAC/hardware address sources in DHCPv6
+// 2. RSOO (Relay supplied options) - Some relays may insert options with the
+// intention for the server to insert them into client directed messages.
+// 3. Control socket. Kea can open a socket and listen for incoming
+// commands.
+
+{ "Dhcp6":
+
+{
+ // Kea is told to listen on eth0 network interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ],
+
+ // This makes interfaces to be re-detected at each (re-)configuration.
+ // By default it is true.
+ "re-detect": true
+ },
+
+ // We need to specify the database used to store leases. As of
+ // June 2022, three database backends are supported: MySQL,
+ // PostgreSQL and the in-memory database, Memfile.
+ // We will use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+ "sanity-checks": {
+ // This parameter determines what to do when a new lease appears in the
+ // system (i.e. either is read from disk during memfile startup or is
+ // added via lease commands). There are five modes supported:
+ // none - do nothing, accept them as is
+ // warn - if subnet-id problems are detected, print a warning, but
+ // otherwise load the lease as is. This is the default value.
+ // fix - attempt to fix the lease by finding appropriate subnet-id value.
+ // if there is no suitable subnet, the lease is loaded as is.
+ // fix-del - attempt to fix the lease by finding appropriate subnet-id
+ // value. If there is no suitable subnet, the lease is deleted.
+ // del - delete leases that have incorrect subnet-id values.
+ "lease-checks": "fix-del"
+ },
+
+ // Kea 0.9.1 introduced MAC/hardware addresses support in DHCPv6. There is
+ // no single reliable method of getting MAC address information in DHCPv6.
+ // Kea supports several methods. Depending on your network set up, some
+ // methods may be more preferable than others, hence the configuration
+ // parameter. 'mac-sources' is a list of methods. Allowed parameters are:
+ // any, raw, duid, ipv6-link-local, client-link-addr-option, rfc6939 (which
+ // is an alias for client-link-addr-option), remote-id, rfc4649 (which is an
+ // alias for remote-id, subscriber-id, rfc4580 (which is an alias for
+ // subscriber-id) and docsis.
+
+ // Note that the order matters. Methods are attempted one by one in the
+ // order specified until hardware address is obtained. If you don't care
+ // which method is used, using 'any' is marginally faster than enumerating
+ // them all.
+
+ // If mac-sources are not specified, a default value of 'any' is used.
+ "mac-sources": [ "client-link-addr-option", "duid", "ipv6-link-local" ],
+
+ // RFC6422 defines a mechanism called relay-supplied options option. The
+ // relay agent may insert certain options that the server will echo back to
+ // the client, if certain criteria are met. One condition is that the option
+ // must be RSOO-enabled (i.e. allowed to be echoed back). IANA maintains a
+ // list of those options here:
+ // http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#options-relay-supplied
+ // However, it is possible to allow the server to echo back additional
+ // options. This entry marks options 110, 120 and 130 as RSOO-enabled.
+ "relay-supplied-options": [ "110", "120", "130" ],
+
+ // This defines a control socket. If defined, Kea will open a UNIX socket
+ // and will listen for incoming commands. See section 15 of the Kea User's
+ // Guide for list of supported commands.
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea6-ctrl-socket"
+ },
+
+ // Addresses will be assigned with preferred and valid lifetimes
+ // being 3000 and 4000, respectively. Client is told to start
+ // renewing after 1000 seconds. If the server does not respond
+ // after 2000 seconds since the lease was granted, client is supposed
+ // to start REBIND procedure (emergency renewal that allows switching
+ // to a different server).
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+ // The following list defines subnets. Each subnet consists of at
+ // least subnet and pool entries. Note the user-context being
+ // used throughout the definitions. This is something that is not
+ // being used by Kea, it's simply parsed and stored in appropriate
+ // structures. You can put anything you want in the user-context
+ // as long as it is a valid JSON and it starts with a map (i.e.
+ // is enclosed by curly brackets).
+ // A comment entry is translated into a user-context with a
+ // "comment" property so you can include comments inside the
+ // configuration itself.
+ "subnet6": [
+ {
+ "pools": [
+ {
+ "pool": "2001:db8:1::/80",
+
+ // This is user context specified for this particular
+ // pool. You can use it to describe the pool in some way.
+ // Just keep in mind that the structure will not be used
+ // by Kea itself. It will be made available to hooks if
+ // they want to use it.
+ "user-context": { "department": "engineering" }
+ }],
+
+ // Here's the user-context for the whole subnet.
+ "user-context": { "comment": "Floor one, west wing" },
+ // Equivalent using smart parser
+ // "comment": "Floor one, west wing",
+
+ // This defines PD (prefix delegation) pools. In this case
+ // we have only one pool. That consists of /64 prefixes
+ // being delegated out of large /48 pool. Each delegated
+ // prefix will contain an excluded-prefix option.
+ "pd-pools": [
+ {
+ "prefix": "2001:db8:abcd::",
+ "prefix-len": 48,
+ "delegated-len": 64,
+ "excluded-prefix": "2001:db8:abcd:0:1234::",
+ "excluded-prefix-len": 80,
+
+ // Another user-context for this PD pool. Again, you can put
+ // anything you want in there as long as it's valid JSON and
+ // starts with a map.
+ "user-context": {
+ "purpose": "For CPE devices"
+ }
+ }
+ ], // end of pools
+
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "interface": "eth0",
+
+ // Sometimes the relay may use an odd IPv6 address that's not matching
+ // the subnet. This is discouraged, but there are valid cases when it
+ // makes sense. One case is when the relay has only link-local address
+ // and another is when there is a shared subnet scenario.
+ "relay": {
+ "ip-address": "3000::1"
+ }
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout",
+ // Several additional parameters are possible in addition
+ // to the typical output. Flush determines whether logger
+ // flushes output to a file. Maxsize determines maximum
+ // filesize before the file is rotated. maxver
+ // specifies the maximum number of rotated files being
+ // kept.
+ "flush": true,
+ "maxsize": 204800,
+ "maxver": 4,
+ // We use pattern to specify custom log message layout
+ "pattern": "%d{%y.%m.%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/all-keys-netconf.json b/doc/examples/kea6/all-keys-netconf.json
new file mode 100644
index 0000000..e9254aa
--- /dev/null
+++ b/doc/examples/kea6/all-keys-netconf.json
@@ -0,0 +1,1215 @@
+// WARNING: This example configuration is not meant for production use.
+// The Kea DHCPv6 server will refuse this configuration because it contains
+// mutually exclusive configuration parameters.
+//
+// The primary purpose of the example file is to provide a comprehensive
+// list of parameters supported by the Kea DHCPv6 server along with the brief
+// description of each parameter.
+//
+// This stable version is used for YANG as we do not want to update code
+// and models each time a keyword is added to the syntax.
+{
+ // Kea DHCPv6 server configuration begins here.
+ "Dhcp6": {
+ // Global flag selecting an IP address allocation strategy for all
+ // subnets.
+ "allocator": "iterative",
+
+ // Global flag selecting a delegated prefix allocation strategy
+ // for all subnets.
+ "pd-allocator": "random",
+
+ // Ordered list of client classes used by the DHCPv6 server.
+ "client-classes": [
+ {
+ // Class name.
+ "name": "phones_server1",
+
+ // Class-specific DHCPv6 options list.
+ "option-data": [],
+
+ // Class selection expression. The DHCP packet is assigned to this
+ // class when the given expression evaluates to true.
+ "test": "member('HA_server1')",
+
+ // Class valid lifetime.
+ "valid-lifetime": 6000,
+
+ // Class min valid lifetime.
+ "min-valid-lifetime": 4000,
+
+ // Class max valid lifetime.
+ "max-valid-lifetime": 8000,
+
+ // Class preferred lifetime.
+ "preferred-lifetime": 7000,
+
+ // Class min preferred lifetime.
+ "min-preferred-lifetime": 5000,
+
+ // Class max preferred lifetime.
+ "max-preferred-lifetime": 9000
+ },
+ {
+ // Second class name.
+ "name": "phones_server2",
+
+ // Class-specific DHCPv6 options list.
+ "option-data": [],
+
+ // Class selection expression. The DHCP packet is assigned to this
+ // class when the given expression evaluates to true.
+ "test": "member('HA_server2')"
+ },
+ {
+ // Third class name.
+ "name": "late",
+
+ // Boolean flag indicating whether the class expression is only evaluated
+ // when the class is required, e.g. the selected address pool configuration
+ // includes this class name in its "require-client-classes" list. The
+ // default value false means that the class test expression must
+ // always be evaluated.
+ "only-if-required": true,
+
+ // Class selection expression.
+ "test": "member('ALL')"
+ },
+ {
+ // Fourth class name.
+ "name": "my-template-class",
+
+ // Template class flag that holds the expression used to generate the names for all
+ // the spawned subclasses. In this case, the classes are named after the client ID.
+ "template-test": "substring(option[1].hex, 0, all)"
+ }
+ ],
+
+ // Parameters for triggering behaviors compatible with broken or
+ // non-compliant clients, relays, or other agents
+ "compatibility": {
+ // Parse options more leniently where fields can be deduced
+ // deterministically, even if against RFC or common practice.
+ "lenient-option-parsing": true
+ },
+
+ // Command control socket configuration parameters for the Kea DHCPv6 server.
+ "control-socket": {
+ // Location of the UNIX domain socket file the DHCPv6 server uses
+ // to receive control commands from the Kea Control Agent or the
+ // local server administrator.
+ "socket-name": "/tmp/kea6-ctrl-socket",
+
+ // Control socket type used by the Kea DHCPv6 server. The 'unix'
+ // socket is currently the only supported type.
+ "socket-type": "unix"
+ },
+
+ // Specifies a prefix to be prepended to the generated Client FQDN.
+ // It may be specified at the global, shared-network, and subnet levels.
+ "ddns-generated-prefix": "myhost",
+
+ // Boolean flag indicating whether the server should ignore DHCP client
+ // wishes to update DNS on its own. With that flag set to true,
+ // the server will send DNS updates for both forward and
+ // reverse DNS data. The default value is false, which indicates
+ // that the server will delegate a DNS update to the client when
+ // requested. It may be specified at the global, shared-network,
+ // and subnet levels.
+ "ddns-override-client-update": false,
+
+ // Boolean flag indicating whether the server should override the DHCP
+ // client's wish to not update the DNS. With this parameter
+ // set to true, the server will send a DNS update even when
+ // the client requested no update. It may be specified at the
+ // global, shared-network, and subnet levels.
+ "ddns-override-no-update": false,
+
+ // Suffix appended to the partial name sent to the DNS. The
+ // default value is an empty string, which indicates that no
+ // suffix is appended. It may be specified at the global,
+ // shared-network, and subnet levels.
+ "ddns-qualifying-suffix": "",
+
+ // Enumeration specifying whether the server should honor
+ // the hostname or Client FQDN sent by the client or replace
+ // this name. The acceptable values are: "never" (use the
+ // name the client sent), "always" (replace the name the
+ // client sent), "when-present" (replace the name the client
+ // sent, but do not generate one when the client didn't send
+ // the name), "when-not-present" (generate the name when
+ // client didn't send one, otherwise leave the name the
+ // client sent). The default value is "never". It may be
+ // specified at the global, shared-network, and subnet levels.
+ "ddns-replace-client-name": "never",
+
+ // Boolean flag which enables or disables DDNS updating. It
+ // defaults to true. It may be specified at the global, shared-
+ // network, and subnet levels. It works in conjunction with
+ // dhcp-ddns:enable-updates, which must be true to enable connectivity
+ // to kea-dhcp-ddns.
+ "ddns-send-updates": true,
+
+ // Boolean flag, which when true instructs the server to always
+ // update DNS when leases are renewed, even if the DNS information
+ // has not changed. The server's default behavior (i.e. flag is false)
+ // is to only update DNS if the DNS information has changed. It
+ // may be specified at the global, shared-network, and subnet levels.
+ "ddns-update-on-renew": true,
+
+ // Boolean flag which is passed to kea-dhcp-ddns with each DDNS
+ // update request, to indicate whether DNS update conflict
+ // resolution as described in RFC 4703 should be employed for the
+ // given update request. The default value for this flag is true.
+ // It may be specified at the global, shared-network, and subnet levels.
+ "ddns-use-conflict-resolution": true,
+
+ // When greater than 0.0, it is the percent of the lease's lifetime
+ // to use for the DNS TTL.
+ "ddns-ttl-percent": 0.75,
+
+ // Time in seconds specifying how long a declined lease should be
+ // excluded from DHCP assignments. The default value is 24 hours.
+ "decline-probation-period": 86400,
+
+ // Name Change Request forwarding configuration for the Kea DHCPv6 server.
+ // NCRs are sent to the Kea D2 module to update DNS upon allocation of
+ // DHCP leases.
+ "dhcp-ddns": {
+ // Boolean flag indicating whether Kea DHCPv6 server should connect to
+ // kea-dhcp-ddns. This must be true for NCRs to be created and
+ // sent to kea-dhcp-ddns. By default, NCRs are not generated.
+ "enable-updates": false,
+
+ // Specifies maximum number of NCRs to queue waiting to be sent
+ // to the Kea D2 server.
+ "max-queue-size": 1024,
+
+ // Packet format to use when sending NCRs to the Kea D2 server.
+ // Currently, only JSON format is supported.
+ "ncr-format": "JSON",
+
+ // Socket protocol to use when sending NCRs to D2. Currently,
+ // only UDP is supported.
+ "ncr-protocol": "UDP",
+
+ // IP address that the Kea DHCPv6 server should use to send
+ // NCRs to D2. The default value of zero indicates that Kea
+ // should pick a suitable address.
+ "sender-ip": "::1",
+
+ // Port number that the Kea DHCPv6 server should use to send
+ // NCRs to D2. The default value of zero indicates that Kea
+ // should pick a suitable port.
+ "sender-port": 0,
+
+ // IP address on which D2 listens for NCRs.
+ "server-ip": "::1",
+
+ // Port number on which D2 listens for NCRs.
+ "server-port": 53001,
+
+ // The following parameters are DEPRECATED. They have been
+ // replaced with parameters that may be set at the global,
+ // shared-network, and subnet6 scopes. They are listed here
+ // as configuration parsing still accepts them. Eventually
+ // support for them will be removed.
+ "generated-prefix": "myhost",
+ "hostname-char-replacement": "x",
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+ "override-client-update": false,
+ "override-no-update": false,
+ "qualifying-suffix": "",
+ "replace-client-name": "never"
+ },
+
+ // Specifies the first of the two consecutive ports of the UDP
+ // sockets used for communication between DHCPv6 and DHCPv4
+ // servers. See RFC 7341.
+ "dhcp4o6-port": 0,
+
+ // Collection of Kea DHCPv6 server parameters configuring how
+ // the server should process expired DHCP leases.
+ "expired-leases-processing": {
+ // Specifies the number of seconds since the last removal of
+ // the expired leases, when the next removal should occur.
+ // If both "flush-reclaimed-timer-wait-time" and
+ // "hold-reclaimed-time" are not 0, when the client sends a release
+ // message the lease is expired instead of being deleted from
+ // lease storage.
+ "flush-reclaimed-timer-wait-time": 25,
+
+ // Specifies the length of time in seconds to keep expired
+ // leases in the lease database (lease affinity).
+ // If both "flush-reclaimed-timer-wait-time" and
+ // "hold-reclaimed-time" are not 0, when the client sends a release
+ // message the lease is expired instead of being deleted from
+ // lease storage.
+ "hold-reclaimed-time": 3600,
+
+ // Specifies the maximum number of expired leases that can be
+ // processed in a single attempt to clean up expired leases
+ // from the lease database. If there are more
+ // expired leases, they will be processed during the next
+ // cleanup attempt.
+ "max-reclaim-leases": 100,
+
+ // Specifies the maximum time in milliseconds that a single attempt
+ // to clean up expired leases from the lease database may take.
+ "max-reclaim-time": 250,
+
+ // Specifies the length of time in seconds since the last attempt
+ // to process expired leases before initiating the next attempt.
+ "reclaim-timer-wait-time": 10,
+
+ // Specifies the maximum number of expired lease-processing cycles
+ // which didn't result in full cleanup of exired leases from the
+ // lease database, after which a warning message is issued.
+ "unwarned-reclaim-cycles": 5
+ },
+
+ // List of hook libraries and their specific configuration parameters
+ // to be loaded by Kea DHCPv4 server.
+ "hooks-libraries": [
+ {
+ // Location of the hook library to be loaded.
+ "library": "/opt/lib/kea/hooks/libdhcp_lease_cmds.so",
+
+ // Hook library-specific configuration parameters.
+ "parameters": { }
+ }
+ ],
+
+ // List of access credentials to external sources of IPv6 reservations,
+ "hosts-databases": [
+ {
+ // Name of the database to connect to.
+ "name": "keatest",
+
+ // Host on which the database resides.
+ "host": "localhost",
+
+ // Database password.
+ "password": "keatest",
+
+ // Port on which the database is available.
+ "port": 3306,
+
+ // Type of database, e.g. "mysql", "postgresql".
+ "type": "mysql",
+
+ // Username to be used to access the database.
+ "user": "keatest",
+
+ // Read-only mode.
+ "readonly": false,
+
+ // The next entries are for OpenSSL support in MySQL.
+
+ // Trust anchor aka certificate authority file or directory.
+ "trust-anchor": "my-ca",
+
+ // Client certificate file name.
+ "cert-file": "my-cert",
+
+ // Private key file name.
+ "key-file": "my-key",
+
+ // Cipher list (see the OpenSSL ciphers command manual).
+ "cipher-list": "AES",
+
+ // Connection reconnect wait time.
+ // This parameter governs how long Kea waits before attempting
+ // to reconnect. Expressed in milliseconds. The default is 0
+ // (disabled) for MySQL and PostgreSQL.
+ "reconnect-wait-time": 3000,
+
+ // Connection maximum reconnect tries.
+ "max-reconnect-tries": 3,
+
+ // Action to take when connection recovery fails.
+ // Supported values: stop-retry-exit, serve-retry-exit,
+ // serve-retry-continue
+ "on-fail": "stop-retry-exit",
+
+ // Connection connect timeout in seconds.
+ "connect-timeout": 100,
+
+ // Timeout of database read operations in seconds.
+ "read-timeout": 120,
+
+ // Timeout of database write operations in seconds.
+ "write-timeout": 180
+ },
+ {
+ // Name of the database to connect to.
+ "name": "keatest",
+
+ // Host on which the database resides.
+ "host": "localhost",
+
+ // Database password.
+ "password": "keatest",
+
+ // Port on which the database is available.
+ "port": 5432,
+
+ // Type of database, e.g. "mysql", "postgresql".
+ "type": "postgresql",
+
+ // Username to be used to access the database.
+ "user": "keatest",
+
+ // TCP user timeout while communicating with the database.
+ // It is specified in seconds.
+ "tcp-user-timeout": 100
+ }
+ ],
+
+ // List of host reservation identifier types to be used by the
+ // Kea DHCPv6 server to fetch static reservations for
+ // DHCP clients. All identifiers are used by default, which
+ // means that the server will issue multiple queries to the
+ // database to find if there is a reservation for a particular
+ // client. If a particular deployment uses only a subset, e.g.
+ // one identifier type, this identifier should be only listed
+ // here to prevent unnecessary queries to the database.
+ "host-reservation-identifiers": [
+ "hw-address",
+ "duid",
+ "flex-id"
+ ],
+
+ // Specifies configuration of interfaces on which the Kea DHCPv6
+ // server is listening to the DHCP queries.
+ "interfaces-config": {
+ // Specifies a list of interfaces on which the Kea DHCPv6
+ // server should listen to DHCP requests.
+ "interfaces": [
+ "eth0"
+ ],
+
+ // Boolean flag indicating whether the available interfaces should
+ // be re-detected upon server reconfiguration. The default value
+ // is true, which means that the interfaces are always
+ // re-detected.
+ "re-detect": true,
+
+ // Kea tries to bind the service sockets during initialization, but it may
+ // fail due to a port being already opened or a misconfiguration. Kea can
+ // suppress these errors and only log them. This flag prevents starting
+ // the DHCP server without binding all sockets. If unspecified, it
+ // defaults to false.
+ "service-sockets-require-all": true,
+
+ // Kea tries to bind the service sockets during initialization. This
+ // option specifies how many times binding to interface will be retried.
+ // The default value is 0, which means that the operation will not be
+ // repeated.
+ "service-sockets-max-retries": 5,
+
+ // The time interval in milliseconds to wait before the next attempt to
+ // retry opening a service socket.
+ "service-sockets-retry-wait-time": 5000
+ },
+
+ // Boolean parameter which controls whether an early global host
+ // reservations lookup should be performed. This lookup takes place
+ // before subnet selection and when a global reservation is found
+ // with some client classes, it triggers a second phase classification.
+ // It can also be used to drop queries using host reservations as a
+ // decision table indexed by reservation identifiers.
+ "early-global-reservations-lookup": true,
+
+ // Boolean parameter which controls the DHCP server's behavior with respect
+ // to creating host reservations for the same IP address or delegated
+ // prefix. By default this flag is set to true in which case the server
+ // prevents creation of multiple host reservations for the same IP address
+ // or delegated prefix. When this parameter is set to false, the server
+ // allows for creating multiple reservations for the same IP address or
+ // delegated prefix within a subnet. This setting is useful in deployments
+ // in which a given host may be communicating with a DHCP server over
+ // multiple interfaces and depending on the chosen interface different
+ // MAC address (or other identifier) will be used to identify the host.
+ // Note that some host backends do not support the mode in which multiple
+ // reservations for the same IP address or delegated prefix are used.
+ // If these backends are in use and this setting is attempted a
+ // configuration error will occur. The MySQL and PostgreSQL backends do
+ // support this mode.
+ "ip-reservations-unique": true,
+
+ // Boolean parameter which controls whether host reservations lookup
+ // should be performed before lease lookup. This parameter has effect
+ // only when multi-threading is disabled. When multi-threading is
+ // enabled, host reservations lookup is always performed first to avoid
+ // lease-lookup resource locking.
+ "reservations-lookup-first": true,
+
+ // Specifies credentials to access lease database.
+ "lease-database": {
+ // memfile backend-specific parameter specifying the interval
+ // in seconds at which the lease file should be cleaned up (outdated
+ // lease entries are removed to prevent the lease file from growing
+ // infinitely).
+ "lfc-interval": 3600,
+
+ // Maximum number of lease-file read errors allowed before
+ // loading the file is abandoned. Defaults to 0 (no limit).
+ "max-row-errors": 100,
+
+ // Name of the lease file. In the case of a database it specifies the
+ // database name.
+ "name": "/tmp/kea-dhcp6.csv",
+
+ // memfile-specific parameter indicating whether leases should
+ // be saved on persistent storage (disk) or not. The true value
+ // is the default and it indicates that leases are stored in
+ // persistent storage. This setting must be used in production.
+ // The false value should only be used for testing purposes
+ // because non-stored leases will be lost upon Kea server restart.
+ "persist": true,
+
+ // Lease database backend type, i.e. "memfile", "mysql" or
+ // "postgresql".
+ "type": "memfile"
+ },
+
+ // List of parameters indicating how the client's MAC address can be
+ // inferred from the DHCP query. Supported values are listed in the
+ // Kea Administrator Reference Manual.
+ "mac-sources": [ "duid" ],
+
+ // List of global DHCP options that the Kea DHCPv6 server assigns to
+ // clients.
+ "option-data": [
+ {
+ // Boolean flag indicating whether the given option is always
+ // sent in response or only when requested. The default
+ // value of false indicates that it is only sent when
+ // requested.
+ "always-send": false,
+
+ // Option code. It is not required if the option name is
+ // provided.
+ "code": 23,
+
+ // Boolean value indicating whether the option data specified
+ // in the "data" field is specified as a string of hexadecimal
+ // digits or in human-readable CSV format.
+ "csv-format": true,
+
+ // Option data to be stored in the option payload.
+ "data": "2001:db8:2::45, 2001:db8:2::100",
+
+ // Option name. It is not required if the option code is
+ // provided.
+ "name": "dns-servers",
+
+ // Boolean flag indicating whether the given option is never
+ // sent in response. The default value of false indicates
+ // that it is sent when it should be. When true, the option
+ // is not sent despite any other setting, i.e. it is
+ // a final flag.
+ "never-send": false,
+
+ // Option space. The default is the "dhcp6" option space which
+ // groups top-level DHCPv6 options.
+ "space": "dhcp6"
+ }
+ ],
+
+ // List of global option definitions, i.e. option formats, that the
+ // Kea DHCPv6 server is using.
+ "option-def": [
+ {
+ // Boolean flag indicating whether the option definition comprises
+ // an array of values of some type, e.g. an array of IPv6 addresses.
+ // The default value of false means that the option does not
+ // comprise an array of values.
+ "array": false,
+
+ // Option code.
+ "code": 6,
+
+ // Holds a name of the option space encapsulated by this option.
+ // All options that belong to this option space will be sent
+ // as sub-options of this option. An empty string means that this
+ // option doesn't encapsulate any option.
+ "encapsulate": "",
+
+ // Option name.
+ "name": "my-option",
+
+ // Specifies the types of fields within the option if the option
+ // is said to be a "record" (see "type"). In this particular example
+ // this option comprises two fields, 1 byte and 2 bytes long.
+ "record-types": "uint8, uint16",
+
+ // Name of the option space to which this option belongs.
+ "space": "my-space",
+
+ // Option type. All possible types are listed in the Kea
+ // Administrator Reference Manual.
+ "type": "record"
+ }
+ ],
+
+ // Global value which limits the number of client packets (e.g.
+ // REQUESTs,RENEWs...) that may be parked while waiting for
+ // hook library work to complete, prior to a response (e.g. REPLY)
+ // being sent back to the client. A typical example is when kea-dhcp6
+ // parks a REQUEST while it sends the lease update(s) to its
+ // HA peer(s). The packet is unparked once the update(s) have been
+ // acknowledged. This value limits the number of packets that can
+ // be held pending the updates. In times of heavy client traffic,
+ // this value can keep kea-dhcp6 from building an insurmountable
+ // backlog of updates.
+ "parked-packet-limit": 128,
+
+ // Global (default) value of the preferred lifetime.
+ "preferred-lifetime": 50,
+
+ // Global min value of the preferred lifetime.
+ "min-preferred-lifetime": 40,
+
+ // Global max value of the preferred lifetime.
+ "max-preferred-lifetime": 60,
+
+ // Global value for the rebind timer, i.e. the time after which the
+ // DHCP client enters the rebind state if it fails to renew the lease.
+ "rebind-timer": 40,
+
+ // List of relay supplied option codes. See RFC 6422.
+ "relay-supplied-options": [ "110", "120", "130" ],
+
+ // Global value for the renew timer, i.e. the time after which the
+ // DHCP client renews the lease.
+ "renew-timer": 30,
+
+ // Global value to store extended information (e.g. relay agent
+ // information) with each lease.
+ "store-extended-info": true,
+
+ // Statistics keep some samples per observation point.
+ // There are two default values: maximum count and maximum age.
+ // Setting the maximum count to zero disables it.
+ "statistic-default-sample-count": 0,
+
+ // When the maximum count is 0 the maximum age (in seconds) applies.
+ "statistic-default-sample-age": 60,
+
+ // Multi-threading parameters.
+ "multi-threading": {
+ // By default, Kea processes packets on multiple threads if the hardware permits.
+ "enable-multi-threading": true,
+
+ // When multi-threading is enabled, Kea will process packets on a
+ // number of multiple threads configurable through this option. The
+ // value must be a positive integer (0 means auto-detect).
+ "thread-pool-size": 0,
+
+ // When multi-threading is enabled, Kea will read packets from the
+ // interface and append a working item to the thread pool. This
+ // option configures the maximum number of items that can be queued.
+ // The value must be a positive integer (0 means unlimited).
+ "packet-queue-size": 0
+ },
+
+ // Governs how the Kea DHCPv6 server should deal with invalid
+ // data received from the client.
+ "sanity-checks": {
+ // Specifies how the Kea DHCPv6 server should behave when invalid
+ // data is read for a lease from the lease file. The following
+ // values are supported: "none" (don't attempt to correct the
+ // lease information), "warn" (print a warning for subnet-id
+ // related inconsistencies), "fix" (correct the subnet id by
+ // trying to find the suitable subnet), "fix-del" (similar
+ // to "fix" but delete the lease if no suitable subnet found),
+ // "del" (delete the lease if the lease has invalid subnet
+ // identifier value).
+ "lease-checks": "warn",
+
+ // Specifies how Kea DHCPv4 server should behave when invalid
+ // extended info is read for a lease from the lease file, or
+ // whether to upgrade from the old format. The following values
+ // are supported: "none" (don't attempt to correct or upgrade
+ // the extended info), "fix" (fix common inconsistencies and
+ // upgrade from the old format; this is the default), "strict"
+ // (fix inconsistencies with an impact on Leasequery),
+ // "pedantic" (enforce full Kea code format).
+ "extended-info-checks": "fix"
+ },
+
+ // Custom DUID used by the DHCPv6 server.
+ "server-id": {
+ // Type of the DUID. Possible values are "LLT", "EN", and "LL".
+ "type": "EN",
+
+ // Enterprise id used for "EN" duid.
+ "enterprise-id": 2495,
+
+ // Identifier part of the DUID.
+ "identifier": "0123456789",
+
+ // Boolean flag indicating whether the DUID should be persisted on
+ // disk.
+ "persist": false
+ },
+
+ // List of shared networks used by the Kea DHCPv6 server. The shared
+ // networks group subnets together.
+ "shared-networks": [
+ {
+ // A flag selecting an IP address allocation strategy for all
+ // subnets in this shared network.
+ "allocator": "random",
+
+ // A flag selecting a delegated prefix allocation strategy for
+ // all subnets in this shared network.
+ "pd-allocator": "iterative",
+
+ // Restricts this shared network to allow only clients
+ // that belong to a particular client class. If an
+ // empty string is provided, no restriction is applied.
+ "client-class": "",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-generated-prefix": "myhost",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-override-client-update": false,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-override-no-update": false,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-qualifying-suffix": "",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-replace-client-name": "never",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-send-updates": true,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-update-on-renew": true,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-use-conflict-resolution": true,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-ttl-percent": 0.65,
+
+ // Shared-network level value. See description at the global level.
+ "hostname-char-replacement": "x",
+
+ // Shared-network level value. See description at the global level.
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+
+ // Specifies that this shared network is selected for
+ // requests received on a particular interface.
+ "interface": "eth0",
+
+ // Specifies the content of the interface-id option used
+ // by relays to identify the interface on the relay to
+ // which the response is sent.
+ "interface-id": "",
+
+ // Shared network name.
+ "name": "my-secret-network",
+
+ // List of shared network-specific DHCP options.
+ "option-data": [],
+
+ // Shared network-specific (default) preferred lifetime.
+ "preferred-lifetime": 2000,
+
+ // Shared network-specific min preferred lifetime.
+ "min-preferred-lifetime": 1500,
+
+ // Shared network-specific ma xpreferred lifetime.
+ "max-preferred-lifetime": 2500,
+
+ // Boolean flag indicating whether the server can respond to
+ // a Solicit message including a Rapid Commit option with
+ // the Reply message (See DHCPv6 rapid commit).
+ "rapid-commit": false,
+
+ // List of IPv6 relay addresses for which this shared
+ // network is selected.
+ "relay": {
+ "ip-addresses": []
+ },
+
+ // Shared-network level rebind timer.
+ "rebind-timer": 41,
+
+ // Shared-network level renew timer.
+ "renew-timer": 31,
+
+ // Shared-network level compute T1 and T2 timers.
+ "calculate-tee-times": true,
+
+ // T1 = valid lifetime * .5.
+ "t1-percent": .5,
+
+ // T2 = valid lifetime * .75.
+ "t2-percent": .75,
+
+ // Cache threshold = valid lifetime * .25.
+ "cache-threshold": .25,
+
+ // Cache maximum: when the client last-transmission time
+ // is close enough, the lease is not renewed and the current
+ // lease is returned as it was "cached".
+ "cache-max-age": 1000,
+
+ // Enumeration specifying the server's mode of operation when it
+ // fetches host reservations.
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and "reservations-out-of-pool"
+ // parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ // If specified, it is inherited by "subnet6" levels.
+ "reservations-out-of-pool": false,
+
+ // List of client classes which must be evaluated when this shared
+ // network is selected for client assignments.
+ "require-client-classes": [ "late" ],
+
+ // Turn off storage of extended information (e.g. relay agent
+ // information) with each lease for this shared network.
+ "store-extended-info": false,
+
+ // List of IPv6 subnets belonging to this shared network.
+ "subnet6": [
+ {
+ // A flag selecting an IP address allocation strategy for
+ // the subnet.
+ "allocator": "iterative",
+
+ // A flag selecting a delegated prefix allocation strategy
+ // for the subnet.
+ "pd-allocator": "iterative",
+
+ // Restricts this subnet to allow only clients that belong
+ // to a particular client class. If an empty string is
+ // provided, no restriction is applied.
+ "client-class": "",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-generated-prefix": "myhost",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-override-client-update": false,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-override-no-update": false,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-qualifying-suffix": "",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-replace-client-name": "never",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-send-updates": true,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-update-on-renew": true,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-use-conflict-resolution": true,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-ttl-percent": 0.55,
+
+ // Subnet-level value. See description at the global level.
+ "hostname-char-replacement": "x",
+
+ // Subnet-level value. See description at the global level.
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+
+ // Subnet unique identifier.
+ "id": 1,
+
+ // Specifies that this subnet is selected for requests
+ // received on a particular interface.
+ "interface": "eth0",
+
+ // Specifies the content of the interface-id option used
+ // by relays to identify the interface on the relay to
+ // which the response is sent.
+ "interface-id": "",
+
+ // Turn on storage of extended information (e.g. relay agent
+ // information) with each lease for this subnet.
+ "store-extended-info": true,
+
+ // Subnet-level list of DHCP options.
+ "option-data": [
+ {
+ // Boolean flag indicating whether the particular option
+ // should be always sent or sent only when requested.
+ "always-send": false,
+
+ // Option code.
+ "code": 7,
+
+ // Boolean flag indicating whether the option value specified
+ // in "data" is a string of hexadecimal values or human-readable
+ // CSV value.
+ "csv-format": false,
+
+ // Option data to be included in the option payload.
+ "data": "0xf0",
+
+ // Option name.
+ "name": "preference",
+
+ // Boolean flag indicating whether the given option is never
+ // sent in response.
+ "never-send": false,
+
+ // Option space. The default value "dhcp6" designates the
+ // top level option space.
+ "space": "dhcp6"
+ }
+ ],
+
+ // List of pools from which delegated prefixes are assigned to the
+ // clients.
+ "pd-pools": [
+ {
+ // Restricts this prefix pool to be used only for the client
+ // requests belonging to a particular client class.
+ "client-class": "phones_server1",
+
+ // Length of prefixes delegated to clients.
+ "delegated-len": 64,
+
+ // Excluded prefix (address) from client assignments.
+ "excluded-prefix": "2001:db8:1::",
+
+ // Excluded prefix (length) from client assignments.
+ "excluded-prefix-len": 72,
+
+ // Prefix pool level list of DHCP options.
+ "option-data": [],
+
+ // Prefix range (address) used for client assignments.
+ "prefix": "2001:db8:1::",
+
+ // Prefix range (length) used for client assignments.
+ "prefix-len": 48,
+
+ // List of client classes which must be evaluated
+ // when this prefix pool is selected for client assignments.
+ "require-client-classes": []
+ }
+ ],
+
+ // List of IP address pools belonging to the subnet.
+ "pools": [
+ {
+ // Restricts this pool to only be used for client
+ // requests belonging to a particular client class.
+ "client-class": "phones_server1",
+
+ // Pool-level list of DHCP options.
+ "option-data": [],
+
+ // Address range used for client assignments.
+ "pool": "2001:db8:0:1::/64",
+
+ // List of client classes which must be evaluated when this pool
+ // is selected for client assignments.
+ "require-client-classes": [ "late" ]
+ },
+ {
+ // Restricts this pool to only be used for client
+ // requests belonging to a particular client class.
+ "client-class": "phones_server2",
+
+ // Pool-level list of DHCP options.
+ "option-data": [],
+
+ // Address range used for client assignments.
+ "pool": "2001:db8:0:3::/64",
+
+ // List of client classes which must be evaluated when this pool
+ // is selected for client assignments.
+ "require-client-classes": [],
+
+ // Pool identifier used to enable statistics for this pool.
+ // The pool ID does not need to be unique within the subnet
+ // or across subnets.
+ // If not unconfigured, it defaults to 0. The statistics
+ // regarding this pool will be combined with the other statistics
+ // of all other pools with the same pool ID in this subnet.
+ "pool-id": 1
+ }
+ ],
+
+ // Subnet specific (default) preferred lifetime.
+ "preferred-lifetime": 2000,
+
+ // Subnet specific min preferred lifetime.
+ "min-preferred-lifetime": 1500,
+
+ // Subnet specific max referred lifetime.
+ "max-preferred-lifetime": 2500,
+
+ // Boolean flag indicating whether the server can respond to
+ // a Solicit message including a Rapid Commit option with
+ // the Reply message (See DHCPv6 rapid commit).
+ "rapid-commit": false,
+
+ // Subnet-level value of the rebind timer.
+ "rebind-timer": 40,
+
+ // List of IPv6 relay addresses for which this subnet is selected.
+ "relay": {
+ "ip-addresses": [
+ "2001:db8:0:f::1"
+ ]
+ },
+
+ // Subnet-level renew timer.
+ "renew-timer": 30,
+
+ // Enumeration specifying the server's mode of operation when it
+ // fetches host reservations.
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and
+ // "reservations-out-of-pool" parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved
+ // addresses are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ "reservations-out-of-pool": false,
+
+ // Subnet-level compute T1 and T2 timers.
+ "calculate-tee-times": true,
+
+ // T1 = valid lifetime * .5.
+ "t1-percent": .5,
+
+ // T2 = valid lifetime * .75.
+ "t2-percent": .75,
+
+ // Cache threshold = valid lifetime * .25.
+ "cache-threshold": .25,
+
+ // Subnet-level cache maximum.
+ "cache-max-age": 1000,
+
+ // List of static IPv6 reservations assigned to clients belonging
+ // to this subnet. For a detailed example, see reservations.json.
+ "reservations": [
+ {
+ // Identifier used for client matching. Supported values are
+ // "duid", "hw-address" and "flex-id".
+ "duid": "01:02:03:04:05:06:07:08:09:0A",
+
+ // List of reserved IPv6 addresses.
+ "ip-addresses": [ "2001:db8:1:cafe::1" ],
+
+ // List of reserved IPv6 prefixes.
+ "prefixes": [ "2001:db8:2:abcd::/64" ],
+
+ // Reserved hostname.
+ "hostname": "foo.example.com",
+
+ // Reservation-specific option data.
+ "option-data": [
+ {
+ // Option name.
+ "name": "vendor-opts",
+
+ // Option value.
+ "data": "4491"
+ }
+ ]
+ }
+ ],
+
+ // List of client classes which must be evaluated when this subnet
+ // is selected for client assignments.
+ "require-client-classes": [ "late" ],
+
+ // Subnet prefix.
+ "subnet": "2001:db8::/32",
+
+ // Subnet-level (default) valid lifetime.
+ "valid-lifetime": 6000,
+
+ // Subnet-level min valid lifetime.
+ "min-valid-lifetime": 4000,
+
+ // Subnet-level max valid lifetime.
+ "max-valid-lifetime": 8000
+ }
+ ],
+
+ // Shared-network level (default) valid lifetime.
+ "valid-lifetime": 6001,
+
+ // Shared-network level min valid lifetime.
+ "min-valid-lifetime": 4001,
+
+ // Shared-network level max valid lifetime.
+ "max-valid-lifetime": 8001
+ }
+ ],
+
+ // List of IPv6 subnets which don't belong to any shared network.
+ "subnet6": [],
+
+ // Global valid lifetime value.
+ "valid-lifetime": 6000,
+
+ // Global min valid lifetime value.
+ "min-valid-lifetime": 4000,
+
+ // Global max valid lifetime value.
+ "max-valid-lifetime": 8000,
+
+ // Reservations (examples are in other files).
+ "reservations": [],
+
+ // Configuration control (currently not used, i.e. this syntax
+ // is already defined but the corresponding feature is not implemented).
+ "config-control": {
+ // Only the configuration databases entry is defined.
+ "config-databases": [
+ {
+ // Name of the database to connect to.
+ "name": "config",
+
+ // Type of database, e.g. "mysql", "postgresql".
+ "type": "mysql"
+ }
+ ],
+ // Interval between attempts to fetch configuration updates
+ // via the configuration backends used.
+ "config-fetch-wait-time": 30
+ },
+
+ // Server tag.
+ "server-tag": "my DHCPv6 server",
+
+ // DHCP queue-control parameters.
+ "dhcp-queue-control": {
+ // Enable queue is mandatory.
+ "enable-queue": true,
+
+ // Queue type is mandatory.
+ "queue-type": "kea-ring6",
+
+ // Capacity is optional.
+ "capacity": 64
+ },
+
+ // Fetches host reservations.
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and "reservations-out-of-pool" parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ // If specified, it is inherited by "shared-networks" and
+ // "subnet6" levels.
+ "reservations-out-of-pool": false,
+
+ // Data directory.
+ "data-directory": "/tmp",
+
+ // Global compute T1 and T2 timers.
+ "calculate-tee-times": true,
+
+ // T1 = valid lifetime * .5.
+ "t1-percent": .5,
+
+ // T2 = valid lifetime * .75.
+ "t2-percent": .75,
+
+ // Cache threshold = valid lifetime * .25.
+ "cache-threshold": .25,
+
+ // Global cache maximum.
+ "cache-max-age": 1000,
+
+ // String of zero or more characters with which to replace each
+ // invalid character in the Client FQDN. The default
+ // value is an empty string, which will cause invalid characters
+ // to be omitted rather than replaced.
+ "hostname-char-replacement": "x",
+
+ // Regular expression describing the invalid character set in
+ // the Client FQDN.
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+
+ // List of loggers used by the servers using this configuration file.
+ "loggers": [
+ {
+ // Debug level, a value between 0..99. The greater the value
+ // the more detailed the debug log.
+ "debuglevel": 99,
+
+ // Name of the logger.
+ "name": "kea-dhcp6",
+
+ // Configures how the log should be output.
+ "output-options": [
+ {
+ // Determines whether the log should be flushed to a file.
+ "flush": true,
+
+ // Specifies maximum filesize before the file is rotated.
+ "maxsize": 10240000,
+
+ // Specifies the maximum number of rotated files to be kept.
+ "maxver": 1,
+
+ // Specifies the logging destination.
+ "output": "stdout",
+
+ // Specifies log entry content
+ "pattern": "%D{%Y-%m-%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+ }
+ ],
+
+ // Specifies logging severity, i.e. "ERROR", "WARN", "INFO", "DEBUG".
+ "severity": "INFO"
+ }
+ ],
+
+ // Look at advanced examples for the use of user-contexts.
+ "user-context": { }
+ }
+}
diff --git a/doc/examples/kea6/all-keys.json b/doc/examples/kea6/all-keys.json
new file mode 100644
index 0000000..90ff5bb
--- /dev/null
+++ b/doc/examples/kea6/all-keys.json
@@ -0,0 +1,1247 @@
+// WARNING: This example configuration is not meant for production use.
+// The Kea DHCPv6 server will refuse this configuration because it contains
+// mutually exclusive configuration parameters.
+//
+// The primary purpose of the example file is to provide a comprehensive
+// list of parameters supported by the Kea DHCPv6 server along with the brief
+// description of each parameter.
+//
+// This current version should be up to date, i.e. new keywords should be
+// added in this file at the same time as in the parser specification.
+{
+ // Kea DHCPv6 server configuration begins here.
+ "Dhcp6": {
+ // Global flag selecting an IP address allocation strategy for all
+ // subnets.
+ "allocator": "iterative",
+
+ // Global flag selecting a delegated prefix allocation strategy
+ // for all subnets.
+ "pd-allocator": "random",
+
+ // Ordered list of client classes used by the DHCPv6 server.
+ "client-classes": [
+ {
+ // Class name.
+ "name": "phones_server1",
+
+ // Class-specific DHCPv6 options list.
+ "option-data": [],
+
+ // Class selection expression. The DHCP packet is assigned to this
+ // class when the given expression evaluates to true.
+ "test": "member('HA_server1')",
+
+ // Class valid lifetime.
+ "valid-lifetime": 6000,
+
+ // Class min valid lifetime.
+ "min-valid-lifetime": 4000,
+
+ // Class max valid lifetime.
+ "max-valid-lifetime": 8000,
+
+ // Class preferred lifetime.
+ "preferred-lifetime": 7000,
+
+ // Class min preferred lifetime.
+ "min-preferred-lifetime": 5000,
+
+ // Class max preferred lifetime.
+ "max-preferred-lifetime": 9000
+ },
+ {
+ // Second class name.
+ "name": "phones_server2",
+
+ // Class-specific DHCPv6 options list.
+ "option-data": [],
+
+ // Class selection expression. The DHCP packet is assigned to this
+ // class when the given expression evaluates to true.
+ "test": "member('HA_server2')"
+ },
+ {
+ // Third class name.
+ "name": "late",
+
+ // Boolean flag indicating whether the class expression is only evaluated
+ // when the class is required, e.g. the selected address pool configuration
+ // includes this class name in its "require-client-classes" list. The
+ // default value false means that the class test expression must
+ // always be evaluated.
+ "only-if-required": true,
+
+ // Class selection expression.
+ "test": "member('ALL')"
+ },
+ {
+ // Fourth class name.
+ "name": "my-template-class",
+
+ // Template class flag that holds the expression used to generate the names for all
+ // the spawned subclasses. In this case, the classes are named after the client ID.
+ "template-test": "substring(option[1].hex, 0, all)"
+ }
+ ],
+
+ // Parameters for triggering behaviors compatible with broken or
+ // non-compliant clients, relays, or other agents
+ "compatibility": {
+ // Parse options more leniently where fields can be deduced
+ // deterministically, even if against RFC or common practice.
+ "lenient-option-parsing": true
+ },
+
+ // Command control socket configuration parameters for the Kea DHCPv6 server.
+ "control-socket": {
+ // Location of the UNIX domain socket file the DHCPv6 server uses
+ // to receive control commands from the Kea Control Agent or the
+ // local server administrator.
+ "socket-name": "/tmp/kea6-ctrl-socket",
+
+ // Control socket type used by the Kea DHCPv6 server. The 'unix'
+ // socket is currently the only supported type.
+ "socket-type": "unix"
+ },
+
+ // Specifies a prefix to be prepended to the generated Client FQDN.
+ // It may be specified at the global, shared-network, and subnet levels.
+ "ddns-generated-prefix": "myhost",
+
+ // Boolean flag indicating whether the server should ignore DHCP client
+ // wishes to update DNS on its own. With that flag set to true,
+ // the server will send DNS updates for both forward and
+ // reverse DNS data. The default value is false, which indicates
+ // that the server will delegate a DNS update to the client when
+ // requested. It may be specified at the global, shared-network,
+ // and subnet levels.
+ "ddns-override-client-update": false,
+
+ // Boolean flag indicating whether the server should override the DHCP
+ // client's wish to not update the DNS. With this parameter
+ // set to true, the server will send a DNS update even when
+ // the client requested no update. It may be specified at the
+ // global, shared-network, and subnet levels.
+ "ddns-override-no-update": false,
+
+ // Suffix appended to the partial name sent to the DNS. The
+ // default value is an empty string, which indicates that no
+ // suffix is appended. It may be specified at the global,
+ // shared-network, and subnet levels.
+ "ddns-qualifying-suffix": "",
+
+ // Enumeration specifying whether the server should honor
+ // the hostname or Client FQDN sent by the client or replace
+ // this name. The acceptable values are: "never" (use the
+ // name the client sent), "always" (replace the name the
+ // client sent), "when-present" (replace the name the client
+ // sent, but do not generate one when the client didn't send
+ // the name), "when-not-present" (generate the name when
+ // client didn't send one, otherwise leave the name the
+ // client sent). The default value is "never". It may be
+ // specified at the global, shared-network, and subnet levels.
+ "ddns-replace-client-name": "never",
+
+ // Boolean flag which enables or disables DDNS updating. It
+ // defaults to true. It may be specified at the global, shared-
+ // network, and subnet levels. It works in conjunction with
+ // dhcp-ddns:enable-updates, which must be true to enable connectivity
+ // to kea-dhcp-ddns.
+ "ddns-send-updates": true,
+
+ // Boolean flag, which when true instructs the server to always
+ // update DNS when leases are renewed, even if the DNS information
+ // has not changed. The server's default behavior (i.e. flag is false)
+ // is to only update DNS if the DNS information has changed. It
+ // may be specified at the global, shared-network, and subnet levels.
+ "ddns-update-on-renew": true,
+
+ // Boolean flag which is passed to kea-dhcp-ddns with each DDNS
+ // update request, to indicate whether DNS update conflict
+ // resolution as described in RFC 4703 should be employed for the
+ // given update request. The default value for this flag is true.
+ // It may be specified at the global, shared-network, and subnet levels.
+ // This field has been replaced by ddns-conflict-resolution-mode.
+ // Parsing is maintained only for backwards compatibility.
+ // "ddns-use-conflict-resolution": true,
+
+ // Enumeration, which is passed to kea-dhcp-ddns with each DDNS
+ // update request to indicate the mode used for resolving conflicts
+ // while performing DDNS updates. The acceptable values are:
+ // check-with-dhcid (this includes adding a DHCID record and checking
+ // that record via conflict detection as per RFC 4703,
+ // no-check-with-dhcid (this will ignore conflict detection but add
+ // a DHCID record when creating/updating an entry),
+ // check-exists-with-dhcid (this will check if there is an existing
+ // DHCID record but does not verify the value of the record matches
+ // the update. This will also update the DHCID record for the entry),
+ // no-check-without-dhcid (this ignores conflict detection and will
+ // not add a DHCID record when creating/updating a DDNS entry).
+ // The default value is "check-with-dhcid". It may be
+ // specified at the global, shared-network and subnet levels.
+ "ddns-conflict-resolution-mode": "check-with-dhcid",
+
+ // When greater than 0.0, it is the percent of the lease's lifetime
+ // to use for the DNS TTL.
+ "ddns-ttl-percent": 0.75,
+
+ // Time in seconds specifying how long a declined lease should be
+ // excluded from DHCP assignments. The default value is 24 hours.
+ "decline-probation-period": 86400,
+
+ // Name Change Request forwarding configuration for the Kea DHCPv6 server.
+ // NCRs are sent to the Kea D2 module to update DNS upon allocation of
+ // DHCP leases.
+ "dhcp-ddns": {
+ // Boolean flag indicating whether Kea DHCPv6 server should connect to
+ // kea-dhcp-ddns. This must be true for NCRs to be created and
+ // sent to kea-dhcp-ddns. By default, NCRs are not generated.
+ "enable-updates": false,
+
+ // Specifies maximum number of NCRs to queue waiting to be sent
+ // to the Kea D2 server.
+ "max-queue-size": 1024,
+
+ // Packet format to use when sending NCRs to the Kea D2 server.
+ // Currently, only JSON format is supported.
+ "ncr-format": "JSON",
+
+ // Socket protocol to use when sending NCRs to D2. Currently,
+ // only UDP is supported.
+ "ncr-protocol": "UDP",
+
+ // IP address that the Kea DHCPv6 server should use to send
+ // NCRs to D2. The default value of zero indicates that Kea
+ // should pick a suitable address.
+ "sender-ip": "::1",
+
+ // Port number that the Kea DHCPv6 server should use to send
+ // NCRs to D2. The default value of zero indicates that Kea
+ // should pick a suitable port.
+ "sender-port": 0,
+
+ // IP address on which D2 listens for NCRs.
+ "server-ip": "::1",
+
+ // Port number on which D2 listens for NCRs.
+ "server-port": 53001,
+
+ // The following parameters are DEPRECATED. They have been
+ // replaced with parameters that may be set at the global,
+ // shared-network, and subnet6 scopes. They are listed here
+ // as configuration parsing still accepts them. Eventually
+ // support for them will be removed.
+ "generated-prefix": "myhost",
+ "hostname-char-replacement": "x",
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+ "override-client-update": false,
+ "override-no-update": false,
+ "qualifying-suffix": "",
+ "replace-client-name": "never"
+ },
+
+ // Specifies the first of the two consecutive ports of the UDP
+ // sockets used for communication between DHCPv6 and DHCPv4
+ // servers. See RFC 7341.
+ "dhcp4o6-port": 0,
+
+ // Collection of Kea DHCPv6 server parameters configuring how
+ // the server should process expired DHCP leases.
+ "expired-leases-processing": {
+ // Specifies the number of seconds since the last removal of
+ // the expired leases, when the next removal should occur.
+ // If both "flush-reclaimed-timer-wait-time" and
+ // "hold-reclaimed-time" are not 0, when the client sends a release
+ // message the lease is expired instead of being deleted from
+ // lease storage.
+ "flush-reclaimed-timer-wait-time": 25,
+
+ // Specifies the length of time in seconds to keep expired
+ // leases in the lease database (lease affinity).
+ // If both "flush-reclaimed-timer-wait-time" and
+ // "hold-reclaimed-time" are not 0, when the client sends a release
+ // message the lease is expired instead of being deleted from
+ // lease storage.
+ "hold-reclaimed-time": 3600,
+
+ // Specifies the maximum number of expired leases that can be
+ // processed in a single attempt to clean up expired leases
+ // from the lease database. If there are more
+ // expired leases, they will be processed during the next
+ // cleanup attempt.
+ "max-reclaim-leases": 100,
+
+ // Specifies the maximum time in milliseconds that a single attempt
+ // to clean up expired leases from the lease database may take.
+ "max-reclaim-time": 250,
+
+ // Specifies the length of time in seconds since the last attempt
+ // to process expired leases before initiating the next attempt.
+ "reclaim-timer-wait-time": 10,
+
+ // Specifies the maximum number of expired lease-processing cycles
+ // which didn't result in full cleanup of exired leases from the
+ // lease database, after which a warning message is issued.
+ "unwarned-reclaim-cycles": 5
+ },
+
+ // List of hook libraries and their specific configuration parameters
+ // to be loaded by Kea DHCPv4 server.
+ "hooks-libraries": [
+ {
+ // Location of the hook library to be loaded.
+ "library": "/opt/lib/kea/hooks/libdhcp_lease_cmds.so",
+
+ // Hook library-specific configuration parameters.
+ "parameters": { }
+ }
+ ],
+
+ // List of access credentials to external sources of IPv6 reservations,
+ "hosts-databases": [
+ {
+ // Name of the database to connect to.
+ "name": "keatest",
+
+ // Host on which the database resides.
+ "host": "localhost",
+
+ // Database password.
+ "password": "keatest",
+
+ // Port on which the database is available.
+ "port": 3306,
+
+ // Type of database, e.g. "mysql", "postgresql".
+ "type": "mysql",
+
+ // Username to be used to access the database.
+ "user": "keatest",
+
+ // Read-only mode.
+ "readonly": false,
+
+ // The next entries are for OpenSSL support in MySQL.
+
+ // Trust anchor aka certificate authority file or directory.
+ "trust-anchor": "my-ca",
+
+ // Client certificate file name.
+ "cert-file": "my-cert",
+
+ // Private key file name.
+ "key-file": "my-key",
+
+ // Cipher list (see the OpenSSL ciphers command manual).
+ "cipher-list": "AES",
+
+ // Connection reconnect wait time.
+ // This parameter governs how long Kea waits before attempting
+ // to reconnect. Expressed in milliseconds. The default is 0
+ // (disabled) for MySQL and PostgreSQL.
+ "reconnect-wait-time": 3000,
+
+ // Connection maximum reconnect tries.
+ "max-reconnect-tries": 3,
+
+ // Action to take when connection recovery fails.
+ // Supported values: stop-retry-exit, serve-retry-exit,
+ // serve-retry-continue
+ "on-fail": "stop-retry-exit",
+
+ // Flag which indicates if the DB recovery should be attempted
+ // at server startup and on reconfiguration events.
+ "retry-on-startup": false,
+
+ // Connection connect timeout in seconds.
+ "connect-timeout": 100,
+
+ // Timeout of database read operations in seconds.
+ "read-timeout": 120,
+
+ // Timeout of database write operations in seconds.
+ "write-timeout": 180
+ },
+ {
+ // Name of the database to connect to.
+ "name": "keatest",
+
+ // Host on which the database resides.
+ "host": "localhost",
+
+ // Database password.
+ "password": "keatest",
+
+ // Port on which the database is available.
+ "port": 5432,
+
+ // Type of database, e.g. "mysql", "postgresql".
+ "type": "postgresql",
+
+ // Username to be used to access the database.
+ "user": "keatest",
+
+ // TCP user timeout while communicating with the database.
+ // It is specified in seconds.
+ "tcp-user-timeout": 100
+ }
+ ],
+
+ // List of host reservation identifier types to be used by the
+ // Kea DHCPv6 server to fetch static reservations for
+ // DHCP clients. All identifiers are used by default, which
+ // means that the server will issue multiple queries to the
+ // database to find if there is a reservation for a particular
+ // client. If a particular deployment uses only a subset, e.g.
+ // one identifier type, this identifier should be only listed
+ // here to prevent unnecessary queries to the database.
+ "host-reservation-identifiers": [
+ "hw-address",
+ "duid",
+ "flex-id"
+ ],
+
+ // Specifies configuration of interfaces on which the Kea DHCPv6
+ // server is listening to the DHCP queries.
+ "interfaces-config": {
+ // Specifies a list of interfaces on which the Kea DHCPv6
+ // server should listen to DHCP requests.
+ "interfaces": [
+ "eth0"
+ ],
+
+ // Boolean flag indicating whether the available interfaces should
+ // be re-detected upon server reconfiguration. The default value
+ // is true, which means that the interfaces are always
+ // re-detected.
+ "re-detect": true,
+
+ // Kea tries to bind the service sockets during initialization, but it may
+ // fail due to a port being already opened or a misconfiguration. Kea can
+ // suppress these errors and only log them. This flag prevents starting
+ // the DHCP server without binding all sockets. If unspecified, it
+ // defaults to false.
+ "service-sockets-require-all": true,
+
+ // Kea tries to bind the service sockets during initialization. This
+ // option specifies how many times binding to interface will be retried.
+ // The default value is 0, which means that the operation will not be
+ // repeated.
+ "service-sockets-max-retries": 5,
+
+ // The time interval in milliseconds to wait before the next attempt to
+ // retry opening a service socket.
+ "service-sockets-retry-wait-time": 5000
+ },
+
+ // Boolean parameter which controls whether an early global host
+ // reservations lookup should be performed. This lookup takes place
+ // before subnet selection and when a global reservation is found
+ // with some client classes, it triggers a second phase classification.
+ // It can also be used to drop queries using host reservations as a
+ // decision table indexed by reservation identifiers.
+ "early-global-reservations-lookup": true,
+
+ // Boolean parameter which controls the DHCP server's behavior with respect
+ // to creating host reservations for the same IP address or delegated
+ // prefix. By default this flag is set to true in which case the server
+ // prevents creation of multiple host reservations for the same IP address
+ // or delegated prefix. When this parameter is set to false, the server
+ // allows for creating multiple reservations for the same IP address or
+ // delegated prefix within a subnet. This setting is useful in deployments
+ // in which a given host may be communicating with a DHCP server over
+ // multiple interfaces and depending on the chosen interface different
+ // MAC address (or other identifier) will be used to identify the host.
+ // Note that some host backends do not support the mode in which multiple
+ // reservations for the same IP address or delegated prefix are used.
+ // If these backends are in use and this setting is attempted a
+ // configuration error will occur. The MySQL and PostgreSQL backends do
+ // support this mode.
+ "ip-reservations-unique": true,
+
+ // Boolean parameter which controls whether host reservations lookup
+ // should be performed before lease lookup. This parameter has effect
+ // only when multi-threading is disabled. When multi-threading is
+ // enabled, host reservations lookup is always performed first to avoid
+ // lease-lookup resource locking.
+ "reservations-lookup-first": true,
+
+ // Specifies credentials to access lease database.
+ "lease-database": {
+ // memfile backend-specific parameter specifying the interval
+ // in seconds at which the lease file should be cleaned up (outdated
+ // lease entries are removed to prevent the lease file from growing
+ // infinitely).
+ "lfc-interval": 3600,
+
+ // Maximum number of lease-file read errors allowed before
+ // loading the file is abandoned. Defaults to 0 (no limit).
+ "max-row-errors": 100,
+
+ // Name of the lease file. In the case of a database it specifies the
+ // database name.
+ "name": "/tmp/kea-dhcp6.csv",
+
+ // memfile-specific parameter indicating whether leases should
+ // be saved on persistent storage (disk) or not. The true value
+ // is the default and it indicates that leases are stored in
+ // persistent storage. This setting must be used in production.
+ // The false value should only be used for testing purposes
+ // because non-stored leases will be lost upon Kea server restart.
+ "persist": true,
+
+ // Lease database backend type, i.e. "memfile", "mysql" or
+ // "postgresql".
+ "type": "memfile"
+ },
+
+ // List of parameters indicating how the client's MAC address can be
+ // inferred from the DHCP query. Supported values are listed in the
+ // Kea Administrator Reference Manual.
+ "mac-sources": [ "duid" ],
+
+ // List of global DHCP options that the Kea DHCPv6 server assigns to
+ // clients.
+ "option-data": [
+ {
+ // Boolean flag indicating whether the given option is always
+ // sent in response or only when requested. The default
+ // value of false indicates that it is only sent when
+ // requested.
+ "always-send": false,
+
+ // Option code. It is not required if the option name is
+ // provided.
+ "code": 23,
+
+ // Boolean value indicating whether the option data specified
+ // in the "data" field is specified as a string of hexadecimal
+ // digits or in human-readable CSV format.
+ "csv-format": true,
+
+ // Option data to be stored in the option payload.
+ "data": "2001:db8:2::45, 2001:db8:2::100",
+
+ // Option name. It is not required if the option code is
+ // provided.
+ "name": "dns-servers",
+
+ // Boolean flag indicating whether the given option is never
+ // sent in response. The default value of false indicates
+ // that it is sent when it should be. When true, the option
+ // is not sent despite any other setting, i.e. it is
+ // a final flag.
+ "never-send": false,
+
+ // Option space. The default is the "dhcp6" option space which
+ // groups top-level DHCPv6 options.
+ "space": "dhcp6"
+ }
+ ],
+
+ // List of global option definitions, i.e. option formats, that the
+ // Kea DHCPv6 server is using.
+ "option-def": [
+ {
+ // Boolean flag indicating whether the option definition comprises
+ // an array of values of some type, e.g. an array of IPv6 addresses.
+ // The default value of false means that the option does not
+ // comprise an array of values.
+ "array": false,
+
+ // Option code.
+ "code": 6,
+
+ // Holds a name of the option space encapsulated by this option.
+ // All options that belong to this option space will be sent
+ // as sub-options of this option. An empty string means that this
+ // option doesn't encapsulate any option.
+ "encapsulate": "",
+
+ // Option name.
+ "name": "my-option",
+
+ // Specifies the types of fields within the option if the option
+ // is said to be a "record" (see "type"). In this particular example
+ // this option comprises two fields, 1 byte and 2 bytes long.
+ "record-types": "uint8, uint16",
+
+ // Name of the option space to which this option belongs.
+ "space": "my-space",
+
+ // Option type. All possible types are listed in the Kea
+ // Administrator Reference Manual.
+ "type": "record"
+ }
+ ],
+
+ // Global value which limits the number of client packets (e.g.
+ // REQUESTs,RENEWs...) that may be parked while waiting for
+ // hook library work to complete, prior to a response (e.g. REPLY)
+ // being sent back to the client. A typical example is when kea-dhcp6
+ // parks a REQUEST while it sends the lease update(s) to its
+ // HA peer(s). The packet is unparked once the update(s) have been
+ // acknowledged. This value limits the number of packets that can
+ // be held pending the updates. In times of heavy client traffic,
+ // this value can keep kea-dhcp6 from building an insurmountable
+ // backlog of updates.
+ "parked-packet-limit": 128,
+
+ // Global (default) value of the preferred lifetime.
+ "preferred-lifetime": 50,
+
+ // Global min value of the preferred lifetime.
+ "min-preferred-lifetime": 40,
+
+ // Global max value of the preferred lifetime.
+ "max-preferred-lifetime": 60,
+
+ // Global value for the rebind timer, i.e. the time after which the
+ // DHCP client enters the rebind state if it fails to renew the lease.
+ "rebind-timer": 40,
+
+ // List of relay supplied option codes. See RFC 6422.
+ "relay-supplied-options": [ "110", "120", "130" ],
+
+ // Global value for the renew timer, i.e. the time after which the
+ // DHCP client renews the lease.
+ "renew-timer": 30,
+
+ // Global value to store extended information (e.g. relay agent
+ // information) with each lease.
+ "store-extended-info": true,
+
+ // Statistics keep some samples per observation point.
+ // There are two default values: maximum count and maximum age.
+ // Setting the maximum count to zero disables it.
+ "statistic-default-sample-count": 0,
+
+ // When the maximum count is 0 the maximum age (in seconds) applies.
+ "statistic-default-sample-age": 60,
+
+ // Multi-threading parameters.
+ "multi-threading": {
+ // By default, Kea processes packets on multiple threads if the hardware permits.
+ "enable-multi-threading": true,
+
+ // When multi-threading is enabled, Kea will process packets on a
+ // number of multiple threads configurable through this option. The
+ // value must be a positive integer (0 means auto-detect).
+ "thread-pool-size": 0,
+
+ // When multi-threading is enabled, Kea will read packets from the
+ // interface and append a working item to the thread pool. This
+ // option configures the maximum number of items that can be queued.
+ // The value must be a positive integer (0 means unlimited).
+ "packet-queue-size": 0
+ },
+
+ // Governs how the Kea DHCPv6 server should deal with invalid
+ // data received from the client.
+ "sanity-checks": {
+ // Specifies how the Kea DHCPv6 server should behave when invalid
+ // data is read for a lease from the lease file. The following
+ // values are supported: "none" (don't attempt to correct the
+ // lease information), "warn" (print a warning for subnet-id
+ // related inconsistencies), "fix" (correct the subnet id by
+ // trying to find the suitable subnet), "fix-del" (similar
+ // to "fix" but delete the lease if no suitable subnet found),
+ // "del" (delete the lease if the lease has invalid subnet
+ // identifier value).
+ "lease-checks": "warn",
+
+ // Specifies how Kea DHCPv4 server should behave when invalid
+ // extended info is read for a lease from the lease file, or
+ // whether to upgrade from the old format. The following values
+ // are supported: "none" (don't attempt to correct or upgrade
+ // the extended info), "fix" (fix common inconsistencies and
+ // upgrade from the old format; this is the default), "strict"
+ // (fix inconsistencies with an impact on Leasequery),
+ // "pedantic" (enforce full Kea code format).
+ "extended-info-checks": "fix"
+ },
+
+ // Custom DUID used by the DHCPv6 server.
+ "server-id": {
+ // Type of the DUID. Possible values are "LLT", "EN", and "LL".
+ "type": "EN",
+
+ // Enterprise id used for "EN" duid.
+ "enterprise-id": 2495,
+
+ // Identifier part of the DUID.
+ "identifier": "0123456789",
+
+ // Boolean flag indicating whether the DUID should be persisted on
+ // disk.
+ "persist": false
+ },
+
+ // List of shared networks used by the Kea DHCPv6 server. The shared
+ // networks group subnets together.
+ "shared-networks": [
+ {
+ // A flag selecting an IP address allocation strategy for all
+ // subnets in this shared network.
+ "allocator": "random",
+
+ // A flag selecting a delegated prefix allocation strategy for
+ // all subnets in this shared network.
+ "pd-allocator": "iterative",
+
+ // Restricts this shared network to allow only clients
+ // that belong to a particular client class. If an
+ // empty string is provided, no restriction is applied.
+ "client-class": "",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-generated-prefix": "myhost",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-override-client-update": false,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-override-no-update": false,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-qualifying-suffix": "",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-replace-client-name": "never",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-send-updates": true,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-update-on-renew": true,
+
+ // Shared-network level value. See description at the global level.
+ // This field has been replaced by ddns-conflict-resolution-mode.
+ // Parsing is maintained only for backwards compatibility.
+ // "ddns-use-conflict-resolution": true,
+
+ // Shared-network level value. See description at the global level.
+ "ddns-conflict-resolution-mode": "check-with-dhcid",
+
+ // Shared-network level value. See description at the global level.
+ "ddns-ttl-percent": 0.65,
+
+ // Shared-network level value. See description at the global level.
+ "hostname-char-replacement": "x",
+
+ // Shared-network level value. See description at the global level.
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+
+ // Specifies that this shared network is selected for
+ // requests received on a particular interface.
+ "interface": "eth0",
+
+ // Specifies the content of the interface-id option used
+ // by relays to identify the interface on the relay to
+ // which the response is sent.
+ "interface-id": "",
+
+ // Shared network name.
+ "name": "my-secret-network",
+
+ // List of shared network-specific DHCP options.
+ "option-data": [],
+
+ // Shared network-specific (default) preferred lifetime.
+ "preferred-lifetime": 2000,
+
+ // Shared network-specific min preferred lifetime.
+ "min-preferred-lifetime": 1500,
+
+ // Shared network-specific ma xpreferred lifetime.
+ "max-preferred-lifetime": 2500,
+
+ // Boolean flag indicating whether the server can respond to
+ // a Solicit message including a Rapid Commit option with
+ // the Reply message (See DHCPv6 rapid commit).
+ "rapid-commit": false,
+
+ // List of IPv6 relay addresses for which this shared
+ // network is selected.
+ "relay": {
+ "ip-addresses": []
+ },
+
+ // Shared-network level rebind timer.
+ "rebind-timer": 41,
+
+ // Shared-network level renew timer.
+ "renew-timer": 31,
+
+ // Shared-network level compute T1 and T2 timers.
+ "calculate-tee-times": true,
+
+ // T1 = valid lifetime * .5.
+ "t1-percent": .5,
+
+ // T2 = valid lifetime * .75.
+ "t2-percent": .75,
+
+ // Cache threshold = valid lifetime * .25.
+ "cache-threshold": .25,
+
+ // Cache maximum: when the client last-transmission time
+ // is close enough, the lease is not renewed and the current
+ // lease is returned as it was "cached".
+ "cache-max-age": 1000,
+
+ // Enumeration specifying the server's mode of operation when it
+ // fetches host reservations.
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and "reservations-out-of-pool"
+ // parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ // If specified, it is inherited by "subnet6" levels.
+ "reservations-out-of-pool": false,
+
+ // List of client classes which must be evaluated when this shared
+ // network is selected for client assignments.
+ "require-client-classes": [ "late" ],
+
+ // Turn off storage of extended information (e.g. relay agent
+ // information) with each lease for this shared network.
+ "store-extended-info": false,
+
+ // List of IPv6 subnets belonging to this shared network.
+ "subnet6": [
+ {
+ // A flag selecting an IP address allocation strategy for
+ // the subnet.
+ "allocator": "iterative",
+
+ // A flag selecting a delegated prefix allocation strategy
+ // for the subnet.
+ "pd-allocator": "iterative",
+
+ // Restricts this subnet to allow only clients that belong
+ // to a particular client class. If an empty string is
+ // provided, no restriction is applied.
+ "client-class": "",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-generated-prefix": "myhost",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-override-client-update": false,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-override-no-update": false,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-qualifying-suffix": "",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-replace-client-name": "never",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-send-updates": true,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-update-on-renew": true,
+
+ // Subnet-level value. See description at the global level.
+ // This field has been replaced by ddns-conflict-resolution-mode.
+ // Parsing is maintained only for backwards compatibility.
+ // "ddns-use-conflict-resolution": true,
+
+ // Subnet-level value. See description at the global level.
+ "ddns-conflict-resolution-mode": "check-with-dhcid",
+
+ // Subnet-level value. See description at the global level.
+ "ddns-ttl-percent": 0.55,
+
+ // Subnet-level value. See description at the global level.
+ "hostname-char-replacement": "x",
+
+ // Subnet-level value. See description at the global level.
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+
+ // Subnet unique identifier.
+ "id": 1,
+
+ // Specifies that this subnet is selected for requests
+ // received on a particular interface.
+ "interface": "eth0",
+
+ // Specifies the content of the interface-id option used
+ // by relays to identify the interface on the relay to
+ // which the response is sent.
+ "interface-id": "",
+
+ // Turn on storage of extended information (e.g. relay agent
+ // information) with each lease for this subnet.
+ "store-extended-info": true,
+
+ // Subnet-level list of DHCP options.
+ "option-data": [
+ {
+ // Boolean flag indicating whether the particular option
+ // should be always sent or sent only when requested.
+ "always-send": false,
+
+ // Option code.
+ "code": 7,
+
+ // Boolean flag indicating whether the option value specified
+ // in "data" is a string of hexadecimal values or human-readable
+ // CSV value.
+ "csv-format": false,
+
+ // Option data to be included in the option payload.
+ "data": "0xf0",
+
+ // Option name.
+ "name": "preference",
+
+ // Boolean flag indicating whether the given option is never
+ // sent in response.
+ "never-send": false,
+
+ // Option space. The default value "dhcp6" designates the
+ // top level option space.
+ "space": "dhcp6"
+ }
+ ],
+
+ // List of pools from which delegated prefixes are assigned to the
+ // clients.
+ "pd-pools": [
+ {
+ // Restricts this prefix pool to be used only for the client
+ // requests belonging to a particular client class.
+ "client-class": "phones_server1",
+
+ // Length of prefixes delegated to clients.
+ "delegated-len": 64,
+
+ // Excluded prefix (address) from client assignments.
+ "excluded-prefix": "2001:db8:1::",
+
+ // Excluded prefix (length) from client assignments.
+ "excluded-prefix-len": 72,
+
+ // Prefix pool level list of DHCP options.
+ "option-data": [],
+
+ // Prefix range (address) used for client assignments.
+ "prefix": "2001:db8:1::",
+
+ // Prefix range (length) used for client assignments.
+ "prefix-len": 48,
+
+ // List of client classes which must be evaluated
+ // when this prefix pool is selected for client assignments.
+ "require-client-classes": []
+ }
+ ],
+
+ // List of IP address pools belonging to the subnet.
+ "pools": [
+ {
+ // Restricts this pool to only be used for client
+ // requests belonging to a particular client class.
+ "client-class": "phones_server1",
+
+ // Pool-level list of DHCP options.
+ "option-data": [],
+
+ // Address range used for client assignments.
+ "pool": "2001:db8:0:1::/64",
+
+ // List of client classes which must be evaluated when this pool
+ // is selected for client assignments.
+ "require-client-classes": [ "late" ]
+ },
+ {
+ // Restricts this pool to only be used for client
+ // requests belonging to a particular client class.
+ "client-class": "phones_server2",
+
+ // Pool-level list of DHCP options.
+ "option-data": [],
+
+ // Address range used for client assignments.
+ "pool": "2001:db8:0:3::/64",
+
+ // List of client classes which must be evaluated when this pool
+ // is selected for client assignments.
+ "require-client-classes": [],
+
+ // Pool identifier used to enable statistics for this pool.
+ // The pool ID does not need to be unique within the subnet
+ // or across subnets.
+ // If not unconfigured, it defaults to 0. The statistics
+ // regarding this pool will be combined with the other statistics
+ // of all other pools with the same pool ID in this subnet.
+ "pool-id": 1
+ }
+ ],
+
+ // Subnet specific (default) preferred lifetime.
+ "preferred-lifetime": 2000,
+
+ // Subnet specific min preferred lifetime.
+ "min-preferred-lifetime": 1500,
+
+ // Subnet specific max referred lifetime.
+ "max-preferred-lifetime": 2500,
+
+ // Boolean flag indicating whether the server can respond to
+ // a Solicit message including a Rapid Commit option with
+ // the Reply message (See DHCPv6 rapid commit).
+ "rapid-commit": false,
+
+ // Subnet-level value of the rebind timer.
+ "rebind-timer": 40,
+
+ // List of IPv6 relay addresses for which this subnet is selected.
+ "relay": {
+ "ip-addresses": [
+ "2001:db8:0:f::1"
+ ]
+ },
+
+ // Subnet-level renew timer.
+ "renew-timer": 30,
+
+ // Enumeration specifying the server's mode of operation when it
+ // fetches host reservations.
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and
+ // "reservations-out-of-pool" parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved
+ // addresses are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ "reservations-out-of-pool": false,
+
+ // Subnet-level compute T1 and T2 timers.
+ "calculate-tee-times": true,
+
+ // T1 = valid lifetime * .5.
+ "t1-percent": .5,
+
+ // T2 = valid lifetime * .75.
+ "t2-percent": .75,
+
+ // Cache threshold = valid lifetime * .25.
+ "cache-threshold": .25,
+
+ // Subnet-level cache maximum.
+ "cache-max-age": 1000,
+
+ // List of static IPv6 reservations assigned to clients belonging
+ // to this subnet. For a detailed example, see reservations.json.
+ "reservations": [
+ {
+ // Identifier used for client matching. Supported values are
+ // "duid", "hw-address" and "flex-id".
+ "duid": "01:02:03:04:05:06:07:08:09:0A",
+
+ // List of reserved IPv6 addresses.
+ "ip-addresses": [ "2001:db8:1:cafe::1" ],
+
+ // List of reserved IPv6 prefixes.
+ "prefixes": [ "2001:db8:2:abcd::/64" ],
+
+ // Reserved hostname.
+ "hostname": "foo.example.com",
+
+ // Reservation-specific option data.
+ "option-data": [
+ {
+ // Option name.
+ "name": "vendor-opts",
+
+ // Option value.
+ "data": "4491"
+ }
+ ]
+ }
+ ],
+
+ // List of client classes which must be evaluated when this subnet
+ // is selected for client assignments.
+ "require-client-classes": [ "late" ],
+
+ // Subnet prefix.
+ "subnet": "2001:db8::/32",
+
+ // Subnet-level (default) valid lifetime.
+ "valid-lifetime": 6000,
+
+ // Subnet-level min valid lifetime.
+ "min-valid-lifetime": 4000,
+
+ // Subnet-level max valid lifetime.
+ "max-valid-lifetime": 8000
+ }
+ ],
+
+ // Shared-network level (default) valid lifetime.
+ "valid-lifetime": 6001,
+
+ // Shared-network level min valid lifetime.
+ "min-valid-lifetime": 4001,
+
+ // Shared-network level max valid lifetime.
+ "max-valid-lifetime": 8001
+ }
+ ],
+
+ // List of IPv6 subnets which don't belong to any shared network.
+ "subnet6": [],
+
+ // Global valid lifetime value.
+ "valid-lifetime": 6000,
+
+ // Global min valid lifetime value.
+ "min-valid-lifetime": 4000,
+
+ // Global max valid lifetime value.
+ "max-valid-lifetime": 8000,
+
+ // Reservations (examples are in other files).
+ "reservations": [],
+
+ // Configuration control (currently not used, i.e. this syntax
+ // is already defined but the corresponding feature is not implemented).
+ "config-control": {
+ // Only the configuration databases entry is defined.
+ "config-databases": [
+ {
+ // Name of the database to connect to.
+ "name": "config",
+
+ // Type of database, e.g. "mysql", "postgresql".
+ "type": "mysql"
+ }
+ ],
+ // Interval between attempts to fetch configuration updates
+ // via the configuration backends used.
+ "config-fetch-wait-time": 30
+ },
+
+ // Server tag.
+ "server-tag": "my DHCPv6 server",
+
+ // DHCP queue-control parameters.
+ "dhcp-queue-control": {
+ // Enable queue is mandatory.
+ "enable-queue": true,
+
+ // Queue type is mandatory.
+ "queue-type": "kea-ring6",
+
+ // Capacity is optional.
+ "capacity": 64
+ },
+
+ // Fetches host reservations.
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and "reservations-out-of-pool" parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ // If specified, it is inherited by "shared-networks" and
+ // "subnet6" levels.
+ "reservations-out-of-pool": false,
+
+ // Data directory.
+ "data-directory": "/tmp",
+
+ // Global compute T1 and T2 timers.
+ "calculate-tee-times": true,
+
+ // T1 = valid lifetime * .5.
+ "t1-percent": .5,
+
+ // T2 = valid lifetime * .75.
+ "t2-percent": .75,
+
+ // Cache threshold = valid lifetime * .25.
+ "cache-threshold": .25,
+
+ // Global cache maximum.
+ "cache-max-age": 1000,
+
+ // String of zero or more characters with which to replace each
+ // invalid character in the Client FQDN. The default
+ // value is an empty string, which will cause invalid characters
+ // to be omitted rather than replaced.
+ "hostname-char-replacement": "x",
+
+ // Regular expression describing the invalid character set in
+ // the Client FQDN.
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+
+ // List of loggers used by the servers using this configuration file.
+ "loggers": [
+ {
+ // Debug level, a value between 0..99. The greater the value
+ // the more detailed the debug log.
+ "debuglevel": 99,
+
+ // Name of the logger.
+ "name": "kea-dhcp6",
+
+ // Configures how the log should be output.
+ "output-options": [
+ {
+ // Determines whether the log should be flushed to a file.
+ "flush": true,
+
+ // Specifies maximum filesize before the file is rotated.
+ "maxsize": 10240000,
+
+ // Specifies the maximum number of rotated files to be kept.
+ "maxver": 1,
+
+ // Specifies the logging destination.
+ "output": "stdout",
+
+ // Specifies log entry content
+ "pattern": "%D{%Y-%m-%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+ }
+ ],
+
+ // Specifies logging severity, i.e. "ERROR", "WARN", "INFO", "DEBUG".
+ "severity": "INFO"
+ }
+ ],
+
+ // Look at advanced examples for the use of user-contexts.
+ "user-context": { }
+ }
+}
diff --git a/doc/examples/kea6/all-options.json b/doc/examples/kea6/all-options.json
new file mode 100644
index 0000000..e3058f3
--- /dev/null
+++ b/doc/examples/kea6/all-options.json
@@ -0,0 +1,2179 @@
+// This example configuration file for DHCPv6 server in Kea contains:
+//
+// - data for all the standard options
+// - custom option definitions at global level along with some associated
+// option data
+// - custom option data with standardized option spaces other than "dhcp6"
+// - custom option spaces
+// - option embedding examples
+// - DOCSIS3 option data
+//
+// The reader is strongly encouraged to take a look at the option formats
+// documented in the Kea ARM:
+// https://kea.readthedocs.io/en/latest/arm/dhcp6-srv.html?highlight=option%20definitions#dhcp6-std-options-list
+//
+// Other options require special logic which is not yet implemented. They are
+// marked with:
+// "Note: special logic not implemented"
+
+{
+ "Dhcp6": {
+ /*
+ Data for all standard option definitions
+ */
+ // Option data defined globally
+ "option-data": [
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_PREFERENCE | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | pref-value |
+ +-+-+-+-+-+-+-+-+
+
+ option-code OPTION_PREFERENCE (7).
+
+ option-len 1.
+
+ pref-value The preference value for the server in this message.
+ */
+ // Type: uint8
+ {
+ "code": 7,
+ "data": "0xf0",
+ "name": "preference"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_UNICAST | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | server-address |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_UNICAST (12).
+
+ option-len 16.
+
+ server-address The IP address to which the client should send
+ messages delivered using unicast.
+ */
+ // Type: IPv6 address
+ {
+ "code": 12,
+ "data": "2001:db8::2",
+ "name": "unicast"
+ },
+
+ /*
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_VENDOR_OPTS | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | enterprise-number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ . .
+ . option-data .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_VENDOR_OPTS (17)
+
+ option-len 4 + length of option-data field
+
+ enterprise-number The vendor's registered Enterprise Number as
+ registered with IANA [6].
+
+ option-data An opaque object of option-len octets,
+ interpreted by vendor-specific code on the
+ clients and servers
+ */
+ // Type: uint32
+ // The vendor options are not standardized and are specific to each
+ // vendor. The vendors are identified with the enterprise number,
+ // sometimes also called vendor-id or enterprise-id. For example,
+ // CableLabs that specified DOCSIS options, use 4491. Some vendors
+ // have their own mechanisms. For example, DOCSIS vendor sub-option 1
+ // is an equivalent of ORO for normal DHCPv6 options. Usually there
+ // are several vendor sub-options defined within. See the ARM section:
+ // https://kea.readthedocs.io/en/latest/arm/dhcp6-srv.html#dhcpv6-vendor-specific-options
+
+ {
+ "code": 17,
+ "data": "4294967295",
+ "name": "vendor-opts"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_RECONF_ACCEPT | 0 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_RECONF_ACCEPT (20).
+
+ option-len 0.
+ */
+ // Type: empty
+ {
+ "code": 20,
+ "name": "reconf-accept"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_SIP_SERVER_D | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | SIP Server Domain Name List |
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ SIP Server Domain Name List: The domain names of the SIP outbound
+ proxy servers for the client to use. The domain names are encoded
+ as specified in Section 8 ("Representation and use of domain
+ names") of the DHCPv6 specification [1].
+ */
+ // Type: array of {FQDN}
+ {
+ "code": 21,
+ "data": "sip1.server.net, sip2.server.net",
+ "name": "sip-server-dns"
+ },
+
+ /*
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_SIP_SERVER_A | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | SIP server (IP address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | SIP server (IP address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_SIP_SERVER_A (22)
+
+ option-length: Length of the 'options' field in octets; must be a
+ multiple of 16.
+
+ SIP server: IPv6 address of a SIP server for the client to use.
+ The servers are listed in the order of preference for
+ use by the client.
+ */
+ // Type: array of {IPv6 address}
+ {
+ "code": 22,
+ "data": "2001:db8::3, 2001:db8::4",
+ "name": "sip-server-addr"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_DNS_SERVERS | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | DNS-recursive-name-server (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | DNS-recursive-name-server (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_DNS_SERVERS (23)
+
+ option-len: Length of the list of DNS recursive name
+ servers in octets; must be a multiple of
+ 16
+
+ DNS-recursive-name-server: IPv6 address of DNS recursive name server
+ */
+ // Type: array of {IPv6 address}
+ {
+ "code": 23,
+ "data": "2001:db8::5, 2001:db8::6",
+ "name": "dns-servers"
+ },
+
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_DOMAIN_LIST | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | searchlist |
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_DOMAIN_LIST (24)
+
+ option-len: Length of the 'searchlist' field in octets
+
+ searchlist: The specification of the list of domain names in the
+ Domain Search List
+ */
+ // Type: array of {FQDN}
+ {
+ "code": 24,
+ "data": "example.com, example.org",
+ "name": "domain-search"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_NIS_SERVERS | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | NIS server (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | NIS server (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_NIS_SERVERS (27)
+
+ option-len: Length of the 'NIS server' fields in octets; It must be
+ a multiple of 16
+
+ NIS server: IPv6 address of NIS server
+ */
+ // Type: array of {IPv6 address}
+ {
+ "code": 27,
+ "data": "2001:db8::7, 2001:db8::8",
+ "name": "nis-servers"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_NISP_SERVERS | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | NIS+ server (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | NIS+ server (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_NISP_SERVERS (28)
+
+ option-len: Length of the 'NIS+ server' fields in octets; It must be
+ a multiple of 16
+
+ NIS+ server: IPv6 address of NIS+ server
+ */
+ // Type: array of {IPv6 address}
+ {
+ "code": 28,
+ "data": "2001:db8::9, 2001:db8::10",
+ "name": "nisp-servers"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_NIS_DOMAIN_NAME | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | nis-domain-name |
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_NIS_DOMAIN_NAME (29)
+
+ option-len: Length of the 'nis-domain-name' field in octets
+
+ nis-domain-name: NIS Domain name for client
+ */
+ // Type: array of {FQDN}
+ {
+ "code": 29,
+ "data": "nis1.example.org, nis2.example.org",
+ "name": "nis-domain-name"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_NISP_DOMAIN_NAME | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | nisp-domain-name |
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_NISP_DOMAIN_NAME (30)
+
+ option-len: Length of the 'nisp-domain-name' field in octets
+
+ nisp-domain-name: NIS+ Domain name for client
+ */
+ // Type: array of {FQDN}
+ {
+ "code": 30,
+ "data": "nisp1.example.org, nisp2.example.org",
+ "name": "nisp-domain-name"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_SNTP_SERVERS | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | SNTP server (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | SNTP server (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_SNTP_SERVERS (31)
+
+ option-len: Length of the 'SNTP server' fields, in octets;
+ it must be a multiple of 16
+
+ SNTP server: IPv6 address of SNTP server
+ */
+ // Type: array of {IPv6 address}
+ {
+ "code": 31,
+ "data": "2001:db8::11, 2001:db8::12",
+ "name": "sntp-servers"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |OPTION_INFORMATION_REFRESH_TIME| option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | information-refresh-time |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_INFORMATION_REFRESH_TIME (32).
+
+ option-len 4.
+
+ information-refresh-time Time duration relative to the current
+ time, expressed in units of seconds. A
+ 4-octet field containing an unsigned
+ integer.
+ */
+ // Type: uint32
+ {
+ "code": 32,
+ "data": "3600",
+ "name": "information-refresh-time"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_BCMCS_SERVER_D | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | BCMCS Control Server Domain Name List |
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_BCMCS_SERVER_D (33).
+
+ option-length: Length of the 'BCMCS Control Server Domain Name List'
+ field in octets; variable.
+
+ BCMCS Control Server Domain Name List: Identical format as in Section
+ 4.1 (except the Code and Len fields).
+ */
+ // Type: array of {FQDN}
+ {
+ "code": 33,
+ "data": "bcmcs1.example.org, bcmcs2.example.org",
+ "name": "bcmcs-server-dns"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_BCMCS_SERVER_A | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | BCMCS Control server-1 address (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | BCMCS Control server-2 address (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_BCMCS_SERVER_A (34).
+
+ option-length: Length of the 'BCMCS Control Server IPv6 address'
+ field in octets; variable.
+ */
+ // Type: array of {IPv6 address}
+ {
+ "code": 34,
+ "data": "2001:db8::13, 2001:db8::14",
+ "name": "bcmcs-server-addr"
+ },
+
+ // Option code 35 is unassigned.
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_GEOCONF_CIVIC | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | what | country code | .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ .
+ . civic address elements .
+ . ... .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_GEOCONF_CIVIC (36)
+
+ option-len: Length of the Countrycode, 'what' and civic address
+ elements in octets.
+
+ what: The 'what' element describes to which location the DHCP entry
+ refers. Currently, three options are defined: the location of the
+ DHCP server (a value of 0), the location of the network element
+ believed to be closest to the client (a value of 1), or the
+ location of the client (a value of 2). Option (2) SHOULD be used,
+ but may not be known. Options (0) and (1) SHOULD NOT be used
+ unless it is known that the DHCP client is in close physical
+ proximity to the server or network element.
+
+ country code: The two-letter ISO 3166 country code in capital ASCII
+ letters, e.g., DE or US. (Civic addresses always contain country
+ designations, suggesting the use of a fixed-format field to save
+ space.)
+
+ civic address elements: Zero or more elements comprising the civic
+ and/or postal address, with the format described below
+ (Section 3.3).
+ */
+ // Type: uint8, uint16, array of {binary}
+ {
+ "code": 36,
+ // 0x5553 is "US" in UTF-8
+ "data": "0, 0x5553, 15 9D, A3 FF",
+ "name": "geoconf-civic"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_REMOTE_ID | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | enterprise-number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ . .
+ . remote-id .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_REMOTE_ID (37)
+
+ option-len 4 + the length, in octets, of the remote-id
+ field. The minimum option-len is 5 octets.
+
+ enterprise-number The vendor's registered Enterprise Number as
+ registered with IANA [5].
+
+ remote-id The opaque value for the remote-id.
+ */
+ // Type: uint32, binary
+ {
+ "code": 37,
+ "data": "4294967295, 1A BB AD AB BA D0 00 00 00 00 00 00 00 00 CA FE",
+ "name": "remote-id"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_FQDN | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | flags | |
+ +-+-+-+-+-+-+-+-+ |
+ . .
+ . domain-name .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_CLIENT_FQDN (39)
+
+ option-len 1 + length of domain name
+
+ flags flag bits used between client and server to
+ negotiate who performs which updates
+
+ domain-name the partial or fully qualified domain name
+ (with length option-len - 1)
+ */
+ // Type: uint8, FQDN
+ {
+ "code": 39,
+ "data": "224, client.example.org",
+ "name": "client-fqdn"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ + +
+ | |
+ + PAA IPv6 Address +
+ | |
+ + +
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | .... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_PANA_AGENT (40).
+
+ option-length: Length of the 'options' field in octets;
+ MUST be a multiple of sixteen (16).
+
+ PAA IPv6 Address: IPv6 address of a PAA for the client to use.
+ The PAAs are listed in the order of preference
+ for use by the client.
+ */
+ // Type: array of {IPv6 address}
+ {
+ "code": 40,
+ "data": "2001:db8::15, 2001:db8::16",
+ "name": "pana-agent"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_NEW_POSIX_TIMEZONE | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | TZ POSIX String |
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_NEW_POSIX_TIMEZONE(41)
+
+ option-length: the number of octets of the TZ POSIX String Index
+ described below.
+ */
+ // Type: string
+ {
+ "code": 41,
+ // String options that have a comma in their values need to have
+ // it escaped (i.e. each comma is preceded by two backslashes).
+ // That's because commas are reserved for separating fields in
+ // compound options. At the same time, we need to be conformant
+ // with JSON spec, that does not allow "\,". Therefore the
+ // slightly uncommon double backslashes notation is needed.
+ // The value sent over the wire is:
+ // EST5EDT4,M3.2.0/02:00,M11.1.0/02:00
+ "data": "EST5EDT4\\,M3.2.0/02:00\\,M11.1.0/02:00",
+ "name": "new-posix-timezone"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_NEW_TZDB_TIMEZONE | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | TZ Name |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_NEW_TZDB_TIMEZONE(42)
+
+ option-length: the number of octets of the TZ Database String Index
+ described below.
+ */
+ // Type: string
+ {
+ "code": 42,
+ "data": "Europe/Zurich",
+ "name": "new-tzdb-timezone"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_ERO | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | requested-option-code-1 | requested-option-code-2 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_ERO (43).
+ option-len 2 * number of requested options.
+ requested-option-code-n The option code for an option requested by
+ the relay agent.
+ */
+ // Type: array of {uint16}
+ {
+ "code": 43,
+ "data": "16, 32, 42",
+ "name": "ero"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_LQ_QUERY | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | query-type | |
+ +-+-+-+-+-+-+-+-+ |
+ | |
+ | link-address |
+ | |
+ | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | | .
+ +-+-+-+-+-+-+-+-+ .
+ . query-options .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_LQ_QUERY (44)
+
+ option-len 17 + length of query-options field.
+
+ link-address A global address that will be used by the
+ server to identify the link to which the
+ query applies, or 0::0 if unspecified.
+
+ query-type The query requested (see below).
+
+ query-options The options related to the query.
+ */
+ // Note: special logic not implemented
+ // Type: uint8, IPv6 address
+ {
+ "code": 44,
+ "data": "1, 2001:db8::17",
+ "name": "lq-query"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_CLIENT_DATA | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ . .
+ . client-options .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_CLIENT_DATA (45)
+
+ option-len Length, in octets, of the encapsulated client-
+ options field.
+
+ client-options The options associated with this client.
+ */
+ // Note: special logic not implemented
+ // Type: empty
+ {
+ "code": 45,
+ "name": "client-data"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_CLT_TIME | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | client-last-transaction-time |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_CLT_TIME (46)
+
+ option-len 4
+
+ client-last-transaction-time
+ The number of seconds since the server last
+ communicated with the client (on that link).
+ */
+ // Note: special logic not implemented
+ // Type: uint32
+ {
+ "code": 46,
+ "data": "600",
+ "name": "clt-time"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_LQ_RELAY_DATA | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | peer-address (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | DHCP-relay-message |
+ . .
+ . .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_LQ_RELAY_DATA (47)
+
+ option-len 16 + length of DHCP-relay-message.
+
+ peer-address The address of the relay agent from which
+ the relayed message was received by the
+ server.
+
+ DHCP-relay-message
+ The last complete relayed message, excluding
+ the client's message OPTION_RELAY_MSG,
+ received by the server.
+ */
+ // Note: special logic not implemented
+ // Type: IPv6 address, binary
+ {
+ "code": 47,
+ "data": "2001:db8::18, 1A BB AD AB BA D0 00 00 00 00 00 00 00 00 CA FE",
+ "name": "lq-relay-data"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_LQ_CLIENT_LINK | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | link-address (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | link-address (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_LQ_CLIENT_LINK (48)
+
+ option-len Length of the list of links in octets;
+ must be a multiple of 16.
+
+ link-address A global address used by the server to
+ identify the link on which the client is
+ located.
+ */
+ // Note: special logic not implemented
+ // Type: array of {IPv6 address}
+ {
+ "code": 48,
+ "data": "2001:db8::19, 2001:db8::20",
+ "name": "lq-client-link"
+ },
+
+ // Option codes 49-50 are not defined in Kea.
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_V6_LOST | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | LoST Server Domain Name |
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_V6_LOST (51)
+
+ option-length: Length of the 'LoST Server Domain Name' field
+ in octets; variable.
+
+ LoST Server Domain Name: The domain name of the LoST
+ server for the client to use.
+ */
+ // Type: FQDN
+ {
+ "code": 51,
+ "data": "lost.example.org",
+ "name": "v6-lost"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ + +
+ | |
+ + AC IPv6 Address +
+ | |
+ + +
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | .... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_CAPWAP_AC_V6 (52)
+
+ option-length: Length of the 'options' field in octets; MUST be a
+ multiple of sixteen (16).
+
+ AC IPv6 Address: IPv6 address of a CAPWAP AC that the WTP may use.
+ The ACs are listed in the order of preference for use by the WTP.
+ */
+ // Type: array of {IPv6 address}
+ {
+ "code": 52,
+ "data": "2001:db8::21, 2001:db8::22",
+ "name": "capwap-ac-v6"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_RELAY_ID | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ . .
+ . DUID .
+ . (variable length) .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_RELAY_ID.
+
+ option-len Length of DUID in octets.
+
+ DUID The DUID for the relay agent.
+ */
+ // Type: binary
+ {
+ "code": 53,
+ "data": "1A BB AD AB BA D0 00 00 00 00 00 00 00 00 CA FE",
+ "name": "relay-id"
+ },
+
+ // Option codes 54-56 are not defined in Kea.
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_V6_ACCESS_DOMAIN | Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ . Access Network Domain Name .
+ . ... .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_V6_ACCESS_DOMAIN (57).
+
+ option-length: The length of the entire access network domain name
+ option in octets.
+
+ option-value: The domain name associated with the access network,
+ encoded as described in Section 3.1.
+ */
+ // Type: FQDN
+ {
+ "code": 57,
+ "data": "v6-access.example.org",
+ "name": "v6-access-domain"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_SIP_UA_CS_LIST | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | searchlist |
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_SIP_UA_CS_LIST (58)
+
+ option-len Length of the 'searchlist' field in octets
+
+ searchlist The specification of the list of domain names in the SIP
+ User Agent Configuration Service Domains
+ */
+ // Type: array of {FQDN}
+ {
+ "code": 58,
+ "data": "sip-ua1.example.org, sip-ua1.example.org",
+ "name": "sip-ua-cs-list"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPT_BOOTFILE_URL | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ . boot-file-url (variable length) .
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPT_BOOTFILE_URL (59).
+
+ option-len Length of the boot-file-url in octets.
+
+ boot-file-url This string is the URL for the boot file. It MUST
+ comply with STD 66 [RFC3986]. The string is not
+ NUL-terminated.
+ */
+ // Type: string
+ {
+ "code": 59,
+ "data": "https://boot.example.org/pxe/os.img",
+ "name": "bootfile-url"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPT_BOOTFILE_PARAM | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | param-len 1 | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ parameter 1 .
+ . (variable length) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ . .
+ . <multiple Parameters> .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | param-len n | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ parameter n .
+ . (variable length) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPT_BOOTFILE_PARAM (60).
+
+ option-len Length of the Boot File Parameters option in octets
+ (not including the size of the option-code and
+ option-len fields).
+
+ param-len 1...n This is a 16-bit integer that specifies the length
+ of the following parameter in octets (not including
+ the parameter-length field).
+
+ parameter 1...n These UTF-8 strings are parameters needed for
+ booting, e.g., kernel parameters. The strings are
+ not NUL-terminated.
+ */
+ // Type: array of {tuple}
+ {
+ "code": 60,
+ "data": "root=/dev/sda2, quiet, splash",
+ "name": "bootfile-param"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_CLIENT_ARCH_TYPE | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ . .
+ . architecture-types (variable length) .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_CLIENT_ARCH_TYPE (61).
+
+ option-len Length of the "architecture-types" field in
+ octets. It MUST be an even number greater than
+ zero. See Section 2.1 of [RFC4578] for details.
+
+ architecture-types A list of one or more architecture types, as
+ specified in Section 2.1 of [RFC4578]. Each
+ architecture type identifier in this list is a
+ 16-bit value that describes the pre-boot runtime
+ environment of the client machine. A list of
+ valid values is maintained by the IANA (see
+ Section 6).
+ */
+ // Type: array of {uint16}
+ {
+ "code": 61,
+ "data": "1, 3, 5, 7",
+ "name": "client-arch-type"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_NII | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Type | Major | Minor |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_NII (62).
+
+ option-len 3
+
+ Type As specified in Section 2.2 of [RFC4578].
+
+ Major As specified in Section 2.2 of [RFC4578].
+
+ Minor As specified in Section 2.2 of [RFC4578].
+ */
+ // Type: uint8, uint8, array of {uint8}
+ {
+ "code": 62,
+ "data": "1, 2, 11, 13",
+ "name": "nii"
+ },
+
+ // Option code 63 is not defined in Kea.
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-------------------------------+-------------------------------+
+ | OPTION_AFTR_NAME: 64 | option-len |
+ +-------------------------------+-------------------------------+
+ | |
+ | tunnel-endpoint-name (FQDN) |
+ | |
+ +---------------------------------------------------------------+
+
+ option-len: Length of the tunnel-endpoint-name field in
+ octets.
+
+ tunnel-endpoint-name: A fully qualified domain name of the AFTR
+ tunnel endpoint
+ */
+ // Type: FQDN
+ {
+ "code": 64,
+ "data": "aftr.example.org",
+ "name": "aftr-name"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_ERP_LOCAL_DOMAIN_NAME| option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | erp-local-domain-name...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-
+
+ option code
+ OPTION_ERP_LOCAL_DOMAIN_NAME (65)
+
+ option-length
+ Length of the erp-local-domain-name field, in octets
+
+ erp-local-domain-name
+ This field contains the name of the local ERP domain and MUST be
+ encoded as specified in Section 8 of RFC 3315 [RFC3315]. Note
+ that this encoding does enable the use of internationalized domain
+ names, but only as a set of A-labels [RFC5890].
+ */
+ // Type: FQDN
+ {
+ "code": 65,
+ "data": "erp-local.example.org",
+ "name": "erp-local-domain-name"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_RSOO | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | options...
+ +-+-+-+-+-+-+-+-+-+-+-+
+
+ OPTION_RSOO
+
+ Relay-Supplied Options code (66).
+
+ option-length
+
+ Length of the RSOO.
+
+ options
+
+ One or more DHCPv6 options.
+ */
+ // Type: empty
+ {
+ "code": 66,
+ "name": "rsoo"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_PD_EXCLUDE | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | prefix-len | IPv6 subnet ID (1 to 16 octets) ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Prefix Exclude Option
+
+ o option-code: OPTION_PD_EXCLUDE (67).
+
+ o option-len: 1 + length of IPv6 subnet ID in octets. A valid
+ option-len is between 2 and 17.
+
+ o prefix-len: The length of the excluded prefix in bits. The
+ prefix-len MUST be between 'OPTION_IAPREFIX prefix-length'+1 and
+ 128.
+
+ o IPv6 subnet ID: A variable-length IPv6 subnet ID up to 128 bits.
+ */
+ // Type: binary
+ {
+ "code": 67,
+ "data": "2001:db8:1:1::/64",
+ "name": "pd-exclude"
+ },
+
+ // Option codes 68-73 are not defined in Kea.
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_RDNSS_SELECTION | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | DNS-recursive-name-server (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Reserved |prf| |
+ +-+-+-+-+-+-+-+-+ Domains and networks |
+ | (variable length) |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_RDNSS_SELECTION (74)
+
+ option-len: Length of the option in octets
+
+ DNS-recursive-name-server: An IPv6 address of RDNSS
+
+ Reserved: Field reserved for the future. MUST be set to zero and
+ MUST be ignored on receipt.
+
+ prf: RDNSS preference:
+
+ 01 High
+ 00 Medium
+ 11 Low
+ 10 Reserved
+
+ Reserved preference value (10) MUST NOT be sent. On receipt,
+ the Reserved value MUST be treated as Medium preference (00).
+ */
+ // Type: IPv6 address, uint8, array of {FQDN}
+ {
+ "code": 74,
+ "data": "2001:db8::23, 01, example.com, example.org",
+ "name": "rdnss-selection"
+ },
+
+ // Option codes 75-78 are not defined in Kea.
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_CLIENT_LINKLAYER_ADDR | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | link-layer type (16 bits) | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ | link-layer address (variable length) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_CLIENT_LINKLAYER_ADDR (79)
+ option-length: 2 + length of link-layer address
+ link-layer type: Client link-layer address type. The link-layer
+ type MUST be a valid hardware type assigned
+ by the IANA, as described in [RFC0826]
+ link-layer address: Client link-layer address
+ */
+ // Type: binary
+ {
+ "code": 79,
+ "data": "1A BB AD AB BA D0 00 00 00 00 00 00 00 00 CA FE",
+ "name": "client-linklayer-addr"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_LINK_ADDRESS | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | link-address (IPv6 address) |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_LINK_ADDRESS (80)
+
+ option-len: 16 (octets)
+
+ link-address: An IPv6 address used by the server to identify the
+ link on which the client is located.
+ */
+ // Type: IPv6 address
+ {
+ "code": 80,
+ "data": "2001:db8::24",
+ "name": "link-address"
+ },
+
+ // Option code 81 is not defined in Kea.
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_SOL_MAX_RT | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | SOL_MAX_RT value |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code OPTION_SOL_MAX_RT (82).
+
+ option-len 4.
+
+ SOL_MAX_RT value Overriding value for SOL_MAX_RT in seconds;
+ MUST be in this range: 60 <= "value" <= 86400
+ (1 day). A 4-octet field containing an
+ unsigned integer.
+ */
+ // Type: uint32
+ {
+ "code": 82,
+ "data": "420",
+ "name": "solmax-rt"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_INF_MAX_RT | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | INF_MAX_RT value |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Figure 39: INF_MAX_RT Option Format
+
+ option-code OPTION_INF_MAX_RT (83).
+
+ option-len 4.
+
+ INF_MAX_RT value Overriding value for INF_MAX_RT in seconds;
+ MUST be in this range: 60 <= "value" <= 86400
+ (1 day). A 4-octet field containing an
+ unsigned integer.
+ */
+ // Type: uint32
+ {
+ "code": 83,
+ "data": "2220",
+ "name": "inf-max-rt"
+ },
+
+ // Option codes 84-86 are not defined in Kea.
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ . IPv6 Address(es) .
+ . .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_DHCP4_O_DHCP6_SERVER (88).
+
+ option-len: Length of the IPv6 address(es) carried by the option,
+ i.e., multiple of 16 octets. Minimal length of this option is 0.
+
+ IPv6 Address: Zero or more IPv6 addresses of the DHCP 4o6 server(s).
+ */
+ // Type: array of {IPv6 address}
+ {
+ "code": 88,
+ "data": "2001:db8::25, 2001:db8::26",
+ "name": "dhcp4o6-server-addr"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_S46_RULE | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | flags | ea-len | prefix4-len | ipv4-prefix |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | (continued) | prefix6-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ipv6-prefix |
+ | (variable length) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ . S46_RULE-options .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ o option-code: OPTION_S46_RULE (89)
+
+ o option-length: length of the option, excluding option-code and
+ option-length fields, including length of all encapsulated
+ options; expressed in octets.
+
+ o flags: 8 bits long; carries flags applicable to the rule. The
+ meanings of the specific bits are explained in Figure 2.
+
+ o ea-len: 8 bits long; specifies the Embedded Address (EA) bit
+ length. Allowed values range from 0 to 48.
+
+ o prefix4-len: 8 bits long; expresses the prefix length of the
+ Rule IPv4 prefix specified in the ipv4-prefix field. Allowed
+ values range from 0 to 32.
+
+ o ipv4-prefix: a fixed-length 32-bit field that specifies the IPv4
+ prefix for the S46 rule. The bits in the prefix after prefix4-len
+ number of bits are reserved and MUST be initialized to zero by the
+ sender and ignored by the receiver.
+
+ o prefix6-len: 8 bits long; expresses the length of the
+ Rule IPv6 prefix specified in the ipv6-prefix field. Allowed
+ values range from 0 to 128.
+
+ o ipv6-prefix: a variable-length field that specifies the IPv6
+ domain prefix for the S46 rule. The field is padded on the right
+ with zero bits up to the nearest octet boundary when prefix6-len
+ is not evenly divisible by 8.
+
+ o S46_RULE-options: a variable-length field that may contain zero or
+ more options that specify additional parameters for this S46 rule.
+ This document specifies one such option: OPTION_S46_PORTPARAMS.
+ */
+ // Type: uint8, uint8, IPv4 address, IPv6 prefix
+ {
+ "code": 89,
+ "data": "1, 0, 24, 192.0.2.0, 2001:db8:1::/64",
+ "name": "s46-rule",
+ "space": "s46-cont-mape-options"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_S46_BR | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | br-ipv6-address |
+ | |
+ | |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ o option-code: OPTION_S46_BR (90)
+
+ o option-length: 16
+
+ o br-ipv6-address: a fixed-length field of 16 octets that specifies
+ the IPv6 address for the S46 BR.
+ */
+ // Type: IPv6 address
+ {
+ "code": 90,
+ "data": "2001:db8::27",
+ "name": "s46-br",
+ "space": "s46-cont-mape-options"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_S46_DMR | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |dmr-prefix6-len| dmr-ipv6-prefix |
+ +-+-+-+-+-+-+-+-+ (variable length) |
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ o option-code: OPTION_S46_DMR (91)
+
+ o option-length: 1 + length of dmr-ipv6-prefix specified in octets.
+
+ o dmr-prefix6-len: 8 bits long; expresses the bitmask length of the
+ IPv6 prefix specified in the dmr-ipv6-prefix field. Allowed
+ values range from 0 to 128.
+
+ o dmr-ipv6-prefix: a variable-length field specifying the IPv6
+ prefix or address for the BR. This field is right-padded with
+ zeros to the nearest octet boundary when dmr-prefix6-len is not
+ divisible by 8.
+ */
+ // Type: IPv6 prefix
+ {
+ "code": 91,
+ "data": "2001:db8:cafe::/64",
+ "name": "s46-dmr",
+ "space": "s46-cont-mapt-options"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_S46_V4V6BIND | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ipv4-address |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |bindprefix6-len| bind-ipv6-prefix |
+ +-+-+-+-+-+-+-+-+ (variable length) |
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ . S46_V4V6BIND-options .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ o option-code: OPTION_S46_V4V6BIND (92)
+
+ o option-length: length of the option, excluding option-code and
+ option-length fields, including length of all encapsulated
+ options; expressed in octets.
+
+ o ipv4-address: a fixed-length field of 4 octets specifying an IPv4
+ address.
+
+ o bindprefix6-len: 8 bits long; expresses the bitmask length of the
+ IPv6 prefix specified in the bind-ipv6-prefix field. Allowed
+ values range from 0 to 128.
+
+ o bind-ipv6-prefix: a variable-length field specifying the IPv6
+ prefix or address for the S46 CE. This field is right-padded with
+ zeros to the nearest octet boundary when bindprefix6-len is not
+ divisible by 8.
+
+ o S46_V4V6BIND-options: a variable-length field that may contain
+ zero or more options that specify additional parameters. This
+ document specifies one such option: OPTION_S46_PORTPARAMS.
+ */
+ // Type: IPv4 address, IPv6 prefix
+ {
+ "code": 92,
+ "data": "192.0.2.78, 2001:db8:1:cafe::/64",
+ "name": "s46-v4v6bind",
+ "space": "s46-cont-lw-options"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_S46_PORTPARAMS | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | offset | PSID-len | PSID |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ o option-code: OPTION_S46_PORTPARAMS (93)
+
+ o option-length: 4
+
+ o offset: Port Set Identifier (PSID) offset. 8 bits long; specifies
+ the numeric value for the S46 algorithm's excluded port range/
+ offset bits (a-bits), as per Section 5.1 of [RFC7597]. Allowed
+ values are between 0 and 15. Default values for this field are
+ specific to the softwire mechanism being implemented and are
+ defined in the relevant specification document.
+
+ o PSID-len: 8 bits long; specifies the number of significant bits in
+ the PSID field (also known as 'k'). When set to 0, the PSID field
+ is to be ignored. After the first 'a' bits, there are k bits in
+ the port number representing the value of the PSID. Consequently,
+ the address-sharing ratio would be 2^k.
+
+ o PSID: 16 bits long. The PSID value algorithmically identifies a
+ set of ports assigned to a CE. The first k bits on the left of
+ this field contain the PSID binary value. The remaining (16 - k)
+ bits on the right are padding zeros.
+ */
+ // Type: uint8, PSID
+ {
+ "code": 93,
+ "data": "2, 3/4",
+ "name": "s46-portparams",
+ "space": "s46-rule-options"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_S46_CONT_MAPE | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ . encapsulated-options (variable length) .
+ . .
+ +---------------------------------------------------------------+
+
+ o option-code: OPTION_S46_CONT_MAPE (94)
+
+ o option-length: length of encapsulated options, expressed in
+ octets.
+
+ o encapsulated-options: options associated with this Softwire46
+ MAP-E domain.
+ */
+ // Type: empty
+ {
+ "code": 94,
+ "name": "s46-cont-mape",
+ "space": "dhcp6"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_S46_CONT_MAPT | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ . encapsulated-options (variable length) .
+ . .
+ +---------------------------------------------------------------+
+
+ o option-code: OPTION_S46_CONT_MAPT (95)
+
+ o option-length: length of encapsulated options, expressed in
+ octets.
+
+ o encapsulated-options: options associated with this Softwire46
+ MAP-T domain.
+ */
+ // Type: empty
+ {
+ "code": 95,
+ "name": "s46-cont-mapt",
+ "space": "dhcp6"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OPTION_S46_CONT_LW | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ + encapsulated-options (variable length) .
+ . .
+ +---------------------------------------------------------------+
+
+ o option-code: OPTION_S46_CONT_LW (96)
+
+ o option-length: length of encapsulated options, expressed in
+ octets.
+
+ o encapsulated-options: options associated with this Softwire46
+ Lightweight 4over6 domain.
+ */
+ // Type: empty
+ {
+ "code": 96,
+ "name": "s46-cont-lw",
+ "space": "dhcp6"
+ },
+
+ // Option codes 97-102 are not defined in Kea.
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ . URI (variable length) .
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ o option-code: The Captive-Portal DHCPv6 option (103) (two octets).
+
+ o option-len: The length, in octets of the URI.
+
+ o URI: The contact URI for the captive portal that the user should
+ connect to (encoded following the rules in [RFC3986]).
+ */
+ // Type: string
+ {
+ "code": 103,
+ "data": "https://example.org/captive-portal",
+ "name": "v6-captive-portal"
+ },
+
+ // Option codes 104-111 are not defined in Kea.
+ // Option code 112 is unassigned.
+ // Option codes 113-134 are not defined in Kea.
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code (136) | option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ . bootstrap-server-list (variable length) .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code: OPTION_V6_SZTP_REDIRECT (136)
+
+ option-length: The option length in octets.
+
+ bootstrap-server-list: A list of servers for the
+ client to attempt contacting, in order to obtain
+ further bootstrapping data. Each URI entry in the
+ bootstrap-server-list is structured as follows:
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+-+-+-+-+-+
+ | uri-length | URI |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+-+-+-+-+-+
+
+ uri-length: 2 octets long; specifies the length of the URI data.
+ URI: URI of the SZTP bootstrap server.
+ */
+ // Type: array of {tuple}
+ {
+ "code": 136,
+ "data": "https://sztp1.example.com:8443, https://sztp2.example.com:8444",
+ "name": "v6-sztp-redirect"
+ },
+
+ // Option codes 137-142 are unassigned.
+
+ /*
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Option Code | Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | IP Address |
+ . .
+ +---------------------------------------------------------------+
+
+ Option Code
+ OPTION-IPv6_Address-ANDSF (143)
+
+ Length
+ Length (in bytes) of the option excluding the 'Option Code' and
+ the 'Length' fields; 'Length' field is set to 16N, where N is the
+ number of IPv6 addresses carried in the option
+
+ IP Address
+ IPv6 address(es) of ANDSF server(s)
+ */
+ // Type: IPv6 address
+ {
+ "code": 143,
+ "data": "2001:db8::28",
+ "name": "ipv6-address-andsf"
+ },
+
+ /*
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Option-code | Option-length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Service Priority | ADN Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ~ authentication-domain-name ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Addr Length | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ ~ ipv6-address(es) ~
+ | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+ ~ Service Parameters (SvcParams) ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Option-code: OPTION_V6_DNR (144)
+
+ Option-length: Length of the enclosed data in octets. The option
+ length is ('ADN Length' + 4) when only an ADN is included in the
+ option.
+
+ Service Priority: The priority of this OPTION_V6_DNR instance
+ compared to other instances. This 16-bit unsigned integer is
+ interpreted following the rules specified in Section 2.4.1 of
+ [RFC9460].
+
+ ADN Length: Length of the authentication-domain-name field in
+ octets.
+
+ authentication-domain-name (variable length): A fully qualified
+ domain name of the encrypted DNS resolver. This field is
+ formatted as specified in Section 10 of [RFC8415].
+
+ Addr Length: Length of enclosed IPv6 addresses in octets. When
+ present, it MUST be a multiple of 16.
+
+ ipv6-address(es) (variable length): Indicates one or more IPv6
+ addresses to reach the encrypted DNS resolver. An address can be
+ link-local, ULA, or GUA.
+
+ Service Parameters (SvcParams) (variable length): Specifies a set of
+ service parameters that are encoded following the rules in
+ Section 2.2 of [RFC9460].
+ */
+ // Type: internal
+ {
+ // DNR option may be configured using convenient notation. Comma separated fields must be provided:
+ // - service priority (mandatory),
+ // - ADN (mandatory),
+ // - IP address(es) (optional - if more than one - they must be space-separated)
+ // - SvcParams (optional - if more than one - they must be space-separated;
+ // to provide more than one alpn-id separate them with double-backslash escaped comma like in the
+ // example below).
+ // Basing on the config, Kea will encode the option according to RFC9463.
+ "code": 144,
+ "name": "v6-dnr",
+ "data": "100, resolver.example., 2001:db8::1 2001:db8::2, alpn=dot\\,h2\\,h3 port=8530 dohpath=/dns-query{?dns}"
+ },
+
+ // Option codes 145-65535 are unassigned.
+
+ /*
+ Custom option data
+ */
+ // See "option-def" below for the definitions.
+ {
+ "code": 111,
+ "data": "88, 96, 64",
+ "name": "s46-priority"
+ },
+ {
+ "code": 1,
+ "name": "my-empty-option",
+ "space": "my-fancy-space"
+ },
+ {
+ "code": 222,
+ "data": "2001:db8::29, 2001:db8::/64, 3/4, 1, example.org, string",
+ "name": "my-lengthy-option",
+ "space": "my-fancy-space"
+ },
+ {
+ "code": 65432,
+ "data": "127, 32767, 2147483647, 255, 65535, 4294967295, 192.0.2.79, 2001:db8::30, 2001:db8::/64, 3/4, 1, example.org, string",
+ "name": "my-fancy-option",
+ "space": "my-fancy-space"
+ },
+ {
+ "code": 12321,
+ "name": "my-encapsulating-option",
+ "space": "my-encapsulating-space"
+ }
+ ],
+
+ /*
+ Custom option definitions
+ */
+ // For kea-dhcp6, custom option definitions are always global. Even when
+ // data for said options is then configured at subnet level.
+ "option-def": [
+ // Inside the default space. Codes need to not overlap with other
+ // standard/custom option definitions.
+ // An option from an actual RFC (8026) not implemented amongst the
+ // standard definitions. The option is structured as an array of 16-bit
+ // integers so "array" is set to true and "type" to "uint16".
+ {
+ "array": true,
+ "code": 111,
+ "encapsulate": "",
+ "name": "s46-priority",
+ "record-types": "",
+ "space": "dhcp6",
+ "type": "uint16"
+ },
+
+ // New option space allows for a new set of option codes.
+ // An empty option requires no "data" in "option-data". It's
+ // presence should be sufficient to trigger custom behavior.
+ {
+ "array": false,
+ "code": 1,
+ "encapsulate": "",
+ "name": "my-empty-option",
+ "record-types": "",
+ "space": "my-fancy-space",
+ "type": "empty"
+ },
+
+ // A custom type has "type" set to "record" and all data types (which need
+ // to be more than 1, otherwise you're better off using the type directly)
+ // are specified in "record-types". If "string" is part of them, it needs
+ // to be last.
+ {
+ "array": false,
+ "code": 222,
+ "encapsulate": "",
+ "name": "my-lengthy-option",
+ "record-types": "ipv6-address, ipv6-prefix, psid, tuple, fqdn, string",
+ "space": "my-fancy-space",
+ "type": "record"
+ },
+
+ // Contains arrays of all types except strings since an array of strings
+ // is not a valid option definition.
+ {
+ "array": true,
+ "code": 65432,
+ "encapsulate": "",
+ "name": "my-fancy-option",
+ "record-types": "int8, int16, int32, uint8, uint16, uint32, ipv4-address, ipv6-address, ipv6-prefix, psid, tuple, fqdn",
+ "space": "my-fancy-space",
+ "type": "record"
+ },
+
+ // A single encapsulating space can be used. An option containing any
+ // option from said space will now be unpacked successfully by Kea.
+ {
+ "array": false,
+ "code": 12321,
+ "encapsulate": "my-fancy-space",
+ "name": "my-encapsulating-option",
+ "record-types": "",
+ "space": "my-encapsulating-space",
+ "type": "empty"
+ }
+ ],
+
+ "subnet6": [
+ /*
+ DOCSIS3 option data
+ */
+ // Headers are as defined in CL-SP-CANN-DHCP-Reg-I16-200715.
+ // "space" is required to be explicitly defined as "docsis3-v6"
+ {
+ "option-data": [
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | device-type |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option-code CL_OPTION_DEVICE_TYPE (2)
+
+ option-len length of device-type field in bytes.
+
+ device-type The device type as NVT ASCII text MUST NOT be null terminated.
+ "ECM" for embedded Cable Modem (as specified by DOCSIS 1.0, 1.1, 2.0, 3.0
+ or 3.1 Base Specifications)
+ "EPS" for CableHome embedded Portal Services Element
+ "EMTA" for PacketCable embedded Multimedia Terminal Adapter
+ "EDVA" for PacketCable embedded Digital Voice Adapter
+ "ESTB" for an embedded Set-Top Box
+ "EROUTER" for an embedded DOCSIS Router
+ "SROUTER" for a Standalone Router
+ */
+ // Type: string
+ {
+ "code": 2,
+ "data": "ECM",
+ "name": "device-type",
+ "space": "docsis3-v6"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | .
+ . vendor-name .
+ . |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option code: CL_OPTION_VENDOR_NAME(10)
+
+ option length: n (for string of length n)
+
+ vendor-name: The vendor name string NVT ASCII text MUST NOT be
+ null terminated.
+ */
+ // Type: string
+ {
+ "code": 10,
+ "data": "CableLabs",
+ "name": "vendor-type",
+ "space": "docsis3-v6"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | TFTP-server-1 |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | TFTP-server-2 |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ . .
+ . .
+ . .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | TFTP-server-n |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option code: CL_OPTION_TFTP_SERVERS(32)
+
+ option length: 16*n (for n servers in the option) in bytes
+
+ TFTP-server: The IPv6 address of a TFTP server
+ */
+ // Type: array of {IPv6 address}
+ {
+ "code": 32,
+ "data": "2001:db8::31",
+ "name": "tftp-servers",
+ "space": "docsis3-v6"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | .
+ . configuration-file-name .
+ . |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option code: CL_OPTION_CONFIG_FILE_NAME(33)
+
+ option length: n (for file name of length n)
+
+ configuration-file-name: The name of the configuration file for the client
+ */
+ // Type: string
+ {
+ "code": 33,
+ "data": "cm/012345678.cfg",
+ "name": "config-file",
+ "space": "docsis3-v6"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | .
+ . vendor-name .
+ . |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option code: CL_OPTION_VENDOR_NAME(10)
+
+ option length: n (for string of length n)
+
+ vendor-name: The vendor name string NVT ASCII text MUST NOT be
+ null terminated.
+ */
+ // Type: array of {IPv6 address}
+ {
+ "code": 34,
+ "data": "2001:db8::32",
+ "name": "syslog-servers",
+ "space": "docsis3-v6"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | .
+ . vendor-name .
+ . |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option code: CL_OPTION_VENDOR_NAME(10)
+
+ option length: n (for string of length n)
+
+ vendor-name: The vendor name string NVT ASCII text MUST NOT be
+ null terminated.
+ */
+ // Type: binary
+ {
+ "code": 36,
+ "data": "1A BB AD AB BA D0 00 00 00 00 00 00 00 00 CA FE",
+ "name": "device-id",
+ "space": "docsis3-v6"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | .
+ . vendor-name .
+ . |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option code: CL_OPTION_VENDOR_NAME(10)
+
+ option length: n (for string of length n)
+
+ vendor-name: The vendor name string NVT ASCII text MUST NOT be
+ null terminated.
+ */
+ // Type: int32
+ {
+ "code": 37,
+ "data": "2001:db8::33",
+ "name": "time-servers",
+ "space": "docsis3-v6"
+ },
+
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | option-code | option-len |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | .
+ . vendor-name .
+ . |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ option code: CL_OPTION_VENDOR_NAME(10)
+
+ option length: n (for string of length n)
+
+ vendor-name: The vendor name string NVT ASCII text MUST NOT be
+ null terminated.
+ */
+ // Type: int32
+ {
+ "code": 38,
+ "data": "-25200",
+ "name": "time-offset",
+ "space": "docsis3-v6"
+ }
+ ],
+ "subnet": "2001:db8:d0c5:15::/64",
+ "id": 1
+ }
+ ]
+ }
+}
diff --git a/doc/examples/kea6/backends.json b/doc/examples/kea6/backends.json
new file mode 100644
index 0000000..350c8d6
--- /dev/null
+++ b/doc/examples/kea6/backends.json
@@ -0,0 +1,109 @@
+// This is an example configuration file for the DHCPv6 server in Kea.
+// It is a basic scenario with one IPv6 subnet configured. It demonstrates
+// how to configure Kea to use various backends to store leases:
+// - memfile
+// - MySQL
+// - PostgreSQL
+
+{ "Dhcp6":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify lease type. Exactly one lease-database section
+// should be present. Make sure you uncomment only one.
+
+// 1. memfile backend. Leases information will be stored in flat CSV file.
+// This is the easiest backend to use as it does not require any extra
+// dependencies or services running.
+ "lease-database": {
+ "type": "memfile",
+ "persist": true,
+ "lfc-interval": 3600
+ },
+
+// 2. MySQL backend. Leases will be stored in MySQL database. Make sure it
+// is up, running and properly initialized. See kea-admin documentation
+// for details on how to initialize the database. The only strictly required
+// parameters are type and name. If other parameters are not specified,
+// Kea will assume the database is available on localhost, that user and
+// password is not necessary to connect and that timeout is 5 seconds.
+// Kea must be compiled with --with-mysql option to use this backend.
+// "lease-database": {
+// "type": "mysql",
+// "name": "keatest",
+// "host": "localhost",
+// "port": 3306,
+// "user": "keatest",
+// "password": "secret1",
+// "reconnect-wait-time": 3000, // expressed in ms
+// "max-reconnect-tries": 3,
+// "on-fail": "stop-retry-exit",
+// "retry-on-startup": false,
+// "connect-timeout": 3
+// },
+
+// 3. PostgreSQL backend. Leases will be stored in PostgreSQL database. Make
+// sure it is up, running and properly initialized. See kea-admin documentation
+// for details on how to initialize the database. The only strictly required
+// parameters are type and name. If other parameters are not specified,
+// Kea will assume the database is available on localhost, that user and
+// password is not necessary to connect and that timeout is 5 seconds.
+// Kea must be compiled with --with-pgsql option to use this backend.
+// "lease-database": {
+// "type": "postgresql",
+// "name": "keatest",
+// "host": "localhost",
+// "port": 5432,
+// "user": "keatest",
+// "password": "secret1",
+// "reconnect-wait-time": 3000, // expressed in ms
+// "max-reconnect-tries": 3,
+// "on-fail": "stop-retry-exit",
+// "retry-on-startup": false,
+// "connect-timeout": 3
+// },
+
+// Addresses will be assigned with preferred and valid lifetimes
+// being 3000 and 4000, respectively. Client is told to start
+// renewing after 1000 seconds. If the server does not respond
+// after 2000 seconds since the lease was granted, client is supposed
+// to start REBIND procedure (emergency renewal that allows switching
+// to a different server).
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+// The following list defines subnets. Each subnet consists of at
+// least subnet and pool entries.
+ "subnet6": [
+ {
+ "pools": [ { "pool": "2001:db8:1::/80" } ],
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "interface": "eth0"
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/classify.json b/doc/examples/kea6/classify.json
new file mode 100644
index 0000000..41c2a2d
--- /dev/null
+++ b/doc/examples/kea6/classify.json
@@ -0,0 +1,113 @@
+// This is an example configuration file for the DHCPv6 server in Kea.
+// The purpose of this example is to showcase how clients can be classified.
+
+{ "Dhcp6":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// Let's use the simplest backend: memfile and use some reasonable values
+// for timers. They are of no concern for the classification demonstration.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+
+// This list defines several classes that incoming packets can be assigned to.
+// One packet can belong to zero or more classes.
+ "client-classes": [
+
+// The first class attempts to match all packets coming in on eth0 interface.
+ {
+ "name": "lab",
+ "test": "pkt.iface == 'eth0'",
+ "option-data": [{
+ "name": "dns-servers",
+ "data": "2001:db8::1"
+ }]
+ },
+
+// Let's classify all incoming RENEW (message type 5) to a separate
+// class.
+ {
+ "name": "renews",
+ "test": "pkt6.msgtype == 5"
+ },
+
+// Let's pick cable modems. In this simple example we'll assume the device
+// is a cable modem if it sends a vendor option with enterprise-id equal
+// to 4491.
+ {
+ "name": "cable-modems",
+ "test": "vendor.enterprise == 4491"
+ }
+
+ ],
+
+
+// The following list defines subnets. Each subnet consists of at
+// least subnet and pool entries.
+ "subnet6": [
+ {
+ "id": 1,
+ "pools": [ { "pool": "2001:db8:1::/80" } ],
+ "subnet": "2001:db8:1::/64",
+ "client-class": "cable-modems",
+ "interface": "eth0"
+ },
+
+ // The following subnet contains a class reservation for a client using
+ // DUID 01:02:03:04:05:0A:0B:0C:0D:0E. This client will always be assigned
+ // to this class.
+ {
+ "id": 2,
+ "pools": [ { "pool": "2001:db8:2::/80" } ],
+ "subnet": "2001:db8:2::/64",
+ "reservations": [
+ {
+ "duid": "01:02:03:04:05:0A:0B:0C:0D:0E",
+ "client-classes": [ "cable-modems" ]
+ } ],
+ "interface": "eth0"
+ },
+
+ // The following subnet contains a pool with a class constraint: only
+ // clients which belong to the class are allowed to use this pool.
+ {
+ "id": 3,
+ "pools": [
+ {
+ "pool": "2001:db8:4::/80",
+ "client-class": "cable-modems"
+ } ],
+ "subnet": "2001:db8:4::/64",
+ "interface": "eth1"
+ }
+
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/classify2.json b/doc/examples/kea6/classify2.json
new file mode 100644
index 0000000..78542f4
--- /dev/null
+++ b/doc/examples/kea6/classify2.json
@@ -0,0 +1,150 @@
+// This is an example configuration file for the DHCPv6 server in Kea.
+// The purpose of this example is to showcase how clients can be classified.
+
+{ "Dhcp6":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// Let's use the simplest backend: memfile and use some reasonable values
+// for timers. They are of no concern for the classification demonstration.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+
+// This list defines several classes that incoming packets can be assigned to.
+// One packet can belong to zero or more classes.
+ "client-classes": [
+
+// This class is required by the second subnet and is evaluated only
+// if it is required. The test expression returns true.
+// Note it is not possible to depend on cable-modems class because it
+// is not yet defined.
+ {
+ "name": "second_subnet",
+ "only-if-required": true,
+ "test": "member('ALL')",
+ "option-data": [{
+ "name": "dns-servers",
+ "data": "2001:db8::1"
+ }]
+ },
+
+// Let's classify all incoming RENEW (message type 5) to a separate
+// class.
+ {
+ "name": "renews",
+ "test": "pkt6.msgtype == 5"
+ },
+
+// Let's pick cable modems. In this simple example we'll assume the device
+// is a cable modem if it sends a vendor option with enterprise-id equal
+// to 4491.
+ {
+ "name": "cable-modems",
+ "test": "vendor.enterprise == 4491"
+ },
+
+// Both a cable modem (by evaluation or host reservation) and has a host
+// reservation.
+ {
+ "name": "cable-modem-hosts",
+ "test": "member('cable-modems') and member('KNOWN')"
+ }
+
+ ],
+
+
+// The following list defines subnets. Each subnet consists of at
+// least subnet and pool entries.
+ "subnet6": [
+ {
+ "id": 1,
+ "pools": [ { "pool": "2001:db8:1::/80" } ],
+ "subnet": "2001:db8:1::/64",
+ "client-class": "cable-modems",
+ "interface": "eth0"
+ },
+// The following subnet contains a class reservation for a client using
+// DUID 01:02:03:04:05:0A:0B:0C:0D:0E. This client will always be assigned
+// to this class.
+ {
+ "id": 2,
+ "pools": [ { "pool": "2001:db8:2::/80" } ],
+ "subnet": "2001:db8:2::/64",
+ "reservations": [
+ {
+ "duid": "01:02:03:04:05:0A:0B:0C:0D:0E",
+ "client-classes": [ "cable-modems" ]
+ } ],
+ "interface": "eth0",
+ "require-client-classes": [ "second_subnet" ]
+ },
+// The following subnet contains a pool with a class constraint: only
+// clients which belong to the class are allowed to use this pool.
+ {
+ "id": 3,
+ "pools": [
+ {
+ "pool": "2001:db8:4::/80",
+ "client-class": "cable-modems"
+ } ],
+ "subnet": "2001:db8:4::/64",
+ "interface": "eth1"
+ },
+// This subnet is divided in two pools for unknown and known
+// (i.e. which have a reservation) clients. The built-in KNOWN and
+// UNKNOWN classes are set or not at host reservation lookup (KNOWN if
+// this returns something, UNKNOWN if this finds nothing) and client
+// classes depending on it are evaluated.
+// This happens after subnet selection and before address allocation
+// from pools.
+ {
+ "id": 4,
+ "pools": [
+ {
+ "pool": "2001:db8:8::/64",
+ "client-class": "UNKNOWN"
+ },
+ {
+ "pool": "2001:db8:9::/64",
+ "client-class": "KNOWN"
+ }
+ ],
+ "subnet": "2001:db8:8::/46",
+ "reservations": [
+ { "hw-address": "00:00:00:11:22:33", "hostname": "h1" },
+ { "hw-address": "00:00:00:44:55:66", "hostname": "h4" },
+ { "hw-address": "00:00:00:77:88:99", "hostname": "h7" },
+ { "hw-address": "00:00:00:aa:bb:cc", "hostname": "ha" }
+ ]
+ }
+
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/comments.json b/doc/examples/kea6/comments.json
new file mode 100644
index 0000000..88cdd41
--- /dev/null
+++ b/doc/examples/kea6/comments.json
@@ -0,0 +1,123 @@
+// This is an example configuration file for the DHCPv6 server in Kea.
+// It uses embedded (i.e., which will be included in configuration objects
+// and not stripped by at lexical analysis) comments.
+
+{ "Dhcp6":
+
+{
+ // Global scope
+ "comment": "A DHCPv6 server",
+
+ // In interface config
+ "interfaces-config": {
+ "comment": "Use wildcard",
+ "interfaces": [ "*" ] },
+
+ // In option definitions
+ "option-def": [ {
+ "comment": "An option definition",
+ "name": "foo",
+ "code": 100,
+ "type": "ipv6-address",
+ "space": "isc"
+ } ],
+
+ // In option data
+ "option-data": [ {
+ "comment": "Set option value",
+ "name": "subscriber-id",
+ "data": "ABCDEF0105",
+ "csv-format": false
+ } ],
+
+ // In client classes
+ "client-classes": [
+ {
+ "comment": "match all",
+ "name": "all",
+ "test": "'' == ''"
+ },
+ // Of course comments are optional
+ {
+ "name": "none"
+ },
+ // A comment and a user-context can be specified
+ {
+ "comment": "a comment",
+ "name": "both",
+ "user-context": {
+ "version": 1
+ }
+ }
+ ],
+
+ // In control socket (more for the agent)
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea6-ctrl-socket",
+ "user-context": { "comment": "Indirect comment" }
+ },
+
+ // In shared networks
+ "shared-networks": [ {
+ "comment": "A shared network",
+ "name": "foo",
+
+ // In subnets
+ "subnet6": [
+ {
+ "comment": "A subnet",
+ "subnet": "2001:db1::/64",
+ "id": 100,
+
+ // In pools
+ "pools": [
+ {
+ "comment": "A pool",
+ "pool": "2001:db1::/64"
+ }
+ ],
+
+ // In prefix pools
+ "pd-pools": [
+ {
+ "comment": "A prefix pool",
+ "prefix": "2001:db2::",
+ "prefix-len": 48,
+ "delegated-len": 64
+ }
+ ],
+
+ // In host reservations
+ "reservations": [
+ {
+ "comment": "A host reservation",
+ "hw-address": "AA:BB:CC:DD:EE:FF",
+ "hostname": "foo.example.com",
+
+ // Again in an option data
+ "option-data": [ {
+ "comment": "An option in a reservation",
+ "name": "domain-search",
+ "data": "example.com"
+ } ]
+ }
+ ]
+ }
+ ]
+ } ],
+
+ // In dhcp ddns
+ "dhcp-ddns": {
+ "comment": "No dynamic DNS",
+ "enable-updates": false
+ },
+
+ // In loggers
+ "loggers": [ {
+ "comment": "A logger",
+ "name": "kea-dhcp6"
+ } ]
+}
+
+}
diff --git a/doc/examples/kea6/config-backend.json b/doc/examples/kea6/config-backend.json
new file mode 100644
index 0000000..d269f3f
--- /dev/null
+++ b/doc/examples/kea6/config-backend.json
@@ -0,0 +1,92 @@
+// This is an example configuration file for the DHCPv4 server in Kea.
+// It demonstrates how to enable Kea Configuration Backend using MySQL.
+// It requires that libdhcp_mysql_cb.so library is available and
+// optionally libdhcp_cb_cmds.so hook library.
+
+{ "Dhcp6":
+
+{
+ // Set the server tag for the configuration backend. This instance will
+ // be named server2. Every configuration element that is applicable to
+ // either "all" or "server2" will be used by this instance.
+ "server-tag": "server2",
+
+ // Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+ // Use memfile lease database backend.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+ // This parameter controls how the server accesses the configuration
+ // database. Currently only one database type is available - "mysql".
+ // It requires that libdhcp_mysql_cb.so hook library is loaded.
+ "config-control": {
+ // A list of database backends to connect to. Currently, it is limited
+ // to a single backend.
+ "config-databases": [
+ {
+ "type": "mysql",
+ "reconnect-wait-time": 3000, // expressed in ms
+ "max-reconnect-tries": 3,
+ "name": "kea",
+ "user": "kea",
+ "password": "kea",
+ "host": "localhost",
+ "port": 3306
+ }
+ ],
+ // Controls how often the server polls the database for the
+ // configuration updates. The setting below implies that it
+ // will take up to approx. 20 seconds for the server to
+ // discover and fetch configuration changes.
+ "config-fetch-wait-time": 20
+ },
+
+ // This defines a control socket. If defined, Kea will open a UNIX socket
+ // and will listen for incoming commands. See section 17 of the Kea ARM for
+ // details.
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea6-ctrl-socket"
+ },
+
+ // Hooks libraries that enable configuration backend are loaded.
+ "hooks-libraries": [
+ // The libdhcp_mysql_cb.so is required to use MySQL Configuration
+ // Backend.
+ {
+ "library": "/usr/local/lib/kea/hooks/libdhcp_mysql_cb.so"
+ }
+ // The libdhcp_cb_cmds.so is optional. It allows for managing the
+ // configuration in the database. If this library is not loaded,
+ // the configuration can be managed directly using available
+ // tools that work directly with the MySQL database.
+ // ,{
+ // "library": "/usr/local/lib/kea/hooks/libdhcp_cb_cmds.so"
+ // }
+ ],
+
+ // The following configures logging. It assumes that messages with at
+ // least informational level (info, warn, error and fatal) should be
+ // logged to stdout. Alternatively, you can specify stderr here, a filename
+ // or 'syslog', which will store output messages via syslog.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/dhcpv4-over-dhcpv6.json b/doc/examples/kea6/dhcpv4-over-dhcpv6.json
new file mode 100644
index 0000000..56e371e
--- /dev/null
+++ b/doc/examples/kea6/dhcpv4-over-dhcpv6.json
@@ -0,0 +1,58 @@
+// This is an example configuration file for the DHCPv6 server of
+// DHCPv4-over-DHCPv6 tests in Kea.
+
+{
+
+// DHCPv6 conf
+"Dhcp6":
+{
+ "interfaces-config": {
+// Enable unicast
+ "interfaces": [ "eth0/2001:db8:1::1" ]
+ },
+
+ "lease-database": {
+ "type": "memfile",
+ "name": "/tmp/kea-dhcp6.csv"
+ },
+
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+ "subnet6": [
+ { "id": 1,
+ "subnet": "2001:db8:1:1::/64",
+ "interface": "eth0",
+ "pools": [ { "pool": "2001:db8:1:1::1:0/112" } ] }
+ ],
+
+// This enables DHCPv4-over-DHCPv6 support
+ "dhcp4o6-port": 6767,
+
+// Required by DHCPv4-over-DHCPv6 clients
+ "option-data": [
+ { "name": "dhcp4o6-server-addr",
+ "code": 88,
+ "space": "dhcp6",
+ "csv-format": true,
+// Put the server address here
+ "data": "2001:db8:1:1::1" }
+ ],
+
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "/tmp/kea-dhcp6.log"
+ }
+ ],
+ "severity": "DEBUG",
+ "debuglevel": 0
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/duid.json b/doc/examples/kea6/duid.json
new file mode 100644
index 0000000..223d4c3
--- /dev/null
+++ b/doc/examples/kea6/duid.json
@@ -0,0 +1,80 @@
+// This is an example configuration file for DHCPv6 server in Kea.
+// It demonstrates how to configure Kea to use DUID-LLT with some
+// values specified explicitly.
+
+{ "Dhcp6":
+
+{
+
+// Configure server identifier (DUID-LLT). The hexadecimal value of the
+// identifier will be used as link layer address component of the DUID.
+// The link layer type will be ethernet. The value of time is set to 0
+// which indicates that the server must generate this value, i.e. use
+// current time. Note that it is easy to move from this configuration
+// to DUID-EN or DUID-LL. It would require changing the "type" value
+// to "EN" or "LL" respectively. The "identifier" would hold a
+// DUID-EN variable length identifier or DUID-LL link layer address.
+// The values of "time" and "htype" would be ignored for DUID-EN.
+// If one wanted to use a non-default enterprise-id for DUID-EN, the
+// "enterprise-id" parameter would need to be added. Note that only
+// a "type" parameter is mandatory while specifying "server-id" map.
+ "server-id": {
+ "type": "LLT",
+ "identifier": "12C4D5AF870C",
+ "time": 0,
+ "htype": 1
+ },
+
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+// Addresses will be assigned with preferred and valid lifetimes
+// being 3000 and 4000, respectively. Client is told to start
+// renewing after 1000 seconds. If the server does not respond
+// after 2000 seconds since the lease was granted, client is supposed
+// to start REBIND procedure (emergency renewal that allows switching
+// to a different server).
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+// The following list defines subnets. Each subnet consists of at
+// least subnet and pool entries.
+ "subnet6": [
+ {
+ "id": 1,
+ "pools": [ { "pool": "2001:db8:1::/80" } ],
+ "subnet": "2001:db8:1::/64",
+ "interface": "eth0"
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at least
+// informational level (info, warn, error) will will be logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/global-reservations.json b/doc/examples/kea6/global-reservations.json
new file mode 100644
index 0000000..b98b3e0
--- /dev/null
+++ b/doc/examples/kea6/global-reservations.json
@@ -0,0 +1,174 @@
+// This is an example configuration file for the DHCPv6 server in Kea.
+// It demonstrates how global host reservations can be configured.
+// The global reservations are not associated with any subnet. They
+// are assigned regardless of the subnet to which the DHCP client belongs.
+// Global reservations are assigned to the DHCP clients using the
+// same host identifier types as subnet specific reservations. This file
+// contains multiple examples of host reservations using different
+// identifier types, e.g. DUID, MAC address etc.
+{ "Dhcp6":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+// This is pretty basic stuff, it has nothing to do with reservations.
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+// Kea supports three types of identifiers in DHCPv6: hw-address (hardware/MAC
+// address of the client), duid (DUID inserted by the client) and flex-id
+// (flexible identifier available when flex_id hook library is loaded) When told
+// to do so, Kea can check for each of these identifier types, but it takes a
+// costly database lookup to do so. It is therefore useful from a performance
+// perspective to use only the reservation types that are actually used in a
+// given network.
+ "host-reservation-identifiers": [ "duid", "hw-address", "flex-id" ],
+
+// This directive tells Kea that reservations are global. Note that this
+// can also be specified at shared network and/or subnet level.
+// "reservation-mode": "global",
+// It is replaced by the "reservations-global", "reservations-in-subnet", and
+// "reservations-out-of-pool" parameters.
+
+// Specify whether the server should look up global reservations.
+ "reservations-global": true,
+
+// Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": false,
+
+// Specify whether the server can assume that all reserved addresses
+// are out-of-pool.
+// Ignored when reservations-in-subnet is false.
+// If specified, it is inherited by "shared-networks" and "subnet6" levels.
+ "reservations-out-of-pool": false,
+
+// Define several global host reservations.
+ "reservations": [
+
+// This is a simple host reservation. The host with DUID matching
+// the specified value will get an address of 2001:db8:1::100.
+// Note it is not recommended but still allowed to reverse addresses at
+// the global scope: as it breaks the link between the reservation and
+// the subnet it can lead to a client localized in another subnet than
+// its address belongs to.
+ {
+ "duid": "01:02:03:04:05:0A:0B:0C:0D:0E",
+ "ip-addresses": [ "2001:db8:1::100" ]
+ },
+
+// This is similar to the previous one, but this time the reservation
+// is done based on hardware/MAC address. The server will do its best to
+// extract the hardware/MAC address from received packets (see
+// 'mac-sources' directive for details). This particular reservation
+// also specifies two extra options to be available for this client. If
+// there are options with the same code specified in a global, subnet or
+// class scope, the values defined at host level take precedence for
+// this particular DHCP client.
+ {
+ "hw-address": "00:01:02:03:04:05",
+ "ip-addresses": [ "2001:db8:1::101" ],
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "3000:1::234"
+ },
+ {
+ "name": "nis-servers",
+ "data": "3000:1::234"
+ }
+ ],
+ "client-classes": [ "special_snowflake", "office" ]
+ },
+
+// This is a bit more advanced reservation. The client with the specified
+// DUID will get a reserved address, a reserved prefix and a hostname.
+// At least one of the three must be specified in a reservation.
+// Finally, this reservation features vendor specific options for CableLabs,
+// which happen to use enterprise-id 4491. Those particular values will
+// be returned only to the client that has a DUID matching this reservation.
+ {
+ "duid": "01:02:03:04:05:06:07:08:09:0A",
+ "ip-addresses": [ "2001:db8:1:cafe::1" ],
+ "prefixes": [ "2001:db8:2:abcd::/64" ],
+ "hostname": "foo.example.com",
+ "option-data": [
+ {
+ "name": "vendor-opts",
+ "data": "4491"
+ },
+ {
+ "name": "tftp-servers",
+ "space": "vendor-4491",
+ "data": "3000:1::234"
+ }
+ ]
+ },
+
+// This reservation is using flexible identifier. Instead of relying
+// on specific field, sysadmin can define an expression similar to what
+// is used for client classification,
+// e.g. substring(relay[0].option[17],0,6). Then, based on the value of
+// that expression for incoming packet, the reservation is matched.
+// Expression can be specified either as hex or plain text using single
+// quotes.
+// Note: flexible identifier requires flex_id hook library to be
+// loaded to work.
+ {
+ "flex-id": "'somevalue'",
+ "ip-addresses": [ "2001:db8:1:cafe::2" ]
+ }
+ ],
+
+// The following list defines subnets. Subnet, pools and interface definitions
+// are the same as in the regular scenario.
+ "subnet6": [
+ {
+ "id": 1,
+
+ "subnet": "2001:db8::/47",
+
+ "pools": [ { "pool": "2001:db8::/64" } ],
+
+ "pd-pools": [
+ {
+ "prefix": "2001:db8:1:8000::",
+ "prefix-len": 56,
+ "delegated-len": 64
+ }
+ ],
+ "interface": "eth0"
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/ha-hot-standby-server1-with-tls.json b/doc/examples/kea6/ha-hot-standby-server1-with-tls.json
new file mode 100644
index 0000000..9fab542
--- /dev/null
+++ b/doc/examples/kea6/ha-hot-standby-server1-with-tls.json
@@ -0,0 +1,169 @@
+// This is an example configuration of the Kea DHCPv6 server. It uses High
+// Availability hook library and Lease Commands hook library to enable
+// High Availability function for the DHCP server. Note that almost exactly
+// the same configuration must be used on the second server (partner).
+// The only difference is that "this-server-name" must be set to "server2"
+// on this other server. Also, the interface configuration and location of TLS
+// specific files depend on the network settings and configuration of the
+// particular machine.
+//
+// The servers using this configuration work in hot standby mode.
+{
+
+// DHCPv6 configuration starts here.
+"Dhcp6": {
+ // Add names of your network interfaces to listen on.
+ "interfaces-config": {
+ // The DHCPv6 server listens on this interface.
+ "interfaces": [ "eth0" ]
+ },
+
+ // Control socket is required for communication between the Control
+ // Agent and the DHCP server. High Availability requires Control Agent
+ // to be running because lease updates are sent over the RESTful
+ // API between the HA peers.
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea6-ctrl-socket"
+ },
+
+ // Use Memfile lease database backend to store leases in a CSV file.
+ // Depending on how Kea was compiled, it may also support SQL databases
+ // (MySQL and/or PostgreSQL). Those database backends require more
+ // parameters, like name, host and possibly user and password.
+ // There are dedicated examples for each backend. See Section 7.2.2 "Lease
+ // Storage" for details.
+ "lease-database": {
+ // Memfile is the simplest and easiest backend to use. It's an in-memory
+ "type": "memfile"
+ },
+
+ // HA requires two hook libraries to be loaded: libdhcp_lease_cmds.so and
+ // libdhcp_ha.so. The former handles incoming lease updates from the HA peers.
+ // The latter implements high availability feature for Kea.
+ "hooks-libraries": [
+ // The lease_cmds library must be loaded because HA makes use of it to
+ // deliver lease updates to the server as well as synchronize the
+ // lease database after failure.
+ {
+ "library": "/opt/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ },
+ {
+ // The HA hook library should be loaded.
+ "library": "/opt/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ // High Availability configuration is specified for the HA hook library.
+ // Each server should have the same HA configuration, except for the
+ // "this-server-name" parameter.
+ "high-availability": [ {
+ // This parameter points to this server instance. The respective
+ // HA peers must have this parameter set to their own names.
+ "this-server-name": "server1",
+ // The HA mode is set to hot-standby. This server will receive lease
+ // updates from the primary. The primary will be responding to all
+ // DHCP queries.
+ "mode": "hot-standby",
+ // Heartbeat is to be sent every 10 seconds if no other control
+ // commands are transmitted.
+ "heartbeat-delay": 10000,
+ // Maximum time for partner's response to a heartbeat, after which
+ // failure detection is started. This is specified in milliseconds.
+ "max-response-delay": 60000,
+ // The following parameters control how the server detects the
+ // partner's failure. The ACK delay sets the threshold for the
+ // 'secs' field of the received discovers. This is specified in
+ // milliseconds.
+ "max-ack-delay": 5000,
+ // This specifies the number of clients which send messages to
+ // the partner but appear to not receive any response.
+ "max-unacked-clients": 5,
+ // Trust anchor aka certificate authority file or directory.
+ "trust-anchor": "/usr/lib/kea/CA.pem",
+ // Client certificate file name.
+ "cert-file": "/usr/lib/kea/server_cert.pem",
+ // Private key file name.
+ "key-file": "/usr/lib/kea/server_key.pem",
+ // Client certificates are required and verified.
+ "require-client-certs": true,
+ "peers": [
+ // This is the configuration of this server instance.
+ {
+ "name": "server1",
+ // This specifies the URL of this server instance. The
+ // Control Agent must run along with this DHCPv6 server
+ // instance and the "http-host" and "http-port" must be
+ // set to the corresponding values.
+ "url": "http://192.168.56.33:8000/",
+ // This server is primary. The other one must be
+ // standby.
+ "role": "primary"
+ },
+ // This is the configuration of the HA peer.
+ {
+ "name": "server2",
+ // Specifies the URL on which the partner's control
+ // channel can be reached. The Control Agent is required
+ // to run on the partner's machine with "http-host" and
+ // "http-port" values set to the corresponding values.
+ "url": "http://192.168.56.66:8000/",
+ // The partner is standby. This server is primary.
+ "role": "standby"
+ }
+ ]
+ } ]
+ }
+ }
+ ],
+
+ // The following list defines subnets. Each subnet consists of at
+ // least subnet and pool entries.
+ "subnet6": [
+ {
+ "id": 1,
+
+ "subnet": "2001:db8:1::/64",
+
+ "pools": [
+ {
+ "pool": "2001:db8:1::100 - 2001:db8:1::250"
+ }
+ ],
+
+ "interface": "eth0"
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout. Alternatively, you can specify stderr here, a filename
+// or 'syslog', which will store output messages via syslog.
+ "loggers": [
+ {
+ // This section affects kea-dhcp6, which is the base logger for DHCPv6
+ // component. It tells DHCPv6 server to write all log messages (on
+ // severity INFO or more) to a file.
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0
+ },
+ {
+ // This section specifies configuration of the HA hook library-specific
+ // logger.
+ "name": "kea-dhcp6.ha-hooks",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 99
+ }
+ ]
+}
+}
diff --git a/doc/examples/kea6/ha-hot-standby-server2.json b/doc/examples/kea6/ha-hot-standby-server2.json
new file mode 100644
index 0000000..6a9a680
--- /dev/null
+++ b/doc/examples/kea6/ha-hot-standby-server2.json
@@ -0,0 +1,160 @@
+// This is an example configuration of the Kea DHCPv6 server. It uses High
+// Availability hook library and Lease Commands hook library to enable
+// High Availability function for the DHCP server. Note that almost exactly
+// the same configuration must be used on the second server (partner).
+// The only difference is that "this-server-name" must be set to "server1"
+// on this other server. Also, the interface configuration depends on the
+// network settings of the particular machine.
+//
+// The servers using this configuration work in hot standby mode.
+{
+
+// DHCPv6 configuration starts here.
+"Dhcp6": {
+ // Add names of your network interfaces to listen on.
+ "interfaces-config": {
+ // The DHCPv6 server listens on this interface.
+ "interfaces": [ "eth0" ]
+ },
+
+ // Control socket is required for communication between the Control
+ // Agent and the DHCP server. High Availability requires Control Agent
+ // to be running because lease updates are sent over the RESTful
+ // API between the HA peers.
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea6-ctrl-socket"
+ },
+
+ // Use Memfile lease database backend to store leases in a CSV file.
+ // Depending on how Kea was compiled, it may also support SQL databases
+ // (MySQL and/or PostgreSQL). Those database backends require more
+ // parameters, like name, host and possibly user and password.
+ // There are dedicated examples for each backend. See Section 7.2.2 "Lease
+ // Storage" for details.
+ "lease-database": {
+ // Memfile is the simplest and easiest backend to use. It's an in-memory
+ "type": "memfile"
+ },
+
+ // HA requires two hook libraries to be loaded: libdhcp_lease_cmds.so and
+ // libdhcp_ha.so. The former handles incoming lease updates from the HA peers.
+ // The latter implements high availability feature for Kea.
+ "hooks-libraries": [
+ // The lease_cmds library must be loaded because HA makes use of it to
+ // deliver lease updates to the server as well as synchronize the
+ // lease database after failure.
+ {
+ "library": "/opt/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ },
+ {
+ // The HA hook library should be loaded.
+ "library": "/opt/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ // High Availability configuration is specified for the HA hook library.
+ // Each server should have the same HA configuration, except for the
+ // "this-server-name" parameter.
+ "high-availability": [ {
+ // This parameter points to this server instance. The respective
+ // HA peers must have this parameter set to their own names.
+ "this-server-name": "server2",
+ // The HA mode is set to hot-standby. This server will receive lease
+ // updates from the primary. The primary will be responding to all
+ // DHCP queries.
+ "mode": "hot-standby",
+ // Heartbeat is to be sent every 10 seconds if no other control
+ // commands are transmitted.
+ "heartbeat-delay": 10000,
+ // Maximum time for partner's response to a heartbeat, after which
+ // failure detection is started. This is specified in milliseconds.
+ "max-response-delay": 60000,
+ // The following parameters control how the server detects the
+ // partner's failure. The ACK delay sets the threshold for the
+ // 'secs' field of the received discovers. This is specified in
+ // milliseconds.
+ "max-ack-delay": 5000,
+ // This specifies the number of clients which send messages to
+ // the partner but appear to not receive any response.
+ "max-unacked-clients": 5,
+ "peers": [
+ // This is the configuration of the HA peer.
+ {
+ "name": "server1",
+ // Specifies the URL on which the partner's control
+ // channel can be reached. The Control Agent is required
+ // to run on the partner's machine with "http-host" and
+ // "http-port" values set to the corresponding values.
+ "url": "http://192.168.56.33:8000/",
+ // The partner is primary. This server is standby.
+ "role": "primary"
+ },
+ // This is the configuration of this server instance.
+ {
+ "name": "server2",
+ // This specifies the URL of this server instance. The
+ // Control Agent must run along with this DHCPv6 server
+ // instance and the "http-host" and "http-port" must be
+ // set to the corresponding values.
+ "url": "http://192.168.56.66:8000/",
+ // This server is standby. The other one must be
+ // primary.
+ "role": "standby"
+ }
+ ]
+ } ]
+ }
+ }
+ ],
+
+ // The following list defines subnets. Each subnet consists of at
+ // least subnet and pool entries.
+ "subnet6": [
+ {
+ "id": 1,
+
+ "subnet": "2001:db8:1::/64",
+
+ "pools": [
+ {
+ "pool": "2001:db8:1::100 - 2001:db8:1::250"
+ }
+ ],
+
+ "interface": "eth0"
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout. Alternatively, you can specify stderr here, a filename
+// or 'syslog', which will store output messages via syslog.
+ "loggers": [
+ {
+ // This section affects kea-dhcp6, which is the base logger for DHCPv6
+ // component. It tells DHCPv6 server to write all log messages (on
+ // severity INFO or more) to a file.
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0
+ },
+ {
+ // This section specifies configuration of the HA hook library-specific
+ // logger.
+ "name": "kea-dhcp6.ha-hooks",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 99
+ }
+ ]
+}
+}
diff --git a/doc/examples/kea6/hooks.json b/doc/examples/kea6/hooks.json
new file mode 100644
index 0000000..4677250
--- /dev/null
+++ b/doc/examples/kea6/hooks.json
@@ -0,0 +1,58 @@
+// This is an example configuration file for the DHCPv6 server in Kea
+// illustrating the configuration of hook libraries. It uses a basic scenario
+// of one IPv6 subnet configured with the default values for all parameters.
+
+{"Dhcp6":
+
+{
+// Kea is told to listen on the eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// Set up the storage for leases.
+ "lease-database": {
+ "type": "memfile"
+ },
+
+// Set values to mandatory timers
+ "renew-timer": 900,
+ "rebind-timer": 1200,
+ "preferred-lifetime": 1800,
+ "valid-lifetime": 2700,
+
+// Define a single subnet.
+ "subnet6": [
+ {
+ "id": 1,
+ "pools": [
+ {
+ "pool": "2001:db8:1::/80",
+ "user-context": { "charging": true }
+ } ],
+ "subnet": "2001:db8:1::/64",
+ "interface": "eth0"
+ }
+ ],
+
+// Set up the hook libraries. For this example, we assume that two libraries
+// are loaded, called "security" and "charging". Note that order is important:
+// "security" is specified first so if both libraries supply a hook function
+// for a given hook, the function in "security" will be called before that in
+// "charging".
+
+ "hooks-libraries": [
+ {
+ "library": "/opt/lib/security.so"
+ },
+ {
+ "library": "/opt/lib/charging.so",
+ "parameters": {
+ "path": "/var/lib/kea",
+ "base-name": "kea-forensic6"
+ }
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/iPXE.json b/doc/examples/kea6/iPXE.json
new file mode 100644
index 0000000..108233b
--- /dev/null
+++ b/doc/examples/kea6/iPXE.json
@@ -0,0 +1,68 @@
+// This is an example configuration for iPXE boot in Kea6.
+
+{
+ "Dhcp6": {
+ // Mandatory part of the config that list interfaces on which
+ // Kea will listen for incoming traffic.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// Two classes are migrated form ISC-DHCP example:
+// if exists dhcp6.client-arch-type and
+// option dhcp6.client-arch-type = 00:07 {
+// option dhcp6.bootfile-url "http://[2001:db8::1]/ipxe.efi";
+// } else if exists dhcp6.user-class and
+// substring(option dhcp6.user-class, 2, 4) = "iPXE" {
+// option dhcp6.bootfile-url "http://[2001:db8::1]/ubuntu.cfg";
+// }
+
+// In example shown below incoming packet will receive value
+// http://[2001:db8::1]/ubuntu.cfg if incoming packet will include user
+// class option with "iPXE" in it and value http://[2001:db8::1]/ipxe.efi
+// if option client architecture type will be 7.
+// If incoming packet will include both of those options with matching
+// values it will be assigned to class "a-ipxe" because it was first
+// matching class. If you want to change that order names of the classes
+// have to have different alphabetical order. In Kea 1.3.0 (and previous
+// versions) alphabetical order is used in classification. Note this
+// should change in next versions, for instance to keep the definition
+// order.
+ "client-classes": [
+ {
+ "name": "a-ipxe",
+ // user-class option (code 15) is a tuple array
+ // so we need to skip the length (tuple first element)
+ "test": "substring(option[15].hex, 2, 4) == 'iPXE'",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "bootfile-url",
+ "code": 59,
+ "data": "http://[2001:db8::1]/ubuntu.cfg"
+ }
+ ]
+ },
+ {
+ "name": "b-efi",
+ // please consider to add a not a-ipxe here to enforce
+ // the "else"?
+ "test": "option[61].hex == 0x0007",
+ "option-data": [
+ {
+ "space": "dhcp6",
+ "name": "bootfile-url",
+ "code": 59,
+ "data": "http://[2001:db8::1]/ipxe.efi"
+ }
+ ]
+ }
+ ],
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8::/64"
+ }
+ ]
+ }
+}
diff --git a/doc/examples/kea6/leases-expiration.json b/doc/examples/kea6/leases-expiration.json
new file mode 100644
index 0000000..d70b763
--- /dev/null
+++ b/doc/examples/kea6/leases-expiration.json
@@ -0,0 +1,85 @@
+// This is an example configuration file for DHCPv6 server in Kea.
+// It provides parameters controlling processing of expired leases,
+// a.k.a. leases reclamation.
+
+{ "Dhcp6":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+// Note, we're setting the maximum number of row read errors to 100,
+// (defaults to 0, meaning unlimited).
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600,
+ "max-row-errors": 100
+ },
+
+// The following parameters control processing expired leases. Expired leases
+// will be reclaimed periodically according to the "reclaim-timer-wait-time"
+// parameter. Reclaimed leases will be held in the database for 1800s to
+// facilitate lease affinity. After this period the leases will be removed.
+// The frequency of removal is controlled by the
+// "flush-reclaimed-timer-wait-time" parameter. The lease reclamation
+// routine will process at most 500 leases or will last for at most
+// 100ms, during a single run. If there are still some unreclaimed
+// leases after 10 attempts, a warning message is issued.
+// If both "flush-reclaimed-timer-wait-time" and "hold-reclaimed-time" are not
+// 0, when the client sends a release message the lease is expired instead of
+// being deleted from lease storage.
+ "expired-leases-processing": {
+ "reclaim-timer-wait-time": 5,
+ "hold-reclaimed-time": 1800,
+ "flush-reclaimed-timer-wait-time": 10,
+ "max-reclaim-leases": 500,
+ "max-reclaim-time": 100,
+ "unwarned-reclaim-cycles": 10
+ },
+
+// Addresses will be assigned with preferred and valid lifetimes
+// being 3000 and 4000, respectively. Client is told to start
+// renewing after 1000 seconds. If the server does not respond
+// after 2000 seconds since the lease was granted, client is supposed
+// to start REBIND procedure (emergency renewal that allows switching
+// to a different server).
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+// The following list defines subnets. Each subnet consists of at
+// least subnet and pool entries.
+ "subnet6": [
+ {
+ "id": 1,
+ "pools": [ { "pool": "2001:db8:1::/80" } ],
+ "subnet": "2001:db8:1::/64",
+ "interface": "eth0"
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/multiple-options.json b/doc/examples/kea6/multiple-options.json
new file mode 100644
index 0000000..03d83fe
--- /dev/null
+++ b/doc/examples/kea6/multiple-options.json
@@ -0,0 +1,184 @@
+// This is an example configuration file for DHCPv6 server in Kea.
+// It demonstrates simple configuration of the options for a subnet.
+
+{ "Dhcp6":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile"
+ },
+
+// Addresses will be assigned with preferred and valid lifetimes
+// being 3000 and 4000, respectively. Client is told to start
+// renewing after 1000 seconds. If the server does not respond
+// after 2000 seconds since the lease was granted, client is supposed
+// to start REBIND procedure (emergency renewal that allows switching
+// to a different server).
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+// Defining a subnet. There are some DHCP options returned to the
+// clients connected to this subnet. The first option is identified
+// by the name. The second option is identified by the code.
+// There are two address pools defined within this subnet. Pool
+// specific value for option 12 is defined for the pool:
+// 2001:db8:1::1 - 2001:db8:1::100. Clients obtaining an address
+// from this pool will be assigned option 12 with a value of
+// 3001:cafe::21. Clients belonging to this subnet but obtaining
+// addresses from the other pool, or the clients obtaining
+// stateless configuration will be assigned subnet specific value
+// of option 12, i.e. 2001:db8:1:0:ff00::1.
+// For DHCPv6 subnets can have prefix delegation pools too so
+// a pd-pools with an option-data is defined too.
+ "subnet6": [
+ {
+ // This is how option values are defined for this particular subnet.
+ "option-data": [
+ // When specifying options, you typically need to specify
+ // one of (name or code) and data. The full option specification
+ // covers name, code, space, csv-format and data.
+ // space defaults to "dhcp6" which is usually correct, unless you
+ // use encapsulate options. csv-format defaults to "true", so
+ // this is also correct, unless you want to specify the whole
+ // option value as long hex string. For example, to specify
+ // domain-name-servers you could do this:
+ // {
+ // "name": "dns-servers",
+ // "code": 23,
+ // "csv-format": true,
+ // "space": "dhcp6",
+ // "data": "2001:db8:2::45, 2001:db8:2::100"
+ // }
+ // but it's a lot of writing, so it's easier to do this instead:
+ {
+ "name": "dns-servers",
+ "data": "2001:db8:2::45, 2001:db8:2::100"
+ },
+
+ // Typically people prefer to refer to options by their
+ // names, so they don't need to remember the code
+ // names. However, some people like to use numerical
+ // values. For example, DHCPv6 can optionally use server
+ // unicast communication, if extra option is present. Option
+ // "unicast" uses option code 12, so you can reference to it
+ // either by "name": "unicast" or "code": 12.
+ {
+ "code": 12,
+ "data": "2001:db8:1:0:ff00::1"
+ },
+
+ // Options can also be specified using hexadecimal format.
+ // This should be avoided if possible, because Kea ability to
+ // validate correctness is limited when using hex values.
+ {
+ "name": "sntp-servers",
+ "csv-format": false,
+ "data": "20010db8000000000000000000000001"
+ },
+
+ // String options that have a comma in their values need to have
+ // it escaped (i.e. each comma is preceded by two backslashes).
+ // That's because commas are reserved for separating fields in
+ // compound options. At the same time, we need to be conformant
+ // with JSON spec, that does not allow "\,". Therefore the
+ // slightly uncommon double backslashes notation is needed.
+
+ // Legal JSON escapes are \ followed by "\/bfnrt character
+ // or \u followed by 4 hexa-decimal numbers (currently Kea
+ // supports only \u0000 to \u00ff code points).
+ // CSV processing translates '\\' into '\' and '\,' into ','
+ // only so for instance '\x' is translated into '\x'. But
+ // as it works on a JSON string value each of these '\'
+ // characters must be doubled on JSON input.
+ {
+ "name": "new-posix-timezone",
+ "data": "EST5EDT4\\,M3.2.0/02:00\\,M11.1.0/02:00"
+ },
+
+ // Options that take integer values can either be specified in
+ // dec or hex format. Hex format could be either plain (e.g. abcd)
+ // or prefixed with 0x (e.g. 0xabcd).
+ {
+ "name": "preference",
+ "data": "0xf0"
+ },
+
+ // A few options are encoded in (length, string) tuples
+ // which can be defined using only strings as the CSV
+ // processing computes lengths.
+ {
+ "name": "bootfile-param",
+ "data": "root=/dev/sda2, quiet, splash"
+ },
+
+ // At a few exceptions options are added to response only when
+ // the client requests them. The always-send flag should be used
+ // to enforce a particular option.
+ {
+ "name": "pana-agent",
+ "data": "2001:db8:2::123",
+ "always-send": true
+ }
+ ],
+ "pools": [
+ {
+ "pool": "2001:db8:1::1 - 2001:db8:1::100",
+ "option-data": [
+ {
+ "code": 12,
+ "data": "3001:cafe::21"
+ }
+ ]
+ },
+ {
+ "pool": "2001:db8:1::500 - 2001:db8:1::1000"
+ }
+ ],
+ "pd-pools": [
+ {
+ "prefix": "2001:2b8:2::",
+ "prefix-len": 56,
+ "delegated-len": 64,
+ "option-data": [
+ {
+ "code": 12,
+ "data": "3001:cafe::12"
+ }
+ ]
+ }
+ ],
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "interface": "eth0"
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/mysql-reservations.json b/doc/examples/kea6/mysql-reservations.json
new file mode 100644
index 0000000..cc1acc3
--- /dev/null
+++ b/doc/examples/kea6/mysql-reservations.json
@@ -0,0 +1,101 @@
+// This is an example configuration file for the DHCPv6 server in Kea.
+// It contains configuration of the MySQL host database backend, used
+// to retrieve reserved addresses, host names, DHCPv4 message fields
+// and DHCP options from MySQL database.
+{ "Dhcp6":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+// This is pretty basic stuff, it has nothing to do with reservations.
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+// Kea supports two types of identifiers in DHCPv6: hw-address
+// (hardware/MAC address of the client) and duid (DUID inserted by the
+// client). When told to do so, Kea can check for each of these
+// identifier types, but it takes a costly database lookup to do so. It
+// is therefore useful from a performance perspective to use only the
+// reservation types that are actually used in a given network.
+ "host-reservation-identifiers": [ "duid", "hw-address" ],
+
+// Specify connection to the database holding host reservations. The type
+// specifies that the MySQL database is used. user and password are the
+// credentials used to connect to the database. host and name specify
+// location of the host where the database instance is running, and the
+// name of the database to use. The server processing a packet will first
+// check if there are any reservations specified for this client in the
+// reservations list, within the subnet (configuration file). If there are
+// no reservations there, the server will try to retrieve reservations
+// from this database.
+ "hosts-database": {
+ "type": "mysql",
+ "reconnect-wait-time": 3000, // expressed in ms
+ "max-reconnect-tries": 3,
+ "name": "keatest",
+ "user": "keatest",
+ "password": "keatest",
+ "host": "localhost",
+ "port": 3306,
+ "readonly": true,
+ "trust-anchor": "my-ca",
+ "cert-file": "my-cert",
+ "key-file": "my-key",
+ "cipher-list": "AES"
+ },
+
+// Define a subnet with a pool of dynamic addresses and a pool of dynamic
+// prefixes. Addresses and prefixes from those pools will be assigned to
+// clients which don't have reservations in the database. Subnet identifier
+// is equal to 1. If this subnet is selected for the client, this subnet
+// id will be used to search for the reservations within the database.
+ "subnet6": [
+ {
+ "subnet": "2001:db8:1::/48",
+
+ "pools": [ { "pool": "2001:db8:1::/80" } ],
+
+ "pd-pools": [
+ {
+ "prefix": "2001:db8:1:8000::",
+ "prefix-len": 56,
+ "delegated-len": 64
+ }
+ ],
+ "interface": "eth0",
+ "id": 1
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/pgsql-reservations.json b/doc/examples/kea6/pgsql-reservations.json
new file mode 100644
index 0000000..6da8d15
--- /dev/null
+++ b/doc/examples/kea6/pgsql-reservations.json
@@ -0,0 +1,98 @@
+// This is an example configuration file for the DHCPv6 server in Kea.
+// It contains configuration of the PostgreSQL host database backend, used
+// to retrieve reserved addresses, host names, DHCPv4 message fields
+// and DHCP options from PostgreSQL database.
+{ "Dhcp6":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile"
+ },
+
+// This is pretty basic stuff, it has nothing to do with reservations.
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+// Kea supports two types of identifiers in DHCPv6: hw-address
+// (hardware/MAC address of the client) and duid (DUID inserted by the
+// client). When told to do so, Kea can check for each of these
+// identifier types, but it takes a costly database lookup to do so. It
+// is therefore useful from a performance perspective to use only the
+// reservation types that are actually used in a given network.
+ "host-reservation-identifiers": [ "duid", "hw-address" ],
+
+// Specify connection to the database holding host reservations. The type
+// specifies that the PostgreSQL database is used. user and password are the
+// credentials used to connect to the database. host and name specify
+// location of the host where the database instance is running, and the
+// name of the database to use. The server processing a packet will first
+// check if there are any reservations specified for this client in the
+// reservations list, within the subnet (configuration file). If there are
+// no reservations there, the server will try to retrieve reservations
+// from this database.
+// The database specification can go into one hosts-database entry for
+// backward compatibility or be listed in hosts-databases list.
+ "hosts-databases": [
+ {
+ "type": "postgresql",
+ "reconnect-wait-time": 3000, // expressed in ms
+ "max-reconnect-tries": 3,
+ "name": "keatest",
+ "user": "keatest",
+ "password": "keatest",
+ "host": "localhost"
+ }
+ ],
+
+// Define a subnet with a pool of dynamic addresses and a pool of dynamic
+// prefixes. Addresses and prefixes from those pools will be assigned to
+// clients which don't have reservations in the database. Subnet identifier
+// is equal to 1. If this subnet is selected for the client, this subnet
+// id will be used to search for the reservations within the database.
+ "subnet6": [
+ {
+ "subnet": "2001:db8:1::/48",
+
+ "pools": [ { "pool": "2001:db8:1::/80" } ],
+
+ "pd-pools": [
+ {
+ "prefix": "2001:db8:1:8000::",
+ "prefix-len": 56,
+ "delegated-len": 64
+ }
+ ],
+ "interface": "eth0",
+ "id": 1
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/reservations.json b/doc/examples/kea6/reservations.json
new file mode 100644
index 0000000..c793cb8
--- /dev/null
+++ b/doc/examples/kea6/reservations.json
@@ -0,0 +1,171 @@
+// This is an example configuration file for DHCPv6 server in Kea
+// that showcases how to do host reservations. It is
+// assumed that one subnet (2001:db8:1::/64) is available directly
+// over eth0 interface. A number of hosts have various combinations
+// of addresses and prefixes reserved for them.
+
+{ "Dhcp6":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+// This is pretty basic stuff, it has nothing to do with reservations.
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+// Kea supports three types of identifiers in DHCPv6: hw-address (hardware/MAC
+// address of the client), duid (DUID inserted by the client) and flex-id
+// (flexible identifier available when flex_id hook library is loaded) When told
+// to do so, Kea can check for each of these identifier types, but it takes a
+// costly database lookup to do so. It is therefore useful from a performance
+// perspective to use only the reservation types that are actually used in a
+// given network.
+ "host-reservation-identifiers": [ "duid", "hw-address", "flex-id" ],
+
+// The following list defines subnets. Subnet, pools and interface definitions
+// are the same as in the regular scenario, without host reservations.
+// least subnet and pool entries.
+ "subnet6": [
+ {
+ "id": 1,
+
+ "subnet": "2001:db8:1::/48",
+
+ // This directive tells Kea that reservations may be made both in-pool
+ // and out-of-pool. For improved performance, you may move all reservations
+ // out of the dynamic pool and change reservation-mode to "out-of-pool".
+ // Kea will then be able to skip querying for host reservations when
+ // assigning leases from dynamic pool.
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global", "reservations-in-subnet"
+ // and "reservations-out-of-pool" parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ // If specified, it is inherited by "shared-networks" and
+ // "subnet6" levels.
+ "reservations-out-of-pool": false,
+
+ "pools": [ { "pool": "2001:db8:1::/120" } ],
+
+ "pd-pools": [
+ {
+ "prefix": "2001:db8:1:8000::",
+ "prefix-len": 56,
+ "delegated-len": 64
+ }
+ ],
+ "interface": "eth0",
+
+// Host reservations. Define several reservations, note that
+// they are all within the range of the pool of the dynamically
+// allocated address. The server will exclude the addresses from this
+// pool and only assign them to the client which has a reservation for
+// them.
+ "reservations": [
+// This is a simple host reservation. The host with DUID matching
+// the specified value will get an address of 2001:db8:1::100.
+ {
+ "duid": "01:02:03:04:05:0A:0B:0C:0D:0E",
+ "ip-addresses": [ "2001:db8:1::100" ]
+ },
+// This is similar to the previous one, but this time the reservation
+// is done based on hardware/MAC address. The server will do its best to
+// extract the hardware/MAC address from received packets (see
+// 'mac-sources' directive for details). This particular reservation
+// also specifies two extra options to be available for this client. If
+// there are options with the same code specified in a global, subnet or
+// class scope, the values defined at host level take precedence.
+ {
+ "hw-address": "00:01:02:03:04:05",
+ "ip-addresses": [ "2001:db8:1::101" ],
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "3000:1::234"
+ },
+ {
+ "name": "nis-servers",
+ "data": "3000:1::234"
+ } ],
+ "client-classes": [ "special_snowflake", "office" ]
+ },
+// This is a bit more advanced reservation. The client with the specified
+// DUID will get a reserved address, a reserved prefix and a hostname.
+// This reservation is for an address that it not within the dynamic pool.
+// Finally, this reservation features vendor specific options for CableLabs,
+// which happen to use enterprise-id 4491. Those particular values will
+// be returned only to the client that has a DUID matching this reservation.
+ {
+ "duid": "01:02:03:04:05:06:07:08:09:0A",
+ "ip-addresses": [ "2001:db8:1:cafe::1" ],
+ "prefixes": [ "2001:db8:2:abcd::/64" ],
+ "hostname": "foo.example.com",
+ "option-data": [ {
+ "name": "vendor-opts",
+ "data": "4491"
+ },
+ {
+ "name": "tftp-servers",
+ "space": "vendor-4491",
+ "data": "3000:1::234"
+ } ]
+
+ },
+// This reservation is using flexible identifier. Instead of relying
+// on specific field, sysadmin can define an expression similar to what
+// is used for client classification,
+// e.g. substring(relay[0].option[17],0,6). Then, based on the value of
+// that expression for incoming packet, the reservation is matched.
+// Expression can be specified either as hex or plain text using single
+// quotes.
+// Note: flexible identifier requires flex_id hook library to be
+// loaded to work.
+ {
+ "flex-id": "'somevalue'",
+ "ip-addresses": [ "2001:db8:1:cafe::2" ]
+ }
+
+ ]
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/several-subnets.json b/doc/examples/kea6/several-subnets.json
new file mode 100644
index 0000000..063ddb4
--- /dev/null
+++ b/doc/examples/kea6/several-subnets.json
@@ -0,0 +1,61 @@
+// This is an example configuration file for DHCPv6 server in Kea.
+// It's a basic scenario with four IPv6 subnets configured. In each
+// subnet, there's a smaller pool of dynamic addresses.
+
+{ "Dhcp6":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile"
+ },
+
+// Addresses will be assigned with preferred and valid lifetimes
+// being 3000 and 4000, respectively. Client is told to start
+// renewing after 1000 seconds. If the server does not respond
+// after 2000 seconds since the lease was granted, client is supposed
+// to start REBIND procedure (emergency renewal that allows switching
+// to a different server).
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+// The following list defines subnets. Each subnet consists of at
+// least subnet and pool entries.
+ "subnet6": [
+ { "pools": [ { "pool": "2001:db8:1::/80" } ],
+ "id": 1, "subnet": "2001:db8:1::/64" },
+ { "pools": [ { "pool": "2001:db8:2::/80" } ],
+ "id": 2, "subnet": "2001:db8:2::/64" },
+ { "pools": [ { "pool": "2001:db8:3::/80" } ],
+ "id": 3, "subnet": "2001:db8:3::/64" },
+ { "pools": [ { "pool": "2001:db8:4::/80" } ],
+ "id": 4, "subnet": "2001:db8:4::/64" } ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/shared-network.json b/doc/examples/kea6/shared-network.json
new file mode 100644
index 0000000..0b3416e
--- /dev/null
+++ b/doc/examples/kea6/shared-network.json
@@ -0,0 +1,153 @@
+// This is an example configuration file for DHCPv6 server in Kea.
+// It demonstrates an advanced feature called shared network. Typically, for
+// each physical link there is one IPv6 subnet that the server is expected
+// to manage. However, in some cases there is a need to configure more subnets
+// in the same physical location. This may sound odd, as IPv6 is not expected
+// to run out of addresses. However, due to vast address space some deployments
+// experiment with various addressing schemes and later find out that the
+// initial proposal was not best and way to migrate to something else.
+{
+ "Dhcp6": {
+ // Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+ // You also need to tell where to store lease information.
+ // memfile is the backend that is easiest to set up.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+ // It is likely that in your network you'll have a mix of regular,
+ // "plain" subnets and shared networks. It is perfectly valid to mix
+ // them in the same config file.
+
+ // This is regular subnet. It's not part of any shared-network.
+ "subnet6": [
+ {
+ "id": 1,
+ "pools": [ { "pool": "2001:db8:2::/80" } ],
+ "subnet": "2001:db8:2::/64",
+ "interface": "eth0"
+ }
+ ],
+
+ // Hhe shared networks definition starts here. shared-networks can
+ // contain a list of shared networks. There are many parameters
+ // that can be specified here, so this example may be overwhelming
+ // at first, but the only mandatory parameter for each shared
+ // network is name. It must be unique. Typically, each shared
+ // network also needs to have at least two subnets to be functional,
+ // but if you really want to, you can define a degraded shared
+ // network that has 1 or even 0 subnets. This may come in handy
+ // when migrating between regular subnets and shared networks
+ // or when debugging a problem. It is not recommended to use
+ // 1 subnet per shared network, as there is extra processing
+ // overhead for shared networks.
+ "shared-networks": [
+ {
+ "interface": "eth1",
+
+ // Similar to regular subnets, it is forbidden to define both
+ // interface and interface-id at the same time. That's because
+ // interface parameter expresses physical network interface
+ // for links available locally and interface-id identifies
+ // values inserted by relays, which are only used for
+ // remote traffic. A shared network cannot be both direct
+ // and relayed.
+ // "interface-id": "content of the option",
+
+ // Other parameters defined here will be inherited by the
+ // subnets.
+ "name": "frog",
+ "option-data": [ ],
+ "preferred-lifetime": 200,
+ "rapid-commit": true,
+ "rebind-timer": 150,
+ "relay": {
+ "ip-address": "2001:db8::1"
+ },
+ "renew-timer": 100,
+
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and "reservations-out-of-pool"
+ // parameters.
+
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ // If specified, it is inherited by "subnet6" levels.
+ "reservations-out-of-pool": false,
+
+ // List of subnets belonging to this particular shared-network
+ // start here.
+ "subnet6": [
+
+ // This is the first subnet.
+ {
+ "preferred-lifetime": 30,
+ "rapid-commit": false,
+ "rebind-timer": 20,
+ // It is possible to override some values here.
+ "relay": {
+ "ip-address": "2001:db8:1::123"
+ },
+ "renew-timer": 10,
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and "reservations-out-of-pool"
+ // parameters.
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ "reservations-out-of-pool": false,
+ "id": 2,
+ "subnet": "2001:db8:1::/64",
+ "pools": [ { "pool": "2001:db8:1:0:abcd::/80" } ],
+ "valid-lifetime": 40
+ },
+
+ // This is the second subnet.
+ {
+ "preferred-lifetime": 30,
+ "pools": [ { "pool": "3000:db8::/64" } ],
+ "rapid-commit": false,
+ "rebind-timer": 20,
+ "relay": {
+ "ip-address": "3000::1"
+ },
+ "renew-timer": 10,
+ // "reservation-mode": "all",
+ // It is replaced by the "reservations-global",
+ // "reservations-in-subnet", and "reservations-out-of-pool"
+ // parameters.
+ // Specify whether the server should look up global reservations.
+ "reservations-global": false,
+ // Specify whether the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+ // Specify whether the server can assume that all reserved addresses
+ // are out-of-pool.
+ // Ignored when reservations-in-subnet is false.
+ "reservations-out-of-pool": false,
+ "id": 3,
+ "subnet": "3000::/16",
+ "valid-lifetime": 40
+ }
+ ],
+ "valid-lifetime": 300
+ } ]
+ }
+}
diff --git a/doc/examples/kea6/simple.json b/doc/examples/kea6/simple.json
new file mode 100644
index 0000000..3b6d3d5
--- /dev/null
+++ b/doc/examples/kea6/simple.json
@@ -0,0 +1,63 @@
+// This is an example configuration file for DHCPv6 server in Kea.
+// It's a basic scenario with one IPv6 subnet configured. It is
+// assumed that one subnet (2001:db8:1::/64 is available directly
+// over eth0 interface.
+
+{ "Dhcp6":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+// Addresses will be assigned with preferred and valid lifetimes
+// being 3000 and 4000, respectively. Client is told to start
+// renewing after 1000 seconds. If the server does not respond
+// after 2000 seconds since the lease was granted, client is supposed
+// to start REBIND procedure (emergency renewal that allows switching
+// to a different server).
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+// The following list defines subnets. Each subnet consists of at
+// least subnet and pool entries.
+ "subnet6": [
+ {
+ "id": 1,
+ "pools": [ { "pool": "2001:db8:1::/80" } ],
+ "subnet": "2001:db8:1::/64",
+ "interface": "eth0"
+ }
+ ],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout. Alternatively, you can specify stderr here, a filename
+// or 'syslog', which will store output messages via syslog.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/softwire46.json b/doc/examples/kea6/softwire46.json
new file mode 100644
index 0000000..bbc810c
--- /dev/null
+++ b/doc/examples/kea6/softwire46.json
@@ -0,0 +1,90 @@
+// This is an example configuration file for DHCPv6 server in Kea.
+// It demonstrates how user can specify values for Softwire options
+// defined in RFC 7598 for Lightweight 4over6 architecture.
+
+{ "Dhcp6":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// Let's use a Memfile backend to store leases.
+ "lease-database": {
+ "type": "memfile"
+ },
+
+// Addresses will be assigned with preferred and valid lifetimes
+// being 3000 and 4000, respectively. Client is told to start
+// renewing after 1000 seconds. If the server does not respond
+// after 2000 seconds since the lease was granted, client is supposed
+// to start REBIND procedure (emergency renewal that allows switching
+// to a different server).
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+// The following list defines subnets. Each subnet consists of at
+// least subnet and pool entries.
+ "subnet6": [
+ {
+ "id": 1,
+ "pools": [ { "pool": "2001:db8:1::/80" } ],
+ "subnet": "2001:db8:1::/64",
+ "interface": "eth0",
+// Include MAP-E Container option for hosts connected to this subnet.
+ "option-data": [
+ {
+ "name": "s46-cont-mape"
+ }
+ ],
+// Send host specific softwire options.
+ "reservations": [
+ {
+ "duid": "01:02:03:04:05:06:07:08:09:0A",
+ "option-data": [
+// These two options will be included in the MAP-E Container
+ {
+ "space": "s46-cont-mape-options",
+ "name": "s46-rule",
+ "data": "1, 0, 24, 192.0.2.0, 2001:db8:1::/64"
+ },
+ {
+ "space": "s46-cont-mape-options",
+ "name": "s46-br",
+ "data": "2001:db8:cafe::1"
+ },
+// This option will be included in the S46 Rule option. It includes
+// PSID/PSID length value in a user friendly form. The PSID length
+// specifies the number of bits on which PSID is coded. The PSID
+// value 3 is a 4th value that is coded on these 4 bits: "0011b".
+ {
+ "space": "s46-rule-options",
+ "name": "s46-portparams",
+ "data": "0, 3/4"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+
+// The following configures logging. Kea will log all debug messages
+// to /var/log/kea-debug.log file.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "/var/log/kea-debug.log"
+ }
+ ],
+ "debuglevel": 99,
+ "severity": "DEBUG"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/stateless.json b/doc/examples/kea6/stateless.json
new file mode 100644
index 0000000..bbe1a84
--- /dev/null
+++ b/doc/examples/kea6/stateless.json
@@ -0,0 +1,29 @@
+// A very simply stateless configuration that provides information about DNS
+// servers to all clients, regardless of their point of attachment.
+//
+// It is also possible to specify options on a per subnet basis
+// in the same way as in stateful mode.
+//
+
+{
+"Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// This is the list of options that will be granted to all clients that ask.
+ "option-data": [ {
+ "name": "dns-servers",
+ "data": "2001:db8::1, 2001:db8::2"
+ } ],
+
+// Kea 0.9.1 requires lease-database to be specified, even it is not used.
+// In stateless mode, only options are granted, not addresses or
+// prefixes, so there will be no leases (unless stateless and stateful
+// mode is used together).
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ }
+}
+}
diff --git a/doc/examples/kea6/tee-times.json b/doc/examples/kea6/tee-times.json
new file mode 100644
index 0000000..e69cc09
--- /dev/null
+++ b/doc/examples/kea6/tee-times.json
@@ -0,0 +1,73 @@
+// This is an example configuration file for DHCPv6 server in Kea.
+// It's a basic scenario with three IPv6 subnets use different
+// methods for determining T1 and T2 values.
+
+{ "Dhcp6":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile"
+ },
+
+// Addresses will be assigned with preferred and valid lifetimes
+// being 3000 and 4000, respectively. By default calculate-tee-times
+// is true with values of .5 and .8 for t1-percent and t2-percent
+// respectively. Since some of our subnets will use calculated values and
+// we must NOT specify global values for renew-timer and rebind-timer.
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+
+// The following list defines subnets. Each subnet consists of at
+// least subnet and pool entries.
+ "subnet6": [
+ {
+ // This subnet use default calculation
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "pools": [ { "pool": "2001:db8:1::/80" } ]
+ },
+ {
+ // This subnet will use explicit values. Explicit
+ // values override calculation.
+ "id": 2,
+ "subnet": "2001:db8:2::/64",
+ "pools": [ { "pool": "2001:db8:2::/80" } ],
+ "renew-timer": 1000,
+ "rebind-timer": 2000
+ },
+ {
+ // This subnet will use custom percents
+ "id": 3,
+ "subnet": "2001:db8:3::/64",
+ "pools": [ { "pool": "2001:db8:3::/80" } ],
+ "t1-percent": .45,
+ "t2-percent": .7
+ }],
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/with-ddns.json b/doc/examples/kea6/with-ddns.json
new file mode 100644
index 0000000..4f2de1f
--- /dev/null
+++ b/doc/examples/kea6/with-ddns.json
@@ -0,0 +1,89 @@
+// This is an example configuration file for DHCPv6 server in Kea.
+// It's a basic scenario with one IPv6 subnet configured. It is
+// assumed that one subnet (2001:db8:1::/64 is available directly
+// over eth0 interface.
+
+{ "Dhcp6":
+
+{
+// Kea is told to listen on eth0 interface only.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+// We need to specify the database used to store leases. As of
+// June 2022, three database backends are supported: MySQL,
+// PostgreSQL and the in-memory database, Memfile.
+// We'll use memfile because it doesn't require any prior set up.
+ "lease-database": {
+ "type": "memfile",
+ "lfc-interval": 3600
+ },
+
+// Addresses will be assigned with preferred and valid lifetimes
+// being 3000 and 4000, respectively. Client is told to start
+// renewing after 1000 seconds. If the server does not respond
+// after 2000 seconds since the lease was granted, client is supposed
+// to start REBIND procedure (emergency renewal that allows switching
+// to a different server).
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+// The following list defines subnets. Each subnet consists of at
+// least subnet and pool entries.
+ "subnet6": [
+ {
+ "pools": [ { "pool": "2001:db8:1::/80" } ],
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "interface": "eth0"
+ }
+ ],
+
+// Enable connectivity with kea-dhcp-ddns
+// (Required for dynamic DNS updates)
+ "dhcp-ddns" : {
+ "enable-updates" : true,
+ "server-ip" : "3001::1",
+ "server-port" : 3432,
+ "sender-ip" : "3001::2",
+ "sender-port" : 3433,
+ "max-queue-size" : 2048,
+ "ncr-protocol" : "UDP",
+ "ncr-format" : "JSON"
+ },
+
+
+// Enable DDNS updates and configure DDNS update behavior
+ "ddns-send-updates" : true,
+ "ddns-override-no-update" : true,
+ "ddns-override-client-update" : true,
+ "ddns-replace-client-name" : "when-present",
+ "ddns-generated-prefix" : "test.prefix",
+ "ddns-qualifying-suffix" : "test.suffix.",
+ "ddns-update-on-renew" : false,
+ "ddns-conflict-resolution-mode": "check-with-dhcid",
+ "ddns-ttl-percent" : 0.75,
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+ "hostname-char-replacement": "x",
+
+// The following configures logging. It assumes that messages with at
+// least informational level (info, warn, error and fatal) should be
+// logged to stdout.
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "debuglevel": 0,
+ "severity": "INFO"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/netconf/comments.json b/doc/examples/netconf/comments.json
new file mode 100644
index 0000000..99014e7
--- /dev/null
+++ b/doc/examples/netconf/comments.json
@@ -0,0 +1,36 @@
+// This is a example of a configuration for Netconf.
+// It uses embedded (i.e., which will be included in configuration objects
+// and not stripped by at lexical analysis) comments.
+
+{
+ "Netconf":
+ {
+ // Global scope
+ "comment": "The Netconf Agent",
+
+ // In servers
+ "managed-servers":
+ {
+ "dhcp4":
+ {
+ "comment": "the model is mandatory",
+ "model": "kea-dhcp4-server",
+ // In control socket.
+ "control-socket":
+ {
+ "comment": "using unix/local socket",
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ }
+ }
+ },
+
+ // In loggers
+ "loggers": [
+ {
+ "comment": "A logger",
+ "name": "kea-ctrl-agent"
+ }
+ ]
+ }
+}
diff --git a/doc/examples/netconf/kea-dhcp6-operations/boot.json b/doc/examples/netconf/kea-dhcp6-operations/boot.json
new file mode 100644
index 0000000..18d1da8
--- /dev/null
+++ b/doc/examples/netconf/kea-dhcp6-operations/boot.json
@@ -0,0 +1,8 @@
+{
+ "Dhcp6": {
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea6-ctrl-socket"
+ }
+ }
+}
diff --git a/doc/examples/netconf/kea-dhcp6-operations/logging.xml b/doc/examples/netconf/kea-dhcp6-operations/logging.xml
new file mode 100644
index 0000000..7ce04e4
--- /dev/null
+++ b/doc/examples/netconf/kea-dhcp6-operations/logging.xml
@@ -0,0 +1,26 @@
+<config xmlns="urn:ietf:params:xml:ns:yang:kea-dhcp6-server">
+ <interfaces-config>
+ <interfaces>eth1</interfaces>
+ </interfaces-config>
+ <subnet6>
+ <id>1</id>
+ <pool>
+ <start-address>2001:db8::1:0</start-address>
+ <end-address>2001:db8::1:ffff</end-address>
+ <prefix>2001:db8::1:0/112</prefix>
+ </pool>
+ <subnet>2001:db8::/64</subnet>
+ </subnet6>
+ <control-socket>
+ <socket-name>/tmp/kea6-ctrl-socket</socket-name>
+ <socket-type>unix</socket-type>
+ </control-socket>
+ <logger>
+ <name>kea-dhcp6</name>
+ <output-option>
+ <output>stderr</output>
+ </output-option>
+ <debuglevel>99</debuglevel>
+ <severity>DEBUG</severity>
+ </logger>
+</config>
diff --git a/doc/examples/netconf/kea-dhcp6-operations/netconf.json b/doc/examples/netconf/kea-dhcp6-operations/netconf.json
new file mode 100644
index 0000000..653a40c
--- /dev/null
+++ b/doc/examples/netconf/kea-dhcp6-operations/netconf.json
@@ -0,0 +1,31 @@
+{
+ "Netconf":
+ {
+ "managed-servers":
+ {
+ "dhcp6":
+ {
+ "control-socket":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea6-ctrl-socket"
+ }
+ }
+ },
+
+ "loggers":
+ [
+ {
+ "name": "kea-netconf",
+ "output-options":
+ [
+ {
+ "output": "stderr"
+ }
+ ],
+ "severity": "DEBUG",
+ "debuglevel": 99
+ }
+ ]
+ }
+}
diff --git a/doc/examples/netconf/kea-dhcp6-operations/startup.xml b/doc/examples/netconf/kea-dhcp6-operations/startup.xml
new file mode 100644
index 0000000..b085833
--- /dev/null
+++ b/doc/examples/netconf/kea-dhcp6-operations/startup.xml
@@ -0,0 +1,18 @@
+<config xmlns="urn:ietf:params:xml:ns:yang:kea-dhcp6-server">
+ <subnet6>
+ <id>1</id>
+ <pool>
+ <start-address>2001:db8::1:0</start-address>
+ <end-address>2001:db8::1:ffff</end-address>
+ <prefix>2001:db8::1:0/112</prefix>
+ </pool>
+ <subnet>2001:db8::/64</subnet>
+ </subnet6>
+ <interfaces-config>
+ <interfaces>eth1</interfaces>
+ </interfaces-config>
+ <control-socket>
+ <socket-name>/tmp/kea6-ctrl-socket</socket-name>
+ <socket-type>unix</socket-type>
+ </control-socket>
+</config>
diff --git a/doc/examples/netconf/kea-dhcp6-operations/twopools.xml b/doc/examples/netconf/kea-dhcp6-operations/twopools.xml
new file mode 100644
index 0000000..8fb32c9
--- /dev/null
+++ b/doc/examples/netconf/kea-dhcp6-operations/twopools.xml
@@ -0,0 +1,23 @@
+<config xmlns="urn:ietf:params:xml:ns:yang:kea-dhcp6-server">
+ <subnet6>
+ <id>1</id>
+ <pool>
+ <start-address>2001:db8::1:0</start-address>
+ <end-address>2001:db8::1:ffff</end-address>
+ <prefix>2001:db8::1:0/112</prefix>
+ </pool>
+ <pool>
+ <start-address>2001:db8::2:0</start-address>
+ <end-address>2001:db8::2:ffff</end-address>
+ <prefix>2001:db8::2:0/112</prefix>
+ </pool>
+ <subnet>2001:db8::/64</subnet>
+ </subnet6>
+ <interfaces-config>
+ <interfaces>eth1</interfaces>
+ </interfaces-config>
+ <control-socket>
+ <socket-name>/tmp/kea6-ctrl-socket</socket-name>
+ <socket-type>unix</socket-type>
+ </control-socket>
+</config>
diff --git a/doc/examples/netconf/kea-dhcp6-operations/twosubnets.xml b/doc/examples/netconf/kea-dhcp6-operations/twosubnets.xml
new file mode 100644
index 0000000..ba68a06
--- /dev/null
+++ b/doc/examples/netconf/kea-dhcp6-operations/twosubnets.xml
@@ -0,0 +1,27 @@
+<config xmlns="urn:ietf:params:xml:ns:yang:kea-dhcp6-server">
+ <subnet6>
+ <id>1</id>
+ <pool>
+ <start-address>2001:db8:1::</start-address>
+ <end-address>2001:db8:1::ffff</end-address>
+ <prefix>2001:db8:1::/112</prefix>
+ </pool>
+ <subnet>2001:db8:1::/64</subnet>
+ </subnet6>
+ <subnet6>
+ <id>2</id>
+ <pool>
+ <start-address>2001:db8:2::</start-address>
+ <end-address>2001:db8:2::ffff</end-address>
+ <prefix>2001:db8:2::/112</prefix>
+ </pool>
+ <subnet>2001:db8:2::/64</subnet>
+ </subnet6>
+ <interfaces-config>
+ <interfaces>eth1</interfaces>
+ </interfaces-config>
+ <control-socket>
+ <socket-name>/tmp/kea6-ctrl-socket</socket-name>
+ <socket-type>unix</socket-type>
+ </control-socket>
+</config>
diff --git a/doc/examples/netconf/simple-dhcp4.json b/doc/examples/netconf/simple-dhcp4.json
new file mode 100644
index 0000000..887c673
--- /dev/null
+++ b/doc/examples/netconf/simple-dhcp4.json
@@ -0,0 +1,119 @@
+// This is a simple example of a configuration for Netconf that handles
+// DHCPv4 configuration. This example provides YANG interface for
+// DHCPv4 server only.
+{
+ "Netconf":
+ {
+ // Three flags control netconf (default values are true):
+ // - "boot-update" about the YANG configuration load when
+ // netconf boots.
+ // - "subscribe-changes" about the subscription to notifications
+ // when the running YANG module is changed.
+ // - "validate-changes" allows to validate changes or not.
+ "boot-update": true,
+ "subscribe-changes": true,
+ "validate-changes": true,
+
+ // This map specifies how each server is managed:
+ // the YANG model to use and the control channel.
+ "managed-servers":
+ {
+ // This is how Netconf can communicate with the DHCPv4 server.
+ "dhcp4":
+ {
+ // Eventually, the kea-netconf will be able to handle multiple
+ // models. However, for the time being the only choice for
+ // DHCPv4 server is kea-dhcp4-server model.
+ "model": "kea-dhcp4-server",
+
+ // The three control flags can be defined in this scope too
+ // and takes precedence over global and default values.
+ // boot-update determines whether the initial configuration
+ // should be retrieved from netconf during kea-netconf startup.
+ // You almost always want to set this to yes.
+ "boot-update": true,
+
+ // This flag control whether the kea-netconf daemon should
+ // subscribe to any changes. If set to true, kea-netconf will
+ // monitor sysrepo and will pick up any changes that may be
+ // introduced, either using netconf clients or sysrepocfg.
+ "subscribe-changes": true,
+
+ // This parameters specifies whether kea-netconf will attempt
+ // to verify if the upcoming NETCONF configuration is sane. The
+ // verification is done by calling config-test. Depending on
+ // Kea response, the new configuration is accepted or rejected.
+ "validate-changes": false,
+
+ // Currently three control channel types are supported:
+ // - "stdout" which output the configuration on the standard
+ // output (this is mainly for testing purposes, but you can
+ // use simple script (such as curl or socat) to pass that
+ // information to the server.
+ // - "unix" which uses the local control channel supported by
+ // "dhcp4" and "dhcp6" servers ("d2" support is coming in Kea 1.5)
+ // - "http" which uses the Control Agent (CA) to manage itself or
+ // to forward commands to "dhcp4" or "dhcp6".
+ "control-socket":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Comment is optional. You can put some notes here.
+ "comment": "Kea DHCPv4 server serving network on floor 13"
+ }
+
+ },
+
+ // Netconf is able to load hook libraries that augment its operation.
+ // The primary functionality is the ability to add new commands.
+
+ // Uncomment this section to load a hook library.
+
+ // "hooks-libraries": [
+ // // Hook libraries list may contain more than one library.
+ // {
+ // // The only necessary parameter is the library filename.
+ // "library": "/opt/local/netconf-commands.so",
+
+ // // Some libraries may support parameters. Make sure you
+ // // type this section carefully, as the CA does not validate
+ // // it (because the format is library-specific).
+ // "parameters": {
+ // "param1": "foo"
+ // }
+ // }
+ // ]
+
+ // Similar to other Kea components, Netconf also uses logging.
+ "loggers": [
+ {
+ "name": "kea-netconf",
+ "output-options": [
+ {
+ // "output": "/var/log/kea-netconf.log",
+ "output": "stdout",
+ // Several additional parameters are possible in addition
+ // to the typical output. Flush determines whether logger
+ // flushes output to a file. Maxsize determines maximum
+ // filesize before the file is rotated. maxver
+ // specifies the maximum number of rotated files being
+ // kept.
+ "flush": true,
+ "maxsize": 204800,
+ "maxver": 4,
+ // We use pattern to specify custom log message layout
+ "pattern": "%d{%y.%m.%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+ }
+ ],
+ // You can change the severity to DEBUG, INFO, WARN, ERROR or
+ // CRIT. For DEBUG level, you can also additionally specify
+ // debuglevel (0-99, higher = more verbose). All configurations
+ // are logged on DEBUG/55.
+ "severity": "INFO",
+ "debuglevel": 0
+ }
+ ]
+ }
+}
diff --git a/doc/examples/netconf/simple-dhcp6.json b/doc/examples/netconf/simple-dhcp6.json
new file mode 100644
index 0000000..1829a44
--- /dev/null
+++ b/doc/examples/netconf/simple-dhcp6.json
@@ -0,0 +1,121 @@
+// This is a simple example of a configuration for Netconf that handles
+// DHCPv6 configuration. This example provides YANG interface for
+// DHCPv6 server only.
+{
+ "Netconf":
+ {
+ // Three flags control netconf (default values are true):
+ // - "boot-update" about the YANG configuration load when
+ // netconf boots.
+ // - "subscribe-changes" about the subscription to notifications
+ // when the running YANG module is changed.
+ // - "validate-changes" allows to validate changes or not.
+ "boot-update": true,
+ "subscribe-changes": true,
+ "validate-changes": true,
+
+ // This map specifies how each server is managed:
+ // the YANG model to use and the control channel.
+ "managed-servers":
+ {
+ // This is how Netconf can communicate with the DHCPv6 server.
+ "dhcp6":
+ {
+ // Eventually, the kea-netconf will be able to handle multiple
+ // models. However, for the time being the choices for
+ // DHCPv6 server are kea-dhcp6-server and
+ // ietf-dhcpv6-server models but only the first is usable.
+ "model": "kea-dhcp6-server",
+
+ // The three control flags can be defined in this scope too
+ // and takes precedence over global and default values.
+ // boot-update determines whether the initial configuration
+ // should be retrieved from netconf during kea-netconf startup.
+ // You almost always want to set this to yes.
+ "boot-update": true,
+
+ // This flag control whether the kea-netconf daemon should
+ // subscribe to any changes. If set to true, kea-netconf will
+ // monitor sysrepo and will pick up any changes that may be
+ // introduced, either using netconf clients or sysrepocfg.
+ "subscribe-changes": true,
+
+ // This parameters specifies whether kea-netconf will attempt
+ // to verify if the upcoming NETCONF configuration is sane. The
+ // verification is done by calling config-test. Depending on
+ // Kea response, the new configuration is accepted or rejected.
+ "validate-changes": false,
+
+ // Currently three control channel types are supported:
+ // - "stdout" which output the configuration on the standard
+ // output (this is mainly for testing purposes, but you can
+ // use simple script (such as curl or socat) to pass that
+ // information to the server.
+ // - "unix" which uses the local control channel supported by
+ // "dhcp4" and "dhcp6" servers ("d2" support is coming in Kea 1.5)
+ // - "http" which uses the Control Agent (CA) to manage itself or
+ // to forward commands to "dhcp4" or "dhcp6" (not yest supported).
+ "control-socket":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea6-ctrl-socket"
+ },
+
+ // Comment is optional. You can put some notes here.
+ "comment": "Kea DHCPv6 server serving network on floor 13"
+ }
+
+ },
+
+ // Netconf is able to load hook libraries that augment its operation.
+ // The primary functionality is the ability to add new commands.
+
+ // Uncomment this section to load a hook library.
+
+ // "hooks-libraries": [
+ // // Hook libraries list may contain more than one library.
+ // {
+ // // The only necessary parameter is the library filename.
+ // "library": "/opt/local/netconf-commands.so",
+
+ // // Some libraries may support parameters. Make sure you
+ // // type this section carefully, as the CA does not validate
+ // // it (because the format is library-specific).
+ // "parameters": {
+ // "param1": "foo"
+ // }
+ // }
+ // ]
+
+ // Similar to other Kea components, Netconf also uses logging.
+ "loggers": [
+ {
+ "name": "kea-netconf",
+ "output-options": [
+ {
+ // "output": "/var/log/kea-netconf.log",
+ "output": "stdout",
+ // Several additional parameters are possible in addition
+ // to the typical output. Flush determines whether logger
+ // flushes output to a file. Maxsize determines maximum
+ // filesize before the file is rotated. maxver
+ // specifies the maximum number of rotated files being
+ // kept.
+ "flush": true,
+ "maxsize": 204800,
+ "maxver": 4,
+ // We use pattern to specify custom log message layout
+ "pattern": "%d{%y.%m.%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+
+ }
+ ],
+ // You can change the severity to DEBUG, INFO, WARN, ERROR or
+ // CRIT. For DEBUG level, you can also additionally specify
+ // debuglevel (0-99, higher = more verbose). All configurations
+ // are logged on DEBUG/55.
+ "severity": "INFO",
+ "debuglevel": 0
+ }
+ ]
+ }
+}
diff --git a/doc/examples/template-ha-mt-tls/info.md b/doc/examples/template-ha-mt-tls/info.md
new file mode 100644
index 0000000..f551220
--- /dev/null
+++ b/doc/examples/template-ha-mt-tls/info.md
@@ -0,0 +1,87 @@
+Template: Secure High Availability Kea DHCP with Multi-Threading
+----------------------------------------------------------------
+
+Below are some templates to assist in configuring a secure Kea DHCP server with
+multi-threading. These templates make the following assumptions:
+
+- The administrator wants to set up High Availability (HA) with multi-threading.
+- The machines running Kea with multi-threading have at least four CPU cores.
+- The connection to the peer is secured using TLS.
+
+The logical setup consists of two hosts, each running a Kea DHCPv4 server and a Control Agent (CA).
+In the multi-threading setup, the CA is not required, as the server is using its
+own dedicated HTTP listener to communicate with the peer. However, the CA can still
+be used to handle user commands.
+
+.. code-block:: none
+
+ +-host-1-+ +-host-2-+
+ | | | |
+ | CA | | CA | ===== - HTTPS connection
+ | # | | # |
+ | # | | # | ##### - UNIX socket
+ | # | | # |
+ | DHCPv4 ========= DHCPv4 |
+ | | | |
+ +--------+ +--------+
+
+The CAs on host-1 and host-2 both listen on port 8001, and the server's dedicated HTTP
+listener uses port 8000. The DHCP servers communicate with each other via the dedicated HTTP
+listener, which forwards only the lease-update commands to the peer server.
+
+Deployment Considerations
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The setup is not expected to scale automatically. This example uses four threads for
+processing DHCP traffic, four threads for listening and handling HA peer HTTP requests,
+and four threads for sending lease updates to the HA peer. The thread queue used to
+store incoming DHCP requests is set to 64, but proper testing and benchmarks are required
+to determine the appropriate values for best performance on the deployment setup.
+
+In this example, there are two hosts running Kea:
+
+- 192.168.1.2 - primary HA server (active, handles all the traffic)
+
+- 192.168.1.3 - secondary HA server (passive, ready to take over if the primary fails)
+
+The network is 192.168.1.0/24. It is assumed that 192.168.1.1 is the default router.
+
+The whole subnet is split into dynamic pools:
+
+- 192.168.1.100 - 192.168.1.199 - this is the dynamic pool. When new devices appear in the network,
+ they are assigned dynamic addresses from this pool.
+
+To deploy this setup, follow the steps provided in the power user home setup with the following distinctions:
+
+1. Install the CA only if the administrator is planning to manage Kea using the RESTful API.
+ Otherwise, the CA is not required for the High Availability Kea server with multi-threading.
+
+2. Alter the following to match the local setup:
+
+ - The paths to ``trust-anchor``, ``cert-file``, and ``key-file`` must be set to the
+ respective values corresponding to the deployment machine.
+
+ - The addressing must be updated, if using something other than 192.168.1.0/24. Make sure the CA port
+ configuration (``http-host`` and ``http-port`` in ``kea-ca.conf``) is different from the DHCPv4 server
+ configuration (``url`` in ``hook-libraries/parameters/high-availability/peers`` in ``kea-dhcp4.conf``).
+ The CA is used to handle only management commands, as the HA module sends lease updates using
+ the dedicated HTTP listener to the peer.
+
+3. Verify the communication between the HA peers by checking the Kea logs.
+
+4. Verify that communication between the hosts works in the opposite direction as well
+ (host-2 can connect to host-1), by repeating step 3 from host-2 using host-1's IP address and port.
+
+5. Install the CA and DHCPv4 on host-2, as in steps 1 and 2. The config file for the
+ standby server is very similar to the one on the primary server, other than the definition of
+ the ``this-server-name`` field (and possibly the interface names).
+
+Possible Extensions
+~~~~~~~~~~~~~~~~~~~
+
+This sample configuration is basic but functional. Once it is set up and running, administrators
+may wish to consider the following changes:
+
+- If using a database, it is also possible to configure TLS for the database backend (for
+ lease, host, configuration backend, or forensic logging). See :ref:`database-connectivity`
+ for more information.
diff --git a/doc/examples/template-ha-mt-tls/kea-ca-1.conf b/doc/examples/template-ha-mt-tls/kea-ca-1.conf
new file mode 100644
index 0000000..765dd9c
--- /dev/null
+++ b/doc/examples/template-ha-mt-tls/kea-ca-1.conf
@@ -0,0 +1,90 @@
+// This is an example of a configuration for Control-Agent (CA) listening
+// for incoming HTTPS traffic. This is necessary for handling API commands.
+// For a High Availability setup with multi-threading enabled the CA is not
+// needed as the peers communicate using a dedicated HTTP listener.
+
+// It is expected to run with a standby (the passive) server, which has a very similar
+// configuration. The only difference is that the location of TLS specific files
+// depend on the configuration of the particular machine.
+{
+ "Control-agent":
+ {
+ // We need to specify where the agent should listen to incoming HTTP
+ // queries.
+ "http-host": "192.168.1.2",
+
+ // TLS trust anchor (Certificate Authority). This is a file name or
+ // (for OpenSSL only) a directory path.
+ "trust-anchor": "/usr/lib/kea/CA.pem",
+
+ // TLS server certificate file name.
+ "cert-file": "/usr/lib/kea/ca1_cert.pem",
+
+ // TLS server private key file name.
+ "key-file": "/usr/lib/kea/ca1_key.pem",
+
+ // TLS require client certificates flag.
+ "cert-required": true,
+
+ // This specifies the port CA will listen on.
+ // If enabling HA and multi-threading, the 8000 port is used by the HA
+ // hook library http listener. When using HA hook library with
+ // multi-threading to function, make sure the port used by dedicated
+ // listener is different (e.g. 8001) than the one used by CA. Note
+ // the commands should still be sent via CA. The dedicated listener
+ // is specifically for HA updates only.
+ "http-port": 8001,
+
+ "control-sockets":
+ {
+ // This is how the Agent can communicate with the DHCPv4 server.
+ "dhcp4":
+ {
+ "comment": "socket to DHCPv4 server",
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Location of the DHCPv6 command channel socket.
+ "dhcp6":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea6-ctrl-socket"
+ },
+
+ // Location of the D2 command channel socket.
+ "d2":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea-ddns-ctrl-socket",
+ "user-context": { "in-use": false }
+ }
+ },
+
+ // Similar to other Kea components, CA also uses logging.
+ "loggers": [
+ {
+ "name": "kea-ctrl-agent",
+ "output-options": [
+ {
+ "output": "/var/log/kea-ctrl-agent.log",
+
+ // Several additional parameters are possible in addition
+ // to the typical output. Flush determines whether logger
+ // flushes output to a file. Maxsize determines maximum
+ // filesize before the file is rotated. maxver
+ // specifies the maximum number of rotated files being
+ // kept.
+ "flush": true,
+ "maxsize": 204800,
+ "maxver": 4,
+ // We use pattern to specify custom log message layout
+ "pattern": "%d{%y.%m.%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0 // debug level only applies when severity is set to DEBUG.
+ }
+ ]
+ }
+}
diff --git a/doc/examples/template-ha-mt-tls/kea-ca-2.conf b/doc/examples/template-ha-mt-tls/kea-ca-2.conf
new file mode 100644
index 0000000..72eb73b
--- /dev/null
+++ b/doc/examples/template-ha-mt-tls/kea-ca-2.conf
@@ -0,0 +1,90 @@
+// This is an example of a configuration for Control-Agent (CA) listening
+// for incoming HTTPS traffic. This is necessary for handling API commands.
+// For a High Availability setup with multi-threading enabled the CA is not
+// needed as the peers communicate using a dedicated HTTP listener.
+
+// It is expected to run with a primary (the active) server, which has a very similar
+// configuration. The only difference is that the location of TLS specific files
+// depend on the configuration of the particular machine.
+{
+ "Control-agent":
+ {
+ // We need to specify where the agent should listen to incoming HTTP
+ // queries.
+ "http-host": "192.168.1.3",
+
+ // TLS trust anchor (Certificate Authority). This is a file name or
+ // (for OpenSSL only) a directory path.
+ "trust-anchor": "/usr/lib/kea/CA.pem",
+
+ // TLS server certificate file name.
+ "cert-file": "/usr/lib/kea/ca2_cert.pem",
+
+ // TLS server private key file name.
+ "key-file": "/usr/lib/kea/ca2_key.pem",
+
+ // TLS require client certificates flag.
+ "cert-required": true,
+
+ // This specifies the port CA will listen on.
+ // If enabling HA and multi-threading, the 8000 port is used by the HA
+ // hook library http listener. When using HA hook library with
+ // multi-threading to function, make sure the port used by dedicated
+ // listener is different (e.g. 8001) than the one used by CA. Note
+ // the commands should still be sent via CA. The dedicated listener
+ // is specifically for HA updates only.
+ "http-port": 8001,
+
+ "control-sockets":
+ {
+ // This is how the Agent can communicate with the DHCPv4 server.
+ "dhcp4":
+ {
+ "comment": "socket to DHCPv4 server",
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Location of the DHCPv6 command channel socket.
+ "dhcp6":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea6-ctrl-socket"
+ },
+
+ // Location of the D2 command channel socket.
+ "d2":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea-ddns-ctrl-socket",
+ "user-context": { "in-use": false }
+ }
+ },
+
+ // Similar to other Kea components, CA also uses logging.
+ "loggers": [
+ {
+ "name": "kea-ctrl-agent",
+ "output-options": [
+ {
+ "output": "/var/log/kea-ctrl-agent.log",
+
+ // Several additional parameters are possible in addition
+ // to the typical output. Flush determines whether logger
+ // flushes output to a file. Maxsize determines maximum
+ // filesize before the file is rotated. maxver
+ // specifies the maximum number of rotated files being
+ // kept.
+ "flush": true,
+ "maxsize": 204800,
+ "maxver": 4,
+ // We use pattern to specify custom log message layout
+ "pattern": "%d{%y.%m.%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0 // debug level only applies when severity is set to DEBUG.
+ }
+ ]
+ }
+}
diff --git a/doc/examples/template-ha-mt-tls/kea-dhcp4-1.conf b/doc/examples/template-ha-mt-tls/kea-dhcp4-1.conf
new file mode 100644
index 0000000..0dc1198
--- /dev/null
+++ b/doc/examples/template-ha-mt-tls/kea-dhcp4-1.conf
@@ -0,0 +1,238 @@
+// This is an example configuration of the Kea DHCPv4 server 1:
+//
+// - uses High Availability hook library and Lease Commands hook library
+// to enable High Availability function for the DHCP server. This config
+// file is for the primary (the active) server.
+// - uses memfile, which stores lease data in a local CSV file
+// - it assumes a single /24 addressing over a link that is directly reachable
+// (no DHCP relays)
+// - there is a handful of IP reservations
+//
+// It is expected to run with a standby (the passive) server, which has a very similar
+// configuration. The only difference is that "this-server-name" must be set to "server2" on the
+// other server. Also, the interface configuration and location of TLS specific files
+// depend on the network settings and configuration of the particular machine.
+
+{
+
+"Dhcp4": {
+
+ // Add names of your network interfaces to listen on.
+ "interfaces-config": {
+ // The DHCPv4 server listens on this interface. When changing this to
+ // the actual name of your interface, make sure to also update the
+ // interface parameter in the subnet definition below.
+ "interfaces": [ "enp0s8" ]
+ },
+
+ // Control socket is required for communication between the Control
+ // Agent and the DHCP server. High Availability requires Control Agent
+ // to be running because lease updates are sent over the RESTful
+ // API between the HA peers.
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Multi-threading parameters.
+ "multi-threading": {
+ // By default, Kea processes packets on multiple threads if the hardware permits.
+ "enable-multi-threading": true,
+
+ // When multi-threading is enabled, Kea will process packets on a
+ // number of multiple threads configurable through this option. The
+ // value must be a positive integer (0 means auto-detect).
+ "thread-pool-size": 4,
+
+ // When multi-threading is enabled, Kea will read packets from the
+ // interface and append a working item to the thread pool. This
+ // option configures the maximum number of items that can be queued.
+ // The value must be a positive integer (0 means unlimited).
+ "packet-queue-size": 64
+ },
+
+ // Use Memfile lease database backend to store leases in a CSV file.
+ // Depending on how Kea was compiled, it may also support SQL databases
+ // (MySQL and/or PostgreSQL). Those database backends require more
+ // parameters, like name, host and possibly user and password.
+ // There are dedicated examples for each backend. See Section 7.2.2 "Lease
+ // Storage" for details.
+ "lease-database": {
+ // Memfile is the simplest and easiest backend to use. It's an in-memory
+ // database with data being written to a CSV file. It is very similar to
+ // what ISC DHCP does.
+ "type": "memfile"
+ },
+
+ // Let's configure some global parameters. The home network is not very dynamic
+ // and there's no shortage of addresses, so no need to recycle aggressively.
+ "valid-lifetime": 43200, // leases will be valid for 12h
+ "renew-timer": 21600, // clients should renew every 6h
+ "rebind-timer": 32400, // clients should start looking for other servers after 9h
+
+ // Kea will clean up its database of expired leases once per hour. However, it
+ // will keep the leases in expired state for 2 days. This greatly increases the
+ // chances for returning devices to get the same address again. To guarantee that,
+ // use host reservation.
+ // If both "flush-reclaimed-timer-wait-time" and "hold-reclaimed-time" are
+ // not 0, when the client sends a release message the lease is expired
+ // instead of being deleted from lease storage.
+ "expired-leases-processing": {
+ "reclaim-timer-wait-time": 3600,
+ "hold-reclaimed-time": 172800,
+ "max-reclaim-leases": 0,
+ "max-reclaim-time": 0
+ },
+
+ // HA requires two hook libraries to be loaded: libdhcp_lease_cmds.so and
+ // libdhcp_ha.so. The former handles incoming lease updates from the HA peers.
+ // The latter implements high availability feature for Kea. Note the library name
+ // should be the same, but the path is OS specific.
+ "hooks-libraries": [
+ // The lease_cmds library must be loaded because HA makes use of it to
+ // deliver lease updates to the server as well as synchronize the
+ // lease database after failure.
+ {
+ "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
+ },
+
+ {
+ // The HA hook library should be loaded.
+ "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ // Each server should have the same HA configuration, except for the
+ // "this-server-name" parameter.
+ "high-availability": [ {
+ // This parameter points to this server instance. The respective
+ // HA peers must have this parameter set to their own names.
+ "this-server-name": "server1",
+ // The HA mode is set to hot-standby. In this mode, the active server handles
+ // all the traffic. The standby takes over if the primary becomes unavailable.
+ "mode": "hot-standby",
+ // Heartbeat is to be sent every 10 seconds if no other control
+ // commands are transmitted.
+ "heartbeat-delay": 10000,
+ // Maximum time for partner's response to a heartbeat, after which
+ // failure detection is started. This is specified in milliseconds.
+ // If we don't hear from the partner in 60 seconds, it's time to
+ // start worrying.
+ "max-response-delay": 60000,
+ // The following parameters control how the server detects the
+ // partner's failure. The ACK delay sets the threshold for the
+ // 'secs' field of the received discovers. This is specified in
+ // milliseconds.
+ "max-ack-delay": 5000,
+ // This specifies the number of clients which send messages to
+ // the partner but appear to not receive any response.
+ "max-unacked-clients": 5,
+ // This specifies the maximum timeout (in milliseconds) for the server
+ // to complete sync. If you have a large deployment (high tens or
+ // hundreds of thousands of clients), you may need to increase it
+ // further. The default value is 60000ms (60 seconds).
+ "sync-timeout": 60000,
+ // Multi-threading parameters.
+ // To not experience performance degradation when the Kea server is
+ // processing packets on multiple threads, the High Availability module
+ // must have multi-threading enabled.
+ "multi-threading": {
+ // Enable High Availability to benefit from multi-threading. Default: true.
+ "enable-multi-threading": true,
+ // When running in MT mode, the dedicated listener is used to handle
+ // lease updates.
+ "http-dedicated-listener": true,
+ // The number of threads used to handle incoming requests.
+ // A value of 0 instructs the server to use the same number of
+ // threads that the Kea core is using for DHCP multi-threading.
+ "http-listener-threads": 0,
+ // The number of threads used to handle outgoing requests.
+ // A value of 0 instructs the server to use the same number of
+ // threads that the Kea core is using for DHCP multi-threading.
+ "http-client-threads": 0
+ },
+ "peers": [
+ // This is the configuration of this server instance.
+ {
+ "name": "server1",
+ // This specifies the URL of this server dedicated HTTP listener.
+ // The Control Agent is not needed for the High Availability
+ // with multi-threading, but if it is used, it must use
+ // different values for "http-host" and "http-port".
+ "url": "http://192.168.1.2:8000/",
+ // Trust anchor aka certificate authority file or directory.
+ "trust-anchor": "/usr/lib/kea/CA.pem",
+ // Client certificate file name.
+ "cert-file": "/usr/lib/kea/server1_cert.pem",
+ // Private key file name.
+ "key-file": "/usr/lib/kea/server1_key.pem",
+ // Client certificates are required and verified.
+ "require-client-certs": true,
+ // This server is primary. The other one must be
+ // secondary.
+ "role": "primary"
+ },
+ // This is the configuration of the secondary server.
+ {
+ "name": "server2",
+ // This specifies the URL of the other server's dedicated HTTP listener.
+ // The Control Agent is not needed for the High Availability
+ // with multi-threading, but if it is used, it must use
+ // different values for "http-host" and "http-port".
+ "url": "http://192.168.1.3:8000/",
+ // Trust anchor aka certificate authority file or directory.
+ "trust-anchor": "/usr/lib/kea/CA.pem",
+ // Client certificate file name.
+ "cert-file": "/usr/lib/kea/server2_cert.pem",
+ // Private key file name.
+ "key-file": "/usr/lib/kea/server2_key.pem",
+ // Client certificates are required and verified.
+ "require-client-certs": true,
+ // The other server is secondary. This one must be
+ // primary.
+ "role": "standby"
+ }
+ ]
+ } ]
+ }
+ }
+ ],
+
+ // This example contains a single subnet declaration.
+ "subnet4": [
+ {
+ // Subnet prefix.
+ "subnet": "192.168.1.0/24",
+
+ // There are no relays in this network, so we need to tell Kea that this subnet
+ // is reachable directly via the specified interface.
+ "interface": "enp0s8",
+
+ // Specify a dynamic address pool.
+ "pools": [
+ {
+ "pool": "192.168.1.100-192.168.1.199"
+ }
+ ]
+ }
+ ],
+
+ // Logging configuration starts here.
+ "loggers": [
+ {
+ // This section affects kea-dhcp4, which is the base logger for DHCPv4 component. It tells
+ // DHCPv4 server to write all log messages (on severity INFO or higher) to a file. The file
+ // will be rotated once it grows to 2MB and up to 4 files will be kept. The debuglevel
+ // (range 0 to 99) is used only when logging on DEBUG level.
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "/var/log/kea-dhcp4.log",
+ "maxsize": 2048000,
+ "maxver": 4
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0
+ }
+ ]
+}
+}
diff --git a/doc/examples/template-ha-mt-tls/kea-dhcp4-2.conf b/doc/examples/template-ha-mt-tls/kea-dhcp4-2.conf
new file mode 100644
index 0000000..070569b
--- /dev/null
+++ b/doc/examples/template-ha-mt-tls/kea-dhcp4-2.conf
@@ -0,0 +1,238 @@
+// This is an example configuration of the Kea DHCPv4 server 2:
+//
+// - uses High Availability hook library and Lease Commands hook library
+// to enable High Availability function for the DHCP server. This config
+// file is for the secondary (the standby) server.
+// - uses memfile, which stores lease data in a local CSV file
+// - it assumes a single /24 addressing over a link that is directly reachable
+// (no DHCP relays)
+// - there is a handful of IP reservations
+//
+// It is expected to run with a primary (the active) server, which has a very similar
+// configuration. The only difference is that "this-server-name" must be set to "server2" on the
+// other server. Also, the interface configuration and location of TLS specific files
+// depend on the network settings and configuration of the particular machine.
+
+{
+
+"Dhcp4": {
+
+ // Add names of your network interfaces to listen on.
+ "interfaces-config": {
+ // The DHCPv4 server listens on this interface. When changing this to
+ // the actual name of your interface, make sure to also update the
+ // interface parameter in the subnet definition below.
+ "interfaces": [ "enp0s8" ]
+ },
+
+ // Control socket is required for communication between the Control
+ // Agent and the DHCP server. High Availability requires Control Agent
+ // to be running because lease updates are sent over the RESTful
+ // API between the HA peers.
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Multi-threading parameters.
+ "multi-threading": {
+ // By default, Kea processes packets on multiple threads if the hardware permits.
+ "enable-multi-threading": true,
+
+ // When multi-threading is enabled, Kea will process packets on a
+ // number of multiple threads configurable through this option. The
+ // value must be a positive integer (0 means auto-detect).
+ "thread-pool-size": 4,
+
+ // When multi-threading is enabled, Kea will read packets from the
+ // interface and append a working item to the thread pool. This
+ // option configures the maximum number of items that can be queued.
+ // The value must be a positive integer (0 means unlimited).
+ "packet-queue-size": 64
+ },
+
+ // Use Memfile lease database backend to store leases in a CSV file.
+ // Depending on how Kea was compiled, it may also support SQL databases
+ // (MySQL and/or PostgreSQL). Those database backends require more
+ // parameters, like name, host and possibly user and password.
+ // There are dedicated examples for each backend. See Section 7.2.2 "Lease
+ // Storage" for details.
+ "lease-database": {
+ // Memfile is the simplest and easiest backend to use. It's an in-memory
+ // database with data being written to a CSV file. It is very similar to
+ // what ISC DHCP does.
+ "type": "memfile"
+ },
+
+ // Let's configure some global parameters. The home network is not very dynamic
+ // and there's no shortage of addresses, so no need to recycle aggressively.
+ "valid-lifetime": 43200, // leases will be valid for 12h
+ "renew-timer": 21600, // clients should renew every 6h
+ "rebind-timer": 32400, // clients should start looking for other servers after 9h
+
+ // Kea will clean up its database of expired leases once per hour. However, it
+ // will keep the leases in expired state for 2 days. This greatly increases the
+ // chances for returning devices to get the same address again. To guarantee that,
+ // use host reservation.
+ // If both "flush-reclaimed-timer-wait-time" and "hold-reclaimed-time" are
+ // not 0, when the client sends a release message the lease is expired
+ // instead of being deleted from lease storage.
+ "expired-leases-processing": {
+ "reclaim-timer-wait-time": 3600,
+ "hold-reclaimed-time": 172800,
+ "max-reclaim-leases": 0,
+ "max-reclaim-time": 0
+ },
+
+ // HA requires two hook libraries to be loaded: libdhcp_lease_cmds.so and
+ // libdhcp_ha.so. The former handles incoming lease updates from the HA peers.
+ // The latter implements high availability feature for Kea. Note the library name
+ // should be the same, but the path is OS specific.
+ "hooks-libraries": [
+ // The lease_cmds library must be loaded because HA makes use of it to
+ // deliver lease updates to the server as well as synchronize the
+ // lease database after failure.
+ {
+ "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
+ },
+
+ {
+ // The HA hook library should be loaded.
+ "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ // Each server should have the same HA configuration, except for the
+ // "this-server-name" parameter.
+ "high-availability": [ {
+ // This parameter points to this server instance. The respective
+ // HA peers must have this parameter set to their own names.
+ "this-server-name": "server2",
+ // The HA mode is set to hot-standby. In this mode, the active server handles
+ // all the traffic. The standby takes over if the primary becomes unavailable.
+ "mode": "hot-standby",
+ // Heartbeat is to be sent every 10 seconds if no other control
+ // commands are transmitted.
+ "heartbeat-delay": 10000,
+ // Maximum time for partner's response to a heartbeat, after which
+ // failure detection is started. This is specified in milliseconds.
+ // If we don't hear from the partner in 60 seconds, it's time to
+ // start worrying.
+ "max-response-delay": 60000,
+ // The following parameters control how the server detects the
+ // partner's failure. The ACK delay sets the threshold for the
+ // 'secs' field of the received discovers. This is specified in
+ // milliseconds.
+ "max-ack-delay": 5000,
+ // This specifies the number of clients which send messages to
+ // the partner but appear to not receive any response.
+ "max-unacked-clients": 5,
+ // This specifies the maximum timeout (in milliseconds) for the server
+ // to complete sync. If you have a large deployment (high tens or
+ // hundreds of thousands of clients), you may need to increase it
+ // further. The default value is 60000ms (60 seconds).
+ "sync-timeout": 60000,
+ // Multi-threading parameters.
+ // To not experience performance degradation when the Kea server is
+ // processing packets on multiple threads, the High Availability module
+ // must have multi-threading enabled.
+ "multi-threading": {
+ // Enable High Availability to benefit from multi-threading. Default: true.
+ "enable-multi-threading": true,
+ // When running in MT mode, the dedicated listener is used to handle
+ // lease updates.
+ "http-dedicated-listener": true,
+ // The number of threads used to handle incoming requests.
+ // A value of 0 instructs the server to use the same number of
+ // threads that the Kea core is using for DHCP multi-threading.
+ "http-listener-threads": 0,
+ // The number of threads used to handle outgoing requests.
+ // A value of 0 instructs the server to use the same number of
+ // threads that the Kea core is using for DHCP multi-threading.
+ "http-client-threads": 0
+ },
+ "peers": [
+ // This is the configuration of the primary server.
+ {
+ "name": "server1",
+ // This specifies the URL of the other server's dedicated HTTP listener.
+ // The Control Agent is not needed for the High Availability
+ // with multi-threading, but if it is used, it must use
+ // different values for "http-host" and "http-port".
+ "url": "http://192.168.1.2:8000/",
+ // Trust anchor aka certificate authority file or directory.
+ "trust-anchor": "/usr/lib/kea/CA.pem",
+ // Client certificate file name.
+ "cert-file": "/usr/lib/kea/server1_cert.pem",
+ // Private key file name.
+ "key-file": "/usr/lib/kea/server1_key.pem",
+ // Client certificates are required and verified.
+ "require-client-certs": true,
+ // The other server is primary. This one must be
+ // secondary.
+ "role": "primary"
+ },
+ // This is the configuration of this server instance.
+ {
+ "name": "server2",
+ // This specifies the URL of this server dedicated HTTP listener.
+ // The Control Agent is not needed for the High Availability
+ // with multi-threading, but if it is used, it must use
+ // different values for "http-host" and "http-port".
+ "url": "http://192.168.1.3:8000/",
+ // Trust anchor aka certificate authority file or directory.
+ "trust-anchor": "/usr/lib/kea/CA.pem",
+ // Client certificate file name.
+ "cert-file": "/usr/lib/kea/server2_cert.pem",
+ // Private key file name.
+ "key-file": "/usr/lib/kea/server2_key.pem",
+ // Client certificates are required and verified.
+ "require-client-certs": true,
+ // This server is secondary. The other one must be
+ // primary.
+ "role": "standby"
+ }
+ ]
+ } ]
+ }
+ }
+ ],
+
+ // This example contains a single subnet declaration.
+ "subnet4": [
+ {
+ // Subnet prefix.
+ "subnet": "192.168.1.0/24",
+
+ // There are no relays in this network, so we need to tell Kea that this subnet
+ // is reachable directly via the specified interface.
+ "interface": "enp0s8",
+
+ // Specify a dynamic address pool.
+ "pools": [
+ {
+ "pool": "192.168.1.100-192.168.1.199"
+ }
+ ]
+ }
+ ],
+
+ // Logging configuration starts here.
+ "loggers": [
+ {
+ // This section affects kea-dhcp4, which is the base logger for DHCPv4 component. It tells
+ // DHCPv4 server to write all log messages (on severity INFO or higher) to a file. The file
+ // will be rotated once it grows to 2MB and up to 4 files will be kept. The debuglevel
+ // (range 0 to 99) is used only when logging on DEBUG level.
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "/var/log/kea-dhcp4.log",
+ "maxsize": 2048000,
+ "maxver": 4
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0
+ }
+ ]
+}
+}
diff --git a/doc/examples/template-power-user-home/info.md b/doc/examples/template-power-user-home/info.md
new file mode 100644
index 0000000..9335ff8
--- /dev/null
+++ b/doc/examples/template-power-user-home/info.md
@@ -0,0 +1,124 @@
+Template: Home Network of a Power User
+--------------------------------------
+
+Below are some templates to assist in configuring the home network of a power user; they may also be
+appropriate for a small office. These templates make the following assumptions:
+
+- The administrator wants to use a single /24 class of IPv4 addresses.
+- High Availability (HA) is desired, so there are two DHCP servers.
+- There are a handful of devices, and some of them (e.g. a printer or NAS) require
+ static addresses or extra options.
+- The administrator does not want to be bothered with database management.
+- The setup is optimized for minimal to zero maintenance.
+- Performance is not an issue; hundreds of queries per second are not expected.
+- IPv6 is not used.
+- DNS updates will not be performed by Kea.
+
+The logical setup consists of two hosts, each running a Kea DHCPv4 server and a Control Agent (CA).
+The server connects with the CA using UNIX sockets. Each DHCPv4+CA acts as one partner of the HA
+pair.
+
+.. code-block:: none
+
+ +-host-1-+ +-host-2-+
+ | | | |
+ | CA <===\ /===> CA | ===== - HTTP connection
+ | # | \ / | # |
+ | # | X | # | ##### - UNIX socket
+ | # | / \ # |
+ | DHCPv4 ==/ \== DHCPv4 |
+ | | | |
+ +--------+ +--------+
+
+The CAs on host-1 and host-2 both listen on port 8000. The DHCP servers communicate
+with each other via the CAs, which forward control commands to the DHCP servers over the UNIX domain
+sockets.
+
+Deployment Considerations
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This setup is not expected to be very performant. Most modest hardware will do; Kea has been successfully
+deployed on Raspberry Pi platforms, for example. If it is running on a VM, 2GB of RAM with one CPU core should
+be sufficient. Ubuntu LTS is a choice that is easy to set up and is
+low maintenance; however, any Linux or FreeBSD operating system is fine. Less popular systems, such as OpenBSD or
+NetBSD, should also work in principle, but they are not regularly tested.
+
+In this example, there are two hosts running Kea:
+
+- 192.168.1.2 - primary HA server (active, handles all the traffic)
+
+- 192.168.1.3 - secondary HA server (passive, ready to take over if the primary fails)
+
+The network is 192.168.1.0/24. It is assumed that 192.168.1.1 is the default router.
+
+The whole subnet is split into dynamic and static pools:
+
+- 192.168.1.100 - 192.168.1.199 - this is the dynamic pool. When new devices appear in the network,
+ they are assigned dynamic addresses from this pool.
+- The reservations are done outside of this dynamic range (depending on the addressing preference,
+ either 192.168.1.1-192.168.1.99 or 192.168.1.200-192.168.1.254).
+
+To deploy this setup, perform the following steps:
+
+1. Install the CA and the DHCPv4 server on host-1, and copy the configuration files to their typical locations.
+ They are usually in ``/etc/kea`` on Linux and ``/usr/local/etc/kea`` on FreeBSD, and the files are typically called
+ ``kea-ctrl-agent.conf`` and ``kea-dhcp4.conf``. Please consult the startup scripts for any specific system.
+
+2. Alter the following to match the local setup:
+
+ - The interface name that Kea should listen on (``interfaces`` in ``interfaces-config``).
+
+ - The interface name that is used to access the subnet (``interface`` in ``subnet4``).
+
+ - The addressing, if using something other than 192.168.1.0/24. Make sure the CA port
+ configuration (``http-host`` and ``http-port`` in ``kea-ca.conf``) matches the DHCPv4 server
+ configuration (``url`` in ``hook-libraries/parameters/high-availability/peers`` in ``kea-dhcp4.conf``).
+
+ - The router option, to match the actual network.
+
+ - The DNS option, to match the actual network.
+
+ - The path to the hook libraries. This is a very OS-specific parameter; the library names are
+ generally the same everywhere, but the path varies. See :ref:`hooks-libraries-introduction` for details.
+
+3. If using a firewall, make sure host-1 can reach host-2. An easy way to ensure that is to
+ try to retrieve host-2's config from host-1:
+
+ ``curl -X POST -H "Content-Type: application/json" -d '{ "command": "config-get", "service": [ "dhcp4" ] }' http://192.168.1.3:8000/``
+
+ The DHCPv4 running configuration should be returned, in JSON format.
+
+4. Verify that communication between the hosts works in the opposite direction as well
+ (host-2 can connect to host-1), by repeating step 3 from host-2 using host-1's IP address and port.
+
+5. Install the CA and the DHCPv4 server on host-2, as in steps 1 and 2. The config file for the
+ standby server is very similar to the one on the primary server, other than the definition of
+ the ``this-server-name`` field (and possibly the interface names).
+
+Possible Extensions
+~~~~~~~~~~~~~~~~~~~
+
+This sample configuration is basic but functional. Once it is set up and running, administrators
+may wish to consider the following changes:
+
+- If there is a local DNS server, DNS updates can be configured via Kea. This requires running a DHCP-DDNS update server
+ (``kea-dhcp-ddns``). See :ref:`dhcp-ddns-overview` for details.
+
+- To run Stateful DHCP for IPv6, a ``kea-dhcp6`` server is necessary. Its configuration
+ is very similar to ``kea-dhcp4``, but there are some notable differences: the default gateway is not
+ configured via the DHCPv6 protocol, but via router advertisements sent by the local router. Also, the
+ DHCPv6 concept of prefix delegation does not exist in DHCPv4. See :ref:`dhcp6`
+ for details.
+
+- To expand the local network, adding a MySQL or PostgreSQL database is a popular solution.
+ Users can choose to store leases, host reservations, and even most of the configuration
+ in a database. See :ref:`admin` and the ``lease-database``, ``hosts-database``, and
+ ``config-control`` parameters in :ref:`dhcp4`.
+
+- To provide more insight into how the DHCP server operates, Kea's RESTful API can query
+ for many runtime statistics or even change the configuration during runtime. Users may also
+ consider deploying Stork, which is a rapidly developing dashboard for Kea. See :ref:`stork`
+ for more information.
+
+- All Kea users should read :ref:`security`: to learn about various trade-offs between
+ convenience and security in Kea.
diff --git a/doc/examples/template-power-user-home/kea-ca-1.conf b/doc/examples/template-power-user-home/kea-ca-1.conf
new file mode 100644
index 0000000..9139008
--- /dev/null
+++ b/doc/examples/template-power-user-home/kea-ca-1.conf
@@ -0,0 +1,66 @@
+// This is an example of a configuration for Control-Agent (CA) listening
+// for incoming HTTP traffic. This is necessary for handling API commands,
+// in particular lease update commands needed for HA setup.
+{
+ "Control-agent":
+ {
+ // We need to specify where the agent should listen to incoming HTTP
+ // queries.
+ "http-host": "192.168.1.2",
+
+ // This specifies the port CA will listen on.
+ "http-port": 8000,
+
+ "control-sockets":
+ {
+ // This is how the Agent can communicate with the DHCPv4 server.
+ "dhcp4":
+ {
+ "comment": "socket to DHCPv4 server",
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Location of the DHCPv6 command channel socket.
+ "dhcp6":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea6-ctrl-socket"
+ },
+
+ // Location of the D2 command channel socket.
+ "d2":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea-ddns-ctrl-socket",
+ "user-context": { "in-use": false }
+ }
+ },
+
+ // Similar to other Kea components, CA also uses logging.
+ "loggers": [
+ {
+ "name": "kea-ctrl-agent",
+ "output-options": [
+ {
+ "output": "/var/log/kea-ctrl-agent.log",
+
+ // Several additional parameters are possible in addition
+ // to the typical output. Flush determines whether logger
+ // flushes output to a file. Maxsize determines maximum
+ // filesize before the file is rotated. maxver
+ // specifies the maximum number of rotated files being
+ // kept.
+ "flush": true,
+ "maxsize": 204800,
+ "maxver": 4,
+ // We use pattern to specify custom log message layout
+ "pattern": "%d{%y.%m.%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0 // debug level only applies when severity is set to DEBUG.
+ }
+ ]
+ }
+}
diff --git a/doc/examples/template-power-user-home/kea-ca-2.conf b/doc/examples/template-power-user-home/kea-ca-2.conf
new file mode 100644
index 0000000..f36c850
--- /dev/null
+++ b/doc/examples/template-power-user-home/kea-ca-2.conf
@@ -0,0 +1,66 @@
+// This is an example of a configuration for Control-Agent (CA) listening
+// for incoming HTTP traffic. This is necessary for handling API commands,
+// in particular lease update commands needed for HA setup.
+{
+ "Control-agent":
+ {
+ // We need to specify where the agent should listen to incoming HTTP
+ // queries.
+ "http-host": "192.168.1.3",
+
+ // This specifies the port CA will listen on.
+ "http-port": 8000,
+
+ "control-sockets":
+ {
+ // This is how the Agent can communicate with the DHCPv4 server.
+ "dhcp4":
+ {
+ "comment": "socket to DHCPv4 server",
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Location of the DHCPv6 command channel socket.
+ "dhcp6":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea6-ctrl-socket"
+ },
+
+ // Location of the D2 command channel socket.
+ "d2":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea-ddns-ctrl-socket",
+ "user-context": { "in-use": false }
+ }
+ },
+
+ // Similar to other Kea components, CA also uses logging.
+ "loggers": [
+ {
+ "name": "kea-ctrl-agent",
+ "output-options": [
+ {
+ "output": "/var/log/kea-ctrl-agent.log",
+
+ // Several additional parameters are possible in addition
+ // to the typical output. Flush determines whether logger
+ // flushes output to a file. Maxsize determines maximum
+ // filesize before the file is rotated. maxver
+ // specifies the maximum number of rotated files being
+ // kept.
+ "flush": true,
+ "maxsize": 204800,
+ "maxver": 4,
+ // We use pattern to specify custom log message layout
+ "pattern": "%d{%y.%m.%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0 // debug level only applies when severity is set to DEBUG.
+ }
+ ]
+ }
+}
diff --git a/doc/examples/template-power-user-home/kea-dhcp4-1.conf b/doc/examples/template-power-user-home/kea-dhcp4-1.conf
new file mode 100644
index 0000000..d4a9d70
--- /dev/null
+++ b/doc/examples/template-power-user-home/kea-dhcp4-1.conf
@@ -0,0 +1,227 @@
+// This is an example configuration of the Kea DHCPv4 server 1:
+//
+// - uses High Availability hook library and Lease Commands hook library
+// to enable High Availability function for the DHCP server. This config
+// file is for the primary (the active) server.
+// - uses memfile, which stores lease data in a local CSV file
+// - it assumes a single /24 addressing over a link that is directly reachable
+// (no DHCP relays)
+// - there is a handful of IP reservations
+//
+// It is expected to run with a standby (the passive) server, which has a very similar
+// configuration. The only difference is that "this-server-name" must be set to "server2" on the
+// other server. Also, the interface configuration depends on the network settings of the
+// particular machine.
+
+{
+
+"Dhcp4": {
+
+ // Add names of your network interfaces to listen on.
+ "interfaces-config": {
+ // The DHCPv4 server listens on this interface. When changing this to
+ // the actual name of your interface, make sure to also update the
+ // interface parameter in the subnet definition below.
+ "interfaces": [ "enp0s8" ]
+ },
+
+ // Control socket is required for communication between the Control
+ // Agent and the DHCP server. High Availability requires Control Agent
+ // to be running because lease updates are sent over the RESTful
+ // API between the HA peers.
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Use Memfile lease database backend to store leases in a CSV file.
+ // Depending on how Kea was compiled, it may also support SQL databases
+ // (MySQL and/or PostgreSQL). Those database backends require more
+ // parameters, like name, host and possibly user and password.
+ // There are dedicated examples for each backend. See Section 7.2.2 "Lease
+ // Storage" for details.
+ "lease-database": {
+ // Memfile is the simplest and easiest backend to use. It's an in-memory
+ // database with data being written to a CSV file. It is very similar to
+ // what ISC DHCP does.
+ "type": "memfile"
+ },
+
+ // Let's configure some global parameters. The home network is not very dynamic
+ // and there's no shortage of addresses, so no need to recycle aggressively.
+ "valid-lifetime": 43200, // leases will be valid for 12h
+ "renew-timer": 21600, // clients should renew every 6h
+ "rebind-timer": 32400, // clients should start looking for other servers after 9h
+
+ // Kea will clean up its database of expired leases once per hour. However, it
+ // will keep the leases in expired state for 2 days. This greatly increases the
+ // chances for returning devices to get the same address again. To guarantee that,
+ // use host reservation.
+ // If both "flush-reclaimed-timer-wait-time" and "hold-reclaimed-time" are
+ // not 0, when the client sends a release message the lease is expired
+ // instead of being deleted from lease storage.
+ "expired-leases-processing": {
+ "reclaim-timer-wait-time": 3600,
+ "hold-reclaimed-time": 172800,
+ "max-reclaim-leases": 0,
+ "max-reclaim-time": 0
+ },
+
+ // HA requires two hook libraries to be loaded: libdhcp_lease_cmds.so and
+ // libdhcp_ha.so. The former handles incoming lease updates from the HA peers.
+ // The latter implements high availability feature for Kea. Note the library name
+ // should be the same, but the path is OS specific.
+ "hooks-libraries": [
+ // The lease_cmds library must be loaded because HA makes use of it to
+ // deliver lease updates to the server as well as synchronize the
+ // lease database after failure.
+ {
+ "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
+ },
+
+ {
+ // The HA hook library should be loaded.
+ "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ // Each server should have the same HA configuration, except for the
+ // "this-server-name" parameter.
+ "high-availability": [ {
+ // This parameter points to this server instance. The respective
+ // HA peers must have this parameter set to their own names.
+ "this-server-name": "server1",
+ // The HA mode is set to hot-standby. In this mode, the active server handles
+ // all the traffic. The standby takes over if the primary becomes unavailable.
+ "mode": "hot-standby",
+ // Heartbeat is to be sent every 10 seconds if no other control
+ // commands are transmitted.
+ "heartbeat-delay": 10000,
+ // Maximum time for partner's response to a heartbeat, after which
+ // failure detection is started. This is specified in milliseconds.
+ // If we don't hear from the partner in 60 seconds, it's time to
+ // start worrying.
+ "max-response-delay": 60000,
+ // The following parameters control how the server detects the
+ // partner's failure. The ACK delay sets the threshold for the
+ // 'secs' field of the received discovers. This is specified in
+ // milliseconds.
+ "max-ack-delay": 5000,
+ // This specifies the number of clients which send messages to
+ // the partner but appear to not receive any response.
+ "max-unacked-clients": 5,
+ // This specifies the maximum timeout (in milliseconds) for the server
+ // to complete sync. If you have a large deployment (high tens or
+ // hundreds of thousands of clients), you may need to increase it
+ // further. The default value is 60000ms (60 seconds).
+ "sync-timeout": 60000,
+ "peers": [
+ // This is the configuration of this server instance.
+ {
+ "name": "server1",
+ // This specifies the URL of this server instance. The
+ // Control Agent must run along with this DHCPv4 server
+ // instance and the "http-host" and "http-port" must be
+ // set to the corresponding values.
+ "url": "http://192.168.1.2:8000/",
+ // This server is primary. The other one must be
+ // secondary.
+ "role": "primary"
+ },
+ // This is the configuration of the secondary server.
+ {
+ "name": "server2",
+ // Specifies the URL on which the partner's control
+ // channel can be reached. The Control Agent is required
+ // to run on the partner's machine with "http-host" and
+ // "http-port" values set to the corresponding values.
+ "url": "http://192.168.1.3:8000/",
+ // The other server is secondary. This one must be
+ // primary.
+ "role": "standby"
+ }
+ ]
+ } ]
+ }
+ }
+ ],
+
+ // This example contains a single subnet declaration.
+ "subnet4": [
+ {
+ // Subnet prefix.
+ "subnet": "192.168.1.0/24",
+
+ // There are no relays in this network, so we need to tell Kea that this subnet
+ // is reachable directly via the specified interface.
+ "interface": "enp0s8",
+
+ // Specify a dynamic address pool.
+ "pools": [
+ {
+ "pool": "192.168.1.100-192.168.1.199"
+ }
+ ],
+
+ // These are options that are subnet specific. In most cases, you need to define at
+ // least routers option, as without this option your clients will not be able to reach
+ // their default gateway and will not have Internet connectivity. If you have many
+ // subnets and they share the same options (e.g. DNS servers typically is the same
+ // everywhere), you may define options at the global scope, so you don't repeat them
+ // for every network.
+ "option-data": [
+ {
+ // For each IPv4 subnet you typically need to specify at least one router.
+ "name": "routers",
+ "data": "192.168.1.1"
+ },
+ {
+ // Using cloudflare or Quad9 is a reasonable option. Change this
+ // to your own DNS servers is you have them. Another popular
+ // choice is 8.8.8.8, owned by Google. Using third party DNS
+ // service raises some privacy concerns.
+ "name": "domain-name-servers",
+ "data": "1.1.1.1,9.9.9.9"
+ }
+ ],
+
+ // Some devices should get a static address. Since the .100 - .199 range is dynamic,
+ // let's use the lower address space for this. There are many ways how reservation
+ // can be defined, but using MAC address (hw-address) is by far the most popular one.
+ // You can use client-id, duid and even custom defined flex-id that may use whatever
+ // parts of the packet you want to use as identifiers. Also, there are many more things
+ // you can specify in addition to just an IP address: extra options, next-server, hostname,
+ // assign device to client classes etc. See the Kea ARM, Section 8.3 for details.
+ // The reservations are subnet specific.
+ "reservations": [
+ {
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "ip-address": "192.168.1.10"
+ },
+ {
+ "client-id": "01:11:22:33:44:55:66",
+ "ip-address": "192.168.1.11"
+ }
+ ]
+ }
+ ],
+
+ // Logging configuration starts here.
+ "loggers": [
+ {
+ // This section affects kea-dhcp4, which is the base logger for DHCPv4 component. It tells
+ // DHCPv4 server to write all log messages (on severity INFO or higher) to a file. The file
+ // will be rotated once it grows to 2MB and up to 4 files will be kept. The debuglevel
+ // (range 0 to 99) is used only when logging on DEBUG level.
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "/var/log/kea-dhcp4.log",
+ "maxsize": 2048000,
+ "maxver": 4
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0
+ }
+ ]
+}
+}
diff --git a/doc/examples/template-power-user-home/kea-dhcp4-2.conf b/doc/examples/template-power-user-home/kea-dhcp4-2.conf
new file mode 100644
index 0000000..f75a997
--- /dev/null
+++ b/doc/examples/template-power-user-home/kea-dhcp4-2.conf
@@ -0,0 +1,227 @@
+// This is an example configuration of the Kea DHCPv4 server 2:
+//
+// - uses High Availability hook library and Lease Commands hook library
+// to enable High Availability function for the DHCP server. This config
+// file is for the primary (the active) server.
+// - uses memfile, which stores lease data in a local CSV file
+// - it assumes a single /24 addressing over a link that is directly reachable
+// (no DHCP relays)
+// - there is a handful of IP reservations
+//
+// It is expected to run with a primary (the active) server, which has a very similar
+// configuration. The only difference is that "this-server-name" must be set to "server2" on the
+// other server. Also, the interface configuration depends on the network settings of the
+// particular machine.
+
+{
+
+"Dhcp4": {
+
+ // Add names of your network interfaces to listen on.
+ "interfaces-config": {
+ // The DHCPv4 server listens on this interface. When changing this to
+ // the actual name of your interface, make sure to also update the
+ // interface parameter in the subnet definition below.
+ "interfaces": [ "enp0s8" ]
+ },
+
+ // Control socket is required for communication between the Control
+ // Agent and the DHCP server. High Availability requires Control Agent
+ // to be running because lease updates are sent over the RESTful
+ // API between the HA peers.
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ },
+
+ // Use Memfile lease database backend to store leases in a CSV file.
+ // Depending on how Kea was compiled, it may also support SQL databases
+ // (MySQL and/or PostgreSQL). Those database backends require more
+ // parameters, like name, host and possibly user and password.
+ // There are dedicated examples for each backend. See Section 7.2.2 "Lease
+ // Storage" for details.
+ "lease-database": {
+ // Memfile is the simplest and easiest backend to use. It's an in-memory
+ // database with data being written to a CSV file. It is very similar to
+ // what ISC DHCP does.
+ "type": "memfile"
+ },
+
+ // Let's configure some global parameters. The home network is not very dynamic
+ // and there's no shortage of addresses, so no need to recycle aggressively.
+ "valid-lifetime": 43200, // leases will be valid for 12h
+ "renew-timer": 21600, // clients should renew every 6h
+ "rebind-timer": 32400, // clients should start looking for other servers after 9h
+
+ // Kea will clean up its database of expired leases once per hour. However, it
+ // will keep the leases in expired state for 2 days. This greatly increases the
+ // chances for returning devices to get the same address again. To guarantee that,
+ // use host reservation.
+ // If both "flush-reclaimed-timer-wait-time" and "hold-reclaimed-time" are
+ // not 0, when the client sends a release message the lease is expired
+ // instead of being deleted from lease storage.
+ "expired-leases-processing": {
+ "reclaim-timer-wait-time": 3600,
+ "hold-reclaimed-time": 172800,
+ "max-reclaim-leases": 0,
+ "max-reclaim-time": 0
+ },
+
+ // HA requires two hook libraries to be loaded: libdhcp_lease_cmds.so and
+ // libdhcp_ha.so. The former handles incoming lease updates from the HA peers.
+ // The latter implements high availability feature for Kea. Note the library name
+ // should be the same, but the path is OS specific.
+ "hooks-libraries": [
+ // The lease_cmds library must be loaded because HA makes use of it to
+ // deliver lease updates to the server as well as synchronize the
+ // lease database after failure.
+ {
+ "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
+ },
+
+ {
+ // The HA hook library should be loaded.
+ "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ // Each server should have the same HA configuration, except for the
+ // "this-server-name" parameter.
+ "high-availability": [ {
+ // This parameter points to this server instance. The respective
+ // HA peers must have this parameter set to their own names.
+ "this-server-name": "server2",
+ // The HA mode is set to hot-standby. In this mode, the active server handles
+ // all the traffic. The standby takes over if the primary becomes unavailable.
+ "mode": "hot-standby",
+ // Heartbeat is to be sent every 10 seconds if no other control
+ // commands are transmitted.
+ "heartbeat-delay": 10000,
+ // Maximum time for partner's response to a heartbeat, after which
+ // failure detection is started. This is specified in milliseconds.
+ // If we don't hear from the partner in 60 seconds, it's time to
+ // start worrying.
+ "max-response-delay": 60000,
+ // The following parameters control how the server detects the
+ // partner's failure. The ACK delay sets the threshold for the
+ // 'secs' field of the received discovers. This is specified in
+ // milliseconds.
+ "max-ack-delay": 5000,
+ // This specifies the number of clients which send messages to
+ // the partner but appear to not receive any response.
+ "max-unacked-clients": 5,
+ // This specifies the maximum timeout (in milliseconds) for the server
+ // to complete sync. If you have a large deployment (high tens or
+ // hundreds of thousands of clients), you may need to increase it
+ // further. The default value is 60000ms (60 seconds).
+ "sync-timeout": 60000,
+ "peers": [
+ // This is the configuration of the primary server.
+ {
+ "name": "server1",
+ // Specifies the URL on which the partner's control
+ // channel can be reached. The Control Agent is required
+ // to run on the partner's machine with "http-host" and
+ // "http-port" values set to the corresponding values.
+ "url": "http://192.168.1.2:8000/",
+ // The other server is primary. This one must be
+ // secondary.
+ "role": "primary"
+ },
+ // This is the configuration of this server instance.
+ {
+ "name": "server2",
+ // This specifies the URL of this server instance. The
+ // Control Agent must run along with this DHCPv4 server
+ // instance and the "http-host" and "http-port" must be
+ // set to the corresponding values.
+ "url": "http://192.168.1.3:8000/",
+ // This server is secondary. The other one must be
+ // primary.
+ "role": "standby"
+ }
+ ]
+ } ]
+ }
+ }
+ ],
+
+ // This example contains a single subnet declaration.
+ "subnet4": [
+ {
+ // Subnet prefix.
+ "subnet": "192.168.1.0/24",
+
+ // There are no relays in this network, so we need to tell Kea that this subnet
+ // is reachable directly via the specified interface.
+ "interface": "enp0s8",
+
+ // Specify a dynamic address pool.
+ "pools": [
+ {
+ "pool": "192.168.1.100-192.168.1.199"
+ }
+ ],
+
+ // These are options that are subnet specific. In most cases, you need to define at
+ // least routers option, as without this option your clients will not be able to reach
+ // their default gateway and will not have Internet connectivity. If you have many
+ // subnets and they share the same options (e.g. DNS servers typically is the same
+ // everywhere), you may define options at the global scope, so you don't repeat them
+ // for every network.
+ "option-data": [
+ {
+ // For each IPv4 subnet you typically need to specify at least one router.
+ "name": "routers",
+ "data": "192.168.1.1"
+ },
+ {
+ // Using cloudflare or Quad9 is a reasonable option. Change this
+ // to your own DNS servers is you have them. Another popular
+ // choice is 8.8.8.8, owned by Google. Using third party DNS
+ // service raises some privacy concerns.
+ "name": "domain-name-servers",
+ "data": "1.1.1.1,9.9.9.9"
+ }
+ ],
+
+ // Some devices should get a static address. Since the .100 - .199 range is dynamic,
+ // let's use the lower address space for this. There are many ways how reservation
+ // can be defined, but using MAC address (hw-address) is by far the most popular one.
+ // You can use client-id, duid and even custom defined flex-id that may use whatever
+ // parts of the packet you want to use as identifiers. Also, there are many more things
+ // you can specify in addition to just an IP address: extra options, next-server, hostname,
+ // assign device to client classes etc. See the Kea ARM, Section 8.3 for details.
+ // The reservations are subnet specific.
+ "reservations": [
+ {
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "ip-address": "192.168.1.10"
+ },
+ {
+ "client-id": "01:11:22:33:44:55:66",
+ "ip-address": "192.168.1.11"
+ }
+ ]
+ }
+ ],
+
+ // Logging configuration starts here.
+ "loggers": [
+ {
+ // This section affects kea-dhcp4, which is the base logger for DHCPv4 component. It tells
+ // DHCPv4 server to write all log messages (on severity INFO or higher) to a file. The file
+ // will be rotated once it grows to 2MB and up to 4 files will be kept. The debuglevel
+ // (range 0 to 99) is used only when logging on DEBUG level.
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "/var/log/kea-dhcp4.log",
+ "maxsize": 2048000,
+ "maxver": 4
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0
+ }
+ ]
+}
+}
diff --git a/doc/images/kea-logo-100x70.png b/doc/images/kea-logo-100x70.png
new file mode 100644
index 0000000..1de8411
--- /dev/null
+++ b/doc/images/kea-logo-100x70.png
Binary files differ
diff --git a/doc/sphinx/Makefile.am b/doc/sphinx/Makefile.am
new file mode 100644
index 0000000..8beac81
--- /dev/null
+++ b/doc/sphinx/Makefile.am
@@ -0,0 +1,257 @@
+EXTRA_DIST =
+sphinxbuilddir = $(builddir)/_build
+abs_sphinxbuilddir = $(abs_builddir)/_build
+
+if GENERATE_DOCS
+
+sphinxopts =
+sphinxopts += -v
+sphinxopts += -E
+sphinxopts += -a
+sphinxopts += -W
+sphinxopts += -c "${abs_srcdir}"
+
+static_sources =
+static_sources += static/kea-imageonly-100bw.png
+static_sources += static/kea-logo-100x70.png
+static_sources += static/kea-logo-200.png
+static_sources += static/kea.css
+
+# ARM
+rst_arm_sources =
+rst_arm_sources += index.rst
+rst_arm_sources += manpages.rst
+rst_arm_sources += umls.rst
+include arm/rst_arm_sources.mk
+
+EXTRA_DIST += arm/rst_arm_sources.mk
+
+main_sources = $(rst_arm_sources) conf.py $(static_sources)
+
+# mans
+rst_man_sources =
+include man/rst_man_sources.mk
+
+EXTRA_DIST += man/rst_man_sources.mk
+
+rst_arm_sources += grammar/grammar.rst
+rst_arm_sources += grammar/grammar-ca-parser.rst
+rst_arm_sources += grammar/grammar-d2-parser.rst
+rst_arm_sources += grammar/grammar-dhcp4-parser.rst
+rst_arm_sources += grammar/grammar-dhcp6-parser.rst
+rst_arm_sources += grammar/grammar-netconf-parser.rst
+
+man8s =
+include man/man8s.mk
+
+EXTRA_DIST += man/man8s.mk
+
+man_sources = $(rst_man_sources) conf.py
+
+EXTRA_DIST += $(main_sources) $(man_sources) mes2doc.py api2doc.py $(man8s)
+
+# list of messages files that are used to generate kea-messages.rst and then kea-messages.pdf
+mes_files =
+include $(srcdir)/mes_files.mk
+
+EXTRA_DIST += mes_files.mk
+
+# list of api files that are used to generate api.rst
+api_files =
+include $(top_srcdir)/src/share/api/api_files.mk
+
+if HAVE_PDFLATEX
+all: html mans pdf text
+else
+all: html mans text
+endif
+
+# build the list of message files
+mes-files.txt: mes_files.mk
+ @sed 's;mes_files .*)/;;' $< > $@
+
+# this rule is only used for development purposes and is not used in official
+# build process as kea-messages.rst is always generated via sphinx's conf.py
+$(srcdir)/kea-messages.rst: $(mes_files) mes2doc.py
+ $(PYTHON) $(srcdir)/mes2doc.py -o $@ $(mes_files)
+
+# build the list of api files
+api-files.txt: $(top_srcdir)/src/share/api/api_files.mk
+ @sed 's;api_files .*)/;;' $< > $@
+
+# some tools do not use this makefile but still need generate files.
+EXTRA_DIST += mes-files.txt api-files.txt
+
+# this rule is only used for development purposes and is not used in official
+# build process as api.rst is always generated via sphinx's conf.py
+$(srcdir)/api.rst: $(api_files) api-files.txt api2doc.py
+ $(PYTHON) $(srcdir)/api2doc.py -o $@ $(api_files)
+
+$(srcdir)/arm/platforms.rst:
+ rm -f $(srcdir)/arm/platforms.rst
+ cp $(srcdir)/../../platforms.rst $(srcdir)/arm/platforms.rst
+
+# UML files
+
+umls =
+umls += uml/appendRequestedOptions.uml
+umls += uml/appendRequestedVendorOptions.uml
+umls += uml/assign-lease4.uml
+umls += uml/buildCfgOptionList.uml
+umls += uml/currentHost4.uml
+umls += uml/lease-states.uml
+umls += uml/main-loop.uml
+umls += uml/packet4.uml
+umls += uml/radius.uml
+umls += uml/recognizing-same-client.uml
+umls += uml/request4-lease.uml
+umls += uml/request4.uml
+umls += uml/requestLease4.uml
+umls += uml/select4.uml
+umls += uml/tkey.uml
+umls += uml/update.uml
+
+pngs =
+pngs += uml/appendRequestedOptions.png
+pngs += uml/appendRequestedVendorOptions.png
+pngs += uml/assign-lease4.png
+pngs += uml/buildCfgOptionList.png
+pngs += uml/currentHost4.png
+pngs += uml/lease-states.png
+pngs += uml/main-loop.png
+pngs += uml/packet4.png
+pngs += uml/radius.png
+pngs += uml/recognizing-same-client.png
+pngs += uml/request4-lease.png
+pngs += uml/request4.png
+pngs += uml/requestLease4.png
+pngs += uml/select4.png
+pngs += uml/tkey.png
+pngs += uml/update.png
+
+svgs =
+svgs += uml/appendRequestedOptions.svg
+svgs += uml/appendRequestedVendorOptions.svg
+svgs += uml/assign-lease4.svg
+svgs += uml/buildCfgOptionList.svg
+svgs += uml/currentHost4.svg
+svgs += uml/lease-states.svg
+svgs += uml/main-loop.svg
+svgs += uml/packet4.svg
+svgs += uml/radius.svg
+svgs += uml/recognizing-same-client.svg
+svgs += uml/request4-lease.svg
+svgs += uml/request4.svg
+svgs += uml/requestLease4.svg
+svgs += uml/select4.svg
+svgs += uml/tkey.svg
+svgs += uml/update.svg
+
+txts =
+txts += uml/option-data-priority.atxt
+txts += uml/priority-of-lease-lifetimes-and-dhcpv4-fields.atxt
+txts += uml/tkey.atxt
+txts += uml/update.atxt
+
+if HAVE_PLANTUML
+uml: uml-to-png uml-to-svg format-svgs uml-to-txt
+
+uml-to-png: $(umls)
+ @ @PLANTUML@ -tpng $^
+
+uml-to-svg: $(umls)
+ @ @PLANTUML@ -tsvg $^
+
+# Formatting puts every tag on a separate line, which makes it easier for diffing.
+format-svgs: $(svgs)
+if HAVE_XMLLINT
+ @ for svg in $(svgs); do xmllint --format $$svg > tmp; mv tmp $$svg; done
+else
+ @ printf 'WARNING: xmllint not found. SVGs not formatted.\n'
+endif
+
+# Only sequence diagrams support ASCII art.
+uml-to-txt: uml/tkey.uml uml/update.uml
+ @ @PLANTUML@ -ttxt $^
+ @ for txt in $(txts); do sed 's/ *$$//g' $$txt > tmp; mv tmp $$txt; done
+endif
+
+EXTRA_DIST += $(umls)
+EXTRA_DIST += $(pngs)
+EXTRA_DIST += $(svgs)
+EXTRA_DIST += $(txts)
+
+PDFLATEX_AND_OPTS=$(PDFLATEX) -interaction nonstopmode
+
+pdf: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst
+ $(SPHINXBUILD) -M latex $(srcdir) $(sphinxbuilddir) $(sphinxopts)
+ cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-arm.tex
+ cd $(abs_sphinxbuilddir)/latex && makeindex -s python.ist kea-arm.idx
+ cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-arm.tex
+ cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-messages.tex
+ cd $(abs_sphinxbuilddir)/latex && makeindex -s python.ist kea-messages.idx
+ cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-messages.tex
+
+html: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst
+ $(SPHINXBUILD) -M html $(srcdir) $(sphinxbuilddir) $(sphinxopts)
+
+# This target is not used anywhere, but people who prefer single page docs
+# can do make -C doc/sphinx singlehtml and then enjoy their docs being
+# generated in doc/sphinx/_build/singlehtml
+singlehtml: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst
+ $(SPHINXBUILD) -M singlehtml $(srcdir) $(sphinxbuilddir) $(sphinxopts)
+
+text: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst
+ $(SPHINXBUILD) -M text $(srcdir) $(sphinxbuilddir) $(sphinxopts)
+
+$(man8s): mans
+
+mans: $(man_sources) api-files.txt mes-files.txt
+ $(SPHINXBUILD) -M man $(srcdir) $(sphinxbuilddir) $(sphinxopts)
+
+# Updates sphinx dependencies to the latest versions available on the current
+# host and freezes them in ./src/requirements.txt. Requires pip-compile:
+# pip install pip-tools
+# Run this when dependencies are added, changed or removed.
+update-python-dependencies: ./src/requirements.in
+ rm ./src/requirements.txt
+ pip-compile -r ./src/requirements.in
+
+clean-local:
+ rm -rf $(sphinxbuilddir)
+ rm -f $(srcdir)/mes-files.txt $(srcdir)/api-files.txt
+ rm -f $(srcdir)/kea-messages.rst $(srcdir)/api.rst
+ rm -f $(srcdir)/arm/platforms.rst
+
+.PHONY: all pdf html mans update-python-dependencies uml uml-to-png uml-to-svg format-svgs uml-to-txt
+
+endif
+
+# install and uninstall can occur with GENERATE_DOCS and without it
+# so we want to install all when GENERATE_DOCS is and
+# just mans when GENERATE_DOCS is not used, and when man files exists (e.g release tarball)
+install-data-local:
+ mkdir -p $(DESTDIR)$(docdir)
+if GENERATE_DOCS
+ cp -r $(sphinxbuilddir)/html $(DESTDIR)$(docdir)
+if HAVE_PDFLATEX
+ ${INSTALL_DATA} $(sphinxbuilddir)/latex/kea-arm.pdf $(DESTDIR)$(docdir)
+ ${INSTALL_DATA} $(sphinxbuilddir)/latex/kea-messages.pdf $(DESTDIR)$(docdir)
+endif
+ ${MKDIR_P} ${DESTDIR}${mandir}/man8
+ ${INSTALL_DATA} $(man8s) ${DESTDIR}${mandir}/man8/
+else
+if INSTALL_MANS
+ ${MKDIR_P} ${DESTDIR}${mandir}/man8
+ ${INSTALL_DATA} $(sphinxbuilddir)/man/*.8 ${DESTDIR}${mandir}/man8/
+endif
+endif
+
+uninstall-local:
+ rm -rf $(DESTDIR)$(docdir)
+
+# There are sometimes conflicts when more then one sphinx-build is run at a time.
+# This target blocks running anything in parallel in this Makefile,
+# all is run serially.
+
+.NOTPARALLEL:
diff --git a/doc/sphinx/Makefile.in b/doc/sphinx/Makefile.in
new file mode 100644
index 0000000..88a7f33
--- /dev/null
+++ b/doc/sphinx/Makefile.in
@@ -0,0 +1,1015 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+
+# some tools do not use this makefile but still need generate files.
+@GENERATE_DOCS_TRUE@am__append_1 = arm/rst_arm_sources.mk \
+@GENERATE_DOCS_TRUE@ man/rst_man_sources.mk man/man8s.mk \
+@GENERATE_DOCS_TRUE@ $(main_sources) $(man_sources) mes2doc.py \
+@GENERATE_DOCS_TRUE@ api2doc.py $(man8s) mes_files.mk \
+@GENERATE_DOCS_TRUE@ mes-files.txt api-files.txt $(umls) \
+@GENERATE_DOCS_TRUE@ $(pngs) $(svgs) $(txts)
+subdir = doc/sphinx
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \
+ $(top_srcdir)/m4macros/ax_cpp14.m4 \
+ $(top_srcdir)/m4macros/ax_cpp20.m4 \
+ $(top_srcdir)/m4macros/ax_crypto.m4 \
+ $(top_srcdir)/m4macros/ax_find_library.m4 \
+ $(top_srcdir)/m4macros/ax_gssapi.m4 \
+ $(top_srcdir)/m4macros/ax_gtest.m4 \
+ $(top_srcdir)/m4macros/ax_isc_rpath.m4 \
+ $(top_srcdir)/m4macros/ax_netconf.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(srcdir)/arm/rst_arm_sources.mk $(srcdir)/man/man8s.mk \
+ $(srcdir)/man/rst_man_sources.mk $(srcdir)/mes_files.mk \
+ $(top_srcdir)/src/share/api/api_files.mk
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_GSSAPI_CONFIGURE_FLAG = @DISTCHECK_GSSAPI_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG = @DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG@
+DISTCHECK_LIBYANG_CONFIGURE_FLAG = @DISTCHECK_LIBYANG_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_MYSQL_CONFIGURE_FLAG = @DISTCHECK_MYSQL_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PGSQL_CONFIGURE_FLAG = @DISTCHECK_PGSQL_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@
+DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG = @DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG@
+DISTCHECK_SYSREPO_CONFIGURE_FLAG = @DISTCHECK_SYSREPO_CONFIGURE_FLAG@
+DLLTOOL = @DLLTOOL@
+DPKG = @DPKG@
+DPKGQUERY = @DPKGQUERY@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+GENHTML = @GENHTML@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GTEST_CONFIG = @GTEST_CONFIG@
+GTEST_INCLUDES = @GTEST_INCLUDES@
+GTEST_LDADD = @GTEST_LDADD@
+GTEST_LDFLAGS = @GTEST_LDFLAGS@
+GTEST_SOURCE = @GTEST_SOURCE@
+HAVE_NETCONF = @HAVE_NETCONF@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KEA_CXXFLAGS = @KEA_CXXFLAGS@
+KEA_SRCID = @KEA_SRCID@
+KRB5_CONFIG = @KRB5_CONFIG@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBYANGCPP_CPPFLAGS = @LIBYANGCPP_CPPFLAGS@
+LIBYANGCPP_INCLUDEDIR = @LIBYANGCPP_INCLUDEDIR@
+LIBYANGCPP_LIBS = @LIBYANGCPP_LIBS@
+LIBYANGCPP_PREFIX = @LIBYANGCPP_PREFIX@
+LIBYANGCPP_VERSION = @LIBYANGCPP_VERSION@
+LIBYANG_CPPFLAGS = @LIBYANG_CPPFLAGS@
+LIBYANG_INCLUDEDIR = @LIBYANG_INCLUDEDIR@
+LIBYANG_LIBS = @LIBYANG_LIBS@
+LIBYANG_PREFIX = @LIBYANG_PREFIX@
+LIBYANG_VERSION = @LIBYANG_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@
+LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LIBS = @MYSQL_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PDFLATEX = @PDFLATEX@
+PERL = @PERL@
+PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PKGPYTHONDIR = @PKGPYTHONDIR@
+PKG_CONFIG = @PKG_CONFIG@
+PLANTUML = @PLANTUML@
+PREMIUM_DIR = @PREMIUM_DIR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SEP = @SEP@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@
+SR_PLUGINS_PATH = @SR_PLUGINS_PATH@
+SR_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_CPPFLAGS = @SYSREPOCPP_CPPFLAGS@
+SYSREPOCPP_INCLUDEDIR = @SYSREPOCPP_INCLUDEDIR@
+SYSREPOCPP_LIBS = @SYSREPOCPP_LIBS@
+SYSREPOCPP_PREFIX = @SYSREPOCPP_PREFIX@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+SYSREPO_PREFIX = @SYSREPO_PREFIX@
+SYSREPO_VERSION = @SYSREPO_VERSION@
+USE_LCOV = @USE_LCOV@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@
+XMLLINT = @XMLLINT@
+YACC = @YACC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = $(am__append_1)
+sphinxbuilddir = $(builddir)/_build
+abs_sphinxbuilddir = $(abs_builddir)/_build
+@GENERATE_DOCS_TRUE@sphinxopts = -v -E -a -W -c "${abs_srcdir}"
+@GENERATE_DOCS_TRUE@static_sources = static/kea-imageonly-100bw.png \
+@GENERATE_DOCS_TRUE@ static/kea-logo-100x70.png \
+@GENERATE_DOCS_TRUE@ static/kea-logo-200.png static/kea.css
+
+# ARM
+@GENERATE_DOCS_TRUE@rst_arm_sources = index.rst manpages.rst umls.rst \
+@GENERATE_DOCS_TRUE@ arm/acknowledgments.rst arm/admin.rst \
+@GENERATE_DOCS_TRUE@ arm/agent.rst arm/classify.rst \
+@GENERATE_DOCS_TRUE@ arm/config-backend.rst \
+@GENERATE_DOCS_TRUE@ arm/config-templates.rst arm/config.rst \
+@GENERATE_DOCS_TRUE@ arm/congestion-handling.rst \
+@GENERATE_DOCS_TRUE@ arm/ctrl-channel.rst \
+@GENERATE_DOCS_TRUE@ arm/database-connectivity.rst arm/ddns.rst \
+@GENERATE_DOCS_TRUE@ arm/dhcp4-srv.rst arm/dhcp6-srv.rst \
+@GENERATE_DOCS_TRUE@ arm/ext-gss-tsig.rst arm/ext-netconf.rst \
+@GENERATE_DOCS_TRUE@ arm/ext-radius.rst arm/hammer.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-bootp.rst arm/hooks-cb-cmds.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-class-cmds.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-ddns-tuning.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-flex-id.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-flex-option.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-gss-tsig.rst arm/hooks-ha.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-host-cache.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-host-cmds.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-lease-cmds.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-lease-query.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-limits.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-cb-mysql.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-cb-pgsql.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-legal-log.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-perfmon.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-ping-check.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-radius.rst arm/hooks-rbac.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-run-script.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-stat-cmds.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-subnet-cmds.rst \
+@GENERATE_DOCS_TRUE@ arm/hooks-user-chk.rst arm/hooks.rst \
+@GENERATE_DOCS_TRUE@ arm/install.rst arm/integrations.rst \
+@GENERATE_DOCS_TRUE@ arm/intro.rst arm/keactrl.rst \
+@GENERATE_DOCS_TRUE@ arm/lease-expiration.rst arm/lfc.rst \
+@GENERATE_DOCS_TRUE@ arm/logging.rst arm/quickstart.rst \
+@GENERATE_DOCS_TRUE@ arm/security.rst arm/shell.rst \
+@GENERATE_DOCS_TRUE@ arm/stats.rst arm/stork.rst \
+@GENERATE_DOCS_TRUE@ grammar/grammar.rst \
+@GENERATE_DOCS_TRUE@ grammar/grammar-ca-parser.rst \
+@GENERATE_DOCS_TRUE@ grammar/grammar-d2-parser.rst \
+@GENERATE_DOCS_TRUE@ grammar/grammar-dhcp4-parser.rst \
+@GENERATE_DOCS_TRUE@ grammar/grammar-dhcp6-parser.rst \
+@GENERATE_DOCS_TRUE@ grammar/grammar-netconf-parser.rst
+@GENERATE_DOCS_TRUE@main_sources = $(rst_arm_sources) conf.py $(static_sources)
+
+# mans
+@GENERATE_DOCS_TRUE@rst_man_sources = man/kea-admin.8.rst \
+@GENERATE_DOCS_TRUE@ man/kea-ctrl-agent.8.rst \
+@GENERATE_DOCS_TRUE@ man/kea-dhcp4.8.rst man/kea-dhcp6.8.rst \
+@GENERATE_DOCS_TRUE@ man/kea-dhcp-ddns.8.rst man/kea-lfc.8.rst \
+@GENERATE_DOCS_TRUE@ man/kea-netconf.8.rst man/kea-shell.8.rst \
+@GENERATE_DOCS_TRUE@ man/keactrl.8.rst man/perfdhcp.8.rst
+@GENERATE_DOCS_TRUE@man8s = $(sphinxbuilddir)/man/kea-admin.8 \
+@GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/kea-ctrl-agent.8 \
+@GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/kea-dhcp4.8 \
+@GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/kea-dhcp6.8 \
+@GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/kea-dhcp-ddns.8 \
+@GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/kea-lfc.8 \
+@GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/kea-netconf.8 \
+@GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/kea-shell.8 \
+@GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/keactrl.8 \
+@GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/perfdhcp.8
+@GENERATE_DOCS_TRUE@man_sources = $(rst_man_sources) conf.py
+
+# list of messages files that are used to generate kea-messages.rst and then kea-messages.pdf
+@GENERATE_DOCS_TRUE@mes_files = $(top_srcdir)/src/hooks/dhcp/flex_option/flex_option_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/hooks/dhcp/bootp/bootp_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/hooks/dhcp/mysql_cb/mysql_cb_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/hooks/dhcp/high_availability/ha_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/hooks/dhcp/stat_cmds/stat_cmds_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/hooks/dhcp/user_chk/user_chk_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/config/config_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/hooks/hooks_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/dhcpsrv/dhcpsrv_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/dhcpsrv/alloc_engine_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/dhcpsrv/hosts_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/http/auth_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/http/http_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/dhcp_ddns/dhcp_ddns_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/database/db_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/log/log_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/log/logimpl_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/log/tests/log_test_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/process/process_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/asiodns/asiodns_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/eval/eval_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/d2srv/d2_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/tcp/tcp_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/bin/dhcp4/dhcp4_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/bin/agent/ca_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/bin/dhcp6/dhcp6_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/bin/lfc/lfc_messages.mes \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/bin/netconf/netconf_messages.mes
+
+# list of api files that are used to generate api.rst
+@GENERATE_DOCS_TRUE@api_files = $(top_srcdir)/src/share/api/build-report.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-clear.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-flush.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-get-by-id.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-insert.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-load.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-remove.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-size.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-write.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/class-add.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/class-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/class-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/class-list.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/class-update.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/config-backend-pull.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/config-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/config-hash-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/config-reload.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/config-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/config-test.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/config-write.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/dhcp-disable.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/dhcp-enable.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/extended-info4-upgrade.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/extended-info6-upgrade.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-key-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-key-expire.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-key-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-list.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-purge-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-purge.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-rekey-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-rekey.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-continue.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-heartbeat.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-maintenance-cancel.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-maintenance-notify.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-maintenance-start.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-reset.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-scopes.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-sync-complete-notify.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-sync.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-add.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-get-by-client-id.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-get-by-hostname.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-get-by-hw-address.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-get-page.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-resend-ddns.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-update.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-wipe.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-write.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-add.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-bulk-apply.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-get-by-duid.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-get-by-hostname.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-get-page.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-resend-ddns.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-update.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-wipe.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-write.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/leases-reclaim.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/libreload.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/list-commands.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network4-add.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network4-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network4-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network4-list.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network4-subnet-add.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network4-subnet-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network6-add.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network6-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network6-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network6-list.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network6-subnet-add.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network6-subnet-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class4-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class4-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class4-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class4-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class6-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class6-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class6-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class6-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter4-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter4-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter4-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter4-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter6-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter6-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter6-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter6-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network4-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network4-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network4-list.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network4-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network6-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network6-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network6-list.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network6-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def4-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def4-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def4-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def4-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def6-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def6-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def6-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def6-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-global-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-global-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-global-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-global-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-network-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-network-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-pool-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-pool-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-subnet-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-subnet-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-global-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-global-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-global-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-global-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-network-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-network-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-pd-pool-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-pd-pool-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-pool-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-pool-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-subnet-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-subnet-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server4-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server4-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server4-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server4-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server6-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server6-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server6-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server6-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet4-del-by-id.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet4-del-by-prefix.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet4-get-by-id.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet4-get-by-prefix.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet4-list.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet4-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet6-del-by-id.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet6-del-by-prefix.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet6-get-by-id.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet6-get-by-prefix.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet6-list.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet6-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-add.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-get-by-address.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-get-by-hostname.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-get-by-id.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-get-page.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-update.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/server-tag-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/shutdown.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/stat-lease4-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/stat-lease6-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-get-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-remove-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-remove.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-reset-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-reset.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-sample-age-set-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-sample-age-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-sample-count-set-all.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-sample-count-set.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/status-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet4-add.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet4-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet4-delta-add.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet4-delta-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet4-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet4-list.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet4-update.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet6-add.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet6-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet6-delta-add.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet6-delta-del.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet6-get.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet6-list.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet6-update.json \
+@GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/version-get.json
+
+# UML files
+@GENERATE_DOCS_TRUE@umls = uml/appendRequestedOptions.uml \
+@GENERATE_DOCS_TRUE@ uml/appendRequestedVendorOptions.uml \
+@GENERATE_DOCS_TRUE@ uml/assign-lease4.uml \
+@GENERATE_DOCS_TRUE@ uml/buildCfgOptionList.uml \
+@GENERATE_DOCS_TRUE@ uml/currentHost4.uml uml/lease-states.uml \
+@GENERATE_DOCS_TRUE@ uml/main-loop.uml uml/packet4.uml \
+@GENERATE_DOCS_TRUE@ uml/radius.uml \
+@GENERATE_DOCS_TRUE@ uml/recognizing-same-client.uml \
+@GENERATE_DOCS_TRUE@ uml/request4-lease.uml uml/request4.uml \
+@GENERATE_DOCS_TRUE@ uml/requestLease4.uml uml/select4.uml \
+@GENERATE_DOCS_TRUE@ uml/tkey.uml uml/update.uml
+@GENERATE_DOCS_TRUE@pngs = uml/appendRequestedOptions.png \
+@GENERATE_DOCS_TRUE@ uml/appendRequestedVendorOptions.png \
+@GENERATE_DOCS_TRUE@ uml/assign-lease4.png \
+@GENERATE_DOCS_TRUE@ uml/buildCfgOptionList.png \
+@GENERATE_DOCS_TRUE@ uml/currentHost4.png uml/lease-states.png \
+@GENERATE_DOCS_TRUE@ uml/main-loop.png uml/packet4.png \
+@GENERATE_DOCS_TRUE@ uml/radius.png \
+@GENERATE_DOCS_TRUE@ uml/recognizing-same-client.png \
+@GENERATE_DOCS_TRUE@ uml/request4-lease.png uml/request4.png \
+@GENERATE_DOCS_TRUE@ uml/requestLease4.png uml/select4.png \
+@GENERATE_DOCS_TRUE@ uml/tkey.png uml/update.png
+@GENERATE_DOCS_TRUE@svgs = uml/appendRequestedOptions.svg \
+@GENERATE_DOCS_TRUE@ uml/appendRequestedVendorOptions.svg \
+@GENERATE_DOCS_TRUE@ uml/assign-lease4.svg \
+@GENERATE_DOCS_TRUE@ uml/buildCfgOptionList.svg \
+@GENERATE_DOCS_TRUE@ uml/currentHost4.svg uml/lease-states.svg \
+@GENERATE_DOCS_TRUE@ uml/main-loop.svg uml/packet4.svg \
+@GENERATE_DOCS_TRUE@ uml/radius.svg \
+@GENERATE_DOCS_TRUE@ uml/recognizing-same-client.svg \
+@GENERATE_DOCS_TRUE@ uml/request4-lease.svg uml/request4.svg \
+@GENERATE_DOCS_TRUE@ uml/requestLease4.svg uml/select4.svg \
+@GENERATE_DOCS_TRUE@ uml/tkey.svg uml/update.svg
+@GENERATE_DOCS_TRUE@txts = uml/option-data-priority.atxt \
+@GENERATE_DOCS_TRUE@ uml/priority-of-lease-lifetimes-and-dhcpv4-fields.atxt \
+@GENERATE_DOCS_TRUE@ uml/tkey.atxt uml/update.atxt
+@GENERATE_DOCS_TRUE@PDFLATEX_AND_OPTS = $(PDFLATEX) -interaction nonstopmode
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/arm/rst_arm_sources.mk $(srcdir)/man/rst_man_sources.mk $(srcdir)/man/man8s.mk $(srcdir)/mes_files.mk $(top_srcdir)/src/share/api/api_files.mk $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/sphinx/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign doc/sphinx/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(srcdir)/arm/rst_arm_sources.mk $(srcdir)/man/rst_man_sources.mk $(srcdir)/man/man8s.mk $(srcdir)/mes_files.mk $(top_srcdir)/src/share/api/api_files.mk $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+@GENERATE_DOCS_FALSE@clean-local:
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-local mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+@GENERATE_DOCS_FALSE@html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+@GENERATE_DOCS_FALSE@pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-local
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ clean-local cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-data-local install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \
+ uninstall-am uninstall-local
+
+.PRECIOUS: Makefile
+
+
+@GENERATE_DOCS_TRUE@@HAVE_PDFLATEX_TRUE@all: html mans pdf text
+@GENERATE_DOCS_TRUE@@HAVE_PDFLATEX_FALSE@all: html mans text
+
+# build the list of message files
+@GENERATE_DOCS_TRUE@mes-files.txt: mes_files.mk
+@GENERATE_DOCS_TRUE@ @sed 's;mes_files .*)/;;' $< > $@
+
+# this rule is only used for development purposes and is not used in official
+# build process as kea-messages.rst is always generated via sphinx's conf.py
+@GENERATE_DOCS_TRUE@$(srcdir)/kea-messages.rst: $(mes_files) mes2doc.py
+@GENERATE_DOCS_TRUE@ $(PYTHON) $(srcdir)/mes2doc.py -o $@ $(mes_files)
+
+# build the list of api files
+@GENERATE_DOCS_TRUE@api-files.txt: $(top_srcdir)/src/share/api/api_files.mk
+@GENERATE_DOCS_TRUE@ @sed 's;api_files .*)/;;' $< > $@
+
+# this rule is only used for development purposes and is not used in official
+# build process as api.rst is always generated via sphinx's conf.py
+@GENERATE_DOCS_TRUE@$(srcdir)/api.rst: $(api_files) api-files.txt api2doc.py
+@GENERATE_DOCS_TRUE@ $(PYTHON) $(srcdir)/api2doc.py -o $@ $(api_files)
+
+@GENERATE_DOCS_TRUE@$(srcdir)/arm/platforms.rst:
+@GENERATE_DOCS_TRUE@ rm -f $(srcdir)/arm/platforms.rst
+@GENERATE_DOCS_TRUE@ cp $(srcdir)/../../platforms.rst $(srcdir)/arm/platforms.rst
+
+@GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@uml: uml-to-png uml-to-svg format-svgs uml-to-txt
+
+@GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@uml-to-png: $(umls)
+@GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@ @ @PLANTUML@ -tpng $^
+
+@GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@uml-to-svg: $(umls)
+@GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@ @ @PLANTUML@ -tsvg $^
+
+# Formatting puts every tag on a separate line, which makes it easier for diffing.
+@GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@format-svgs: $(svgs)
+@GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@@HAVE_XMLLINT_TRUE@ @ for svg in $(svgs); do xmllint --format $$svg > tmp; mv tmp $$svg; done
+@GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@@HAVE_XMLLINT_FALSE@ @ printf 'WARNING: xmllint not found. SVGs not formatted.\n'
+
+# Only sequence diagrams support ASCII art.
+@GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@uml-to-txt: uml/tkey.uml uml/update.uml
+@GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@ @ @PLANTUML@ -ttxt $^
+@GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@ @ for txt in $(txts); do sed 's/ *$$//g' $$txt > tmp; mv tmp $$txt; done
+
+@GENERATE_DOCS_TRUE@pdf: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst
+@GENERATE_DOCS_TRUE@ $(SPHINXBUILD) -M latex $(srcdir) $(sphinxbuilddir) $(sphinxopts)
+@GENERATE_DOCS_TRUE@ cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-arm.tex
+@GENERATE_DOCS_TRUE@ cd $(abs_sphinxbuilddir)/latex && makeindex -s python.ist kea-arm.idx
+@GENERATE_DOCS_TRUE@ cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-arm.tex
+@GENERATE_DOCS_TRUE@ cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-messages.tex
+@GENERATE_DOCS_TRUE@ cd $(abs_sphinxbuilddir)/latex && makeindex -s python.ist kea-messages.idx
+@GENERATE_DOCS_TRUE@ cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-messages.tex
+
+@GENERATE_DOCS_TRUE@html: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst
+@GENERATE_DOCS_TRUE@ $(SPHINXBUILD) -M html $(srcdir) $(sphinxbuilddir) $(sphinxopts)
+
+# This target is not used anywhere, but people who prefer single page docs
+# can do make -C doc/sphinx singlehtml and then enjoy their docs being
+# generated in doc/sphinx/_build/singlehtml
+@GENERATE_DOCS_TRUE@singlehtml: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst
+@GENERATE_DOCS_TRUE@ $(SPHINXBUILD) -M singlehtml $(srcdir) $(sphinxbuilddir) $(sphinxopts)
+
+@GENERATE_DOCS_TRUE@text: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst
+@GENERATE_DOCS_TRUE@ $(SPHINXBUILD) -M text $(srcdir) $(sphinxbuilddir) $(sphinxopts)
+
+@GENERATE_DOCS_TRUE@$(man8s): mans
+
+@GENERATE_DOCS_TRUE@mans: $(man_sources) api-files.txt mes-files.txt
+@GENERATE_DOCS_TRUE@ $(SPHINXBUILD) -M man $(srcdir) $(sphinxbuilddir) $(sphinxopts)
+
+# Updates sphinx dependencies to the latest versions available on the current
+# host and freezes them in ./src/requirements.txt. Requires pip-compile:
+# pip install pip-tools
+# Run this when dependencies are added, changed or removed.
+@GENERATE_DOCS_TRUE@update-python-dependencies: ./src/requirements.in
+@GENERATE_DOCS_TRUE@ rm ./src/requirements.txt
+@GENERATE_DOCS_TRUE@ pip-compile -r ./src/requirements.in
+
+@GENERATE_DOCS_TRUE@clean-local:
+@GENERATE_DOCS_TRUE@ rm -rf $(sphinxbuilddir)
+@GENERATE_DOCS_TRUE@ rm -f $(srcdir)/mes-files.txt $(srcdir)/api-files.txt
+@GENERATE_DOCS_TRUE@ rm -f $(srcdir)/kea-messages.rst $(srcdir)/api.rst
+@GENERATE_DOCS_TRUE@ rm -f $(srcdir)/arm/platforms.rst
+
+@GENERATE_DOCS_TRUE@.PHONY: all pdf html mans update-python-dependencies uml uml-to-png uml-to-svg format-svgs uml-to-txt
+
+# install and uninstall can occur with GENERATE_DOCS and without it
+# so we want to install all when GENERATE_DOCS is and
+# just mans when GENERATE_DOCS is not used, and when man files exists (e.g release tarball)
+install-data-local:
+ mkdir -p $(DESTDIR)$(docdir)
+@GENERATE_DOCS_TRUE@ cp -r $(sphinxbuilddir)/html $(DESTDIR)$(docdir)
+@GENERATE_DOCS_TRUE@@HAVE_PDFLATEX_TRUE@ ${INSTALL_DATA} $(sphinxbuilddir)/latex/kea-arm.pdf $(DESTDIR)$(docdir)
+@GENERATE_DOCS_TRUE@@HAVE_PDFLATEX_TRUE@ ${INSTALL_DATA} $(sphinxbuilddir)/latex/kea-messages.pdf $(DESTDIR)$(docdir)
+@GENERATE_DOCS_TRUE@ ${MKDIR_P} ${DESTDIR}${mandir}/man8
+@GENERATE_DOCS_TRUE@ ${INSTALL_DATA} $(man8s) ${DESTDIR}${mandir}/man8/
+@GENERATE_DOCS_FALSE@@INSTALL_MANS_TRUE@ ${MKDIR_P} ${DESTDIR}${mandir}/man8
+@GENERATE_DOCS_FALSE@@INSTALL_MANS_TRUE@ ${INSTALL_DATA} $(sphinxbuilddir)/man/*.8 ${DESTDIR}${mandir}/man8/
+
+uninstall-local:
+ rm -rf $(DESTDIR)$(docdir)
+
+# There are sometimes conflicts when more then one sphinx-build is run at a time.
+# This target blocks running anything in parallel in this Makefile,
+# all is run serially.
+
+.NOTPARALLEL:
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/doc/sphinx/_build/man/kea-admin.8 b/doc/sphinx/_build/man/kea-admin.8
new file mode 100644
index 0000000..0e9c2d6
--- /dev/null
+++ b/doc/sphinx/_build/man/kea-admin.8
@@ -0,0 +1,185 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KEA-ADMIN" "8" "Mar 22, 2024" "2.5.7" "Kea"
+.SH NAME
+kea-admin \- Shell script for managing Kea databases
+.SH SYNOPSIS
+.sp
+\fBkea\-admin\fP [command] [backend] [\fB\-h\fP database_host]
+[\fB\-P\fP database_port] [\fB\-u\fP database_username]
+[\fB\-p\fP [database_password]] [\fB\-n\fP database_name] [\fB\-d\fP script_directory]
+[\fB\-v\fP] [\fB\-x\fP extra_argument [\fB\-x\fP extra_argument ...]]
+[\fB\-4\fP | \fB\-6\fP] [\fB\-i\fP input_file] [\fB\-o\fP output_file] [\fB\-y\fP]
+.SH DESCRIPTION
+.sp
+\fBkea\-admin\fP is a shell script that offers database maintenance. In
+particular, it features database initialization, database version
+checking, and database schema upgrading.
+.SH ARGUMENTS
+.INDENT 0.0
+.TP
+.B \fBcommand\fP
+Specifies the command to be issued to the servers. It can be one of the
+following:
+.INDENT 7.0
+.TP
+.B \fBdb\-init\fP
+Initializes a new database schema. This is useful during a new Kea
+installation. The database is initialized to the latest version
+supported by the version of the software being installed.
+.TP
+.B \fBdb\-version\fP
+Reports the database backend version number. This is not necessarily
+equal to the Kea version number, as each backend has its own
+versioning scheme.
+.TP
+.B \fBdb\-upgrade\fP
+Conducts a database schema upgrade. This is useful when upgrading Kea.
+.TP
+.B \fBlease\-dump\fP
+Dumps the contents of the lease database (for MySQL or
+PostgreSQL backends) to a CSV (comma\-separated values) text file. (Support
+for the Cassandra backend has been deprecated.)
+The first line of the file contains the column names. This can be used
+as a way to switch from a database backend to a memfile backend.
+Alternatively, it can be used as a diagnostic tool, so it provides a
+portable form of the lease data. There are other mandatory arguments
+that must be used together with this command. Either \fB\-4\fP or \fB\-6\fP must
+be specified. Also \fB\-o\fP or \fB\-\-output\fP must be provided.
+.TP
+.B \fBlease\-upload\fP
+Uploads leases from a CSV (comma\-separated values) text file to a MySQL or
+a PostgreSQL lease database. The CSV file needs to be in memfile format.
+There are other mandatory arguments that must be used together with this
+command. Either \fB\-4\fP or \fB\-6\fP must be specified.
+Also \fB\-i\fP or \fB\-\-input\fP must be provided.
+.TP
+.B \fBstats\-recount\fP
+Recounts lease statistics for a MySQL or PostgreSQL database.
+.UNINDENT
+.TP
+.B \fBbackend\fP
+Specifies the backend type. Currently allowed backends are: memfile,
+mysql, and pgsql; cql has been deprecated.
+.TP
+.B \fB\-h|\-\-host hostname\fP
+Specifies the hostname when connecting to a database.
+The default value is \fBlocalhost\fP\&.
+.TP
+.B \fB\-i|\-\-input input_file\fP
+Specifies the CSV (comma\-separated values) text file with leases to be uploaded.
+Required for \fBlease\-upload\fP\&.
+.TP
+.B \fB\-P|\-\-port port\fP
+Specifies the port when connecting to a database. If not specified,
+the default value chosen by the database client is used.
+.TP
+.B \fB\-u|\-\-user username\fP
+Specifies the username when connecting to a database.
+The default value is \fBkeatest\fP\&.
+.TP
+.B \fB\-p|\-\-password password\fP
+Specifies the password when connecting to a database.
+If only \fB\-p\fP or \fB\-\-password\fP is given, the user is prompted for a password.
+If not specified at all, the \fBKEA_ADMIN_DB_PASSWORD\fP environment variable
+is checked for a value and used if it exists.
+Otherwise the default value of \fBkeatest\fP is used.
+.TP
+.B \fB\-n|\-\-name database\-name\fP
+Specifies the name of the database to connect to. The
+default value is \fBkeatest\fP\&.
+.TP
+.B \fB\-d|\-\-directory script\-directory\fP
+Specifies the override scripts directory. That script is used during
+upgrades, database initialization, and possibly other operations.
+The default value is \fB(prefix)/share/kea/scripts/\fP\&.
+.TP
+.B \fB\-o|\-\-output output_file\fP
+Specifies the file to which the lease data will be dumped. Required for
+\fBlease\-dump\fP\&.
+.TP
+.B \fB\-v|\-\-version\fP
+Prints the \fBkea\-admin\fP version and quits.
+.TP
+.B \fB\-4\fP
+Directs \fBkea\-admin\fP to lease\-dump the DHCPv4 leases. Incompatible with
+the \-6 option.
+.TP
+.B \fB\-6\fP
+Directs \fBkea\-admin\fP to lease\-dump the DHCPv6 leases. Incompatible with
+the \-4 option.
+.TP
+.B \fB\-x|\-\-extra\fP
+Specifies an extra argument to pass to the database command tool e.g.
+to invoke \fBmysql\fP with the \fB\-\-ssl\fP argument. This can be repeated
+to pass more than one argument. Quotes are not preserved. Avoid commands
+containing spaces.
+.TP
+.B \fB\-y|\-\-yes\fP
+Assume yes on overwriting temporary files.
+.UNINDENT
+.SH DOCUMENTATION
+.sp
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software \- compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+\fI\%https://kea.readthedocs.io\fP\&.
+.sp
+Kea source code is documented in the Kea Developer\(aqs Guide,
+available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&.
+.sp
+The Kea project website is available at \fI\%https://kea.isc.org\fP\&.
+.SH MAILING LISTS AND SUPPORT
+.sp
+There are two public mailing lists available for the Kea project. \fBkea\-users\fP
+(kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP
+(kea\-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+\fI\%https://lists.isc.org\fP\&. The community provides best\-effort support
+on both of those lists.
+.sp
+ISC provides professional support for Kea services. See
+\fI\%https://www.isc.org/kea/\fP for details.
+.SH SEE ALSO
+.sp
+\fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP,
+\fBkea\-dhcp\-ddns(8)\fP, \fBkea\-ctrl\-agent(8)\fP,
+\fBkeactrl(8)\fP, \fBperfdhcp(8)\fP, \fBkea\-netconf(8)\fP,
+Kea Administrator Reference Manual.
+.SH AUTHOR
+Internet Systems Consortium
+.SH COPYRIGHT
+2019-2024, Internet Systems Consortium
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/sphinx/_build/man/kea-ctrl-agent.8 b/doc/sphinx/_build/man/kea-ctrl-agent.8
new file mode 100644
index 0000000..e12b4e1
--- /dev/null
+++ b/doc/sphinx/_build/man/kea-ctrl-agent.8
@@ -0,0 +1,113 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KEA-CTRL-AGENT" "8" "Mar 22, 2024" "2.5.7" "Kea"
+.SH NAME
+kea-ctrl-agent \- Control Agent process in Kea
+.SH SYNOPSIS
+.sp
+\fBkea\-ctrl\-agent\fP [\fB\-v\fP] [\fB\-V\fP] [\fB\-W\fP] [\fB\-d\fP] [\fB\-c\fP config\-file] [\fB\-t\fP config\-file]
+.SH DESCRIPTION
+.sp
+The \fBkea\-ctrl\-agent\fP provides a REST service for controlling Kea
+services. The received HTTP requests are decapsulated and forwarded to
+the respective Kea services in JSON format. Received JSON responses are
+encapsulated within HTTP responses and returned to the controlling
+entity. Some commands may be handled by the Control Agent directly, and
+not forwarded to any Kea service.
+.SH ARGUMENTS
+.sp
+The arguments are as follows:
+.INDENT 0.0
+.TP
+.B \fB\-v\fP
+Displays the version.
+.TP
+.B \fB\-V\fP
+Displays the extended version.
+.TP
+.B \fB\-W\fP
+Displays the configuration report.
+.TP
+.B \fB\-d\fP
+Sets the logging level to debug with extra verbosity. This is primarily for
+development purposes in stand\-alone mode.
+.TP
+.B \fB\-c config\-file\fP
+Specifies the file with the configuration for the Control Agent
+server. It may also contain configuration entries for other Kea
+services.
+.TP
+.B \fB\-t config\-file\fP
+Checks the syntax of the configuration file and reports the first error,
+if any. Note that not all parameters are completely checked; in
+particular, service and client sockets are not opened, and hook
+libraries are not loaded.
+.UNINDENT
+.SH DOCUMENTATION
+.sp
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software \- compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+\fI\%https://kea.readthedocs.io\fP\&.
+.sp
+Kea source code is documented in the Kea Developer\(aqs Guide,
+available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&.
+.sp
+The Kea project website is available at \fI\%https://kea.isc.org\fP\&.
+.SH MAILING LISTS AND SUPPORT
+.sp
+There are two public mailing lists available for the Kea project. \fBkea\-users\fP
+(kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP
+(kea\-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+\fI\%https://lists.isc.org\fP\&. The community provides best\-effort support
+on both of those lists.
+.sp
+ISC provides professional support for Kea services. See
+\fI\%https://www.isc.org/kea/\fP for details.
+.SH HISTORY
+.sp
+The \fBkea\-ctrl\-agent\fP was first coded in December 2016 by Marcin
+Siodelski.
+.SH SEE ALSO
+.sp
+\fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP,
+\fBkea\-dhcp\-ddns(8)\fP, \fBkea\-admin(8)\fP, \fBkeactrl(8)\fP,
+\fBperfdhcp(8)\fP, \fBkea\-lfc(8)\fP, Kea Administrator Reference Manual.
+.SH AUTHOR
+Internet Systems Consortium
+.SH COPYRIGHT
+2019-2024, Internet Systems Consortium
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/sphinx/_build/man/kea-dhcp-ddns.8 b/doc/sphinx/_build/man/kea-dhcp-ddns.8
new file mode 100644
index 0000000..8368f0a
--- /dev/null
+++ b/doc/sphinx/_build/man/kea-dhcp-ddns.8
@@ -0,0 +1,113 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KEA-DHCP-DDNS" "8" "Mar 22, 2024" "2.5.7" "Kea"
+.SH NAME
+kea-dhcp-ddns \- DHCP-DDNS process in Kea
+.SH SYNOPSIS
+.sp
+\fBkea\-dhcp\-ddns\fP [\fB\-v\fP] [\fB\-V\fP] [\fB\-W\fP] [\fB\-d\fP] [\fB\-c\fP config\-file] [\fB\-t\fP config\-file]
+.SH DESCRIPTION
+.sp
+The \fBkea\-dhcp\-ddns\fP service process requests an update of DNS mapping
+based on DHCP lease\-change events. It runs as a separate process that
+expects to receive Name Change Requests from Kea DHCP servers.
+.SH ARGUMENTS
+.sp
+The arguments are as follows:
+.INDENT 0.0
+.TP
+.B \fB\-v\fP
+Displays the version.
+.TP
+.B \fB\-V\fP
+Displays the extended version.
+.TP
+.B \fB\-W\fP
+Displays the configuration report.
+.TP
+.B \fB\-d\fP
+Sets the logging level to debug with extra verbosity. This is primarily for
+development purposes in stand\-alone mode.
+.TP
+.B \fB\-c config\-file\fP
+Specifies the configuration file with the configuration for the DHCP\-DDNS server. It
+may also contain configuration entries for other Kea services.
+.TP
+.B \fB\-t config\-file\fP
+Checks the syntax of the configuration file and reports the first error,
+if any. Note that not all parameters are completely checked; in
+particular, a service socket is not opened.
+.UNINDENT
+.SH DOCUMENTATION
+.sp
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software \- compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+\fI\%https://kea.readthedocs.io\fP\&.
+.sp
+Kea source code is documented in the Kea Developer\(aqs Guide,
+available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&.
+.sp
+The Kea project website is available at \fI\%https://kea.isc.org\fP\&.
+.SH MAILING LISTS AND SUPPORT
+.sp
+There are two public mailing lists available for the Kea project. \fBkea\-users\fP
+(kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP
+(kea\-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+\fI\%https://lists.isc.org\fP\&. The community provides best\-effort support
+on both of those lists.
+.sp
+ISC provides professional support for Kea services. See
+\fI\%https://www.isc.org/kea/\fP for details.
+.SH HISTORY
+.sp
+The \fBb10\-dhcp\-ddns\fP process was first coded in May 2013 by Thomas
+Markwalder.
+.sp
+Kea became a standalone server and the BIND 10 framework was removed. The
+DHCP\-DDNS server binary was renamed to kea\-dhcp\-ddns in July 2014. Kea
+1.0.0 was released in December 2015.
+.SH SEE ALSO
+.sp
+\fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP,
+\fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkeactrl(8)\fP,
+\fBperfdhcp(8)\fP, \fBkea\-netconf(8)\fP, \fBkea\-lfc(8)\fP,
+Kea Administrator Reference Manual.
+.SH AUTHOR
+Internet Systems Consortium
+.SH COPYRIGHT
+2019-2024, Internet Systems Consortium
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/sphinx/_build/man/kea-dhcp4.8 b/doc/sphinx/_build/man/kea-dhcp4.8
new file mode 100644
index 0000000..cb000ac
--- /dev/null
+++ b/doc/sphinx/_build/man/kea-dhcp4.8
@@ -0,0 +1,128 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KEA-DHCP4" "8" "Mar 22, 2024" "2.5.7" "Kea"
+.SH NAME
+kea-dhcp4 \- DHCPv4 server in Kea
+.SH SYNOPSIS
+.sp
+\fBkea\-dhcp4\fP [\fB\-v\fP] [\fB\-V\fP] [\fB\-W\fP] [\fB\-d\fP] [\fB\-c\fP config\-file] [\fB\-t\fP config\-file] [\fB\-p\fP server\-port\-number] [\fB\-P\fP client\-port\-number]
+.SH DESCRIPTION
+.sp
+The \fBkea\-dhcp4\fP daemon provides the DHCPv4 server implementation.
+.SH ARGUMENTS
+.sp
+The arguments are as follows:
+.INDENT 0.0
+.TP
+.B \fB\-v\fP
+Displays the version.
+.TP
+.B \fB\-V\fP
+Displays the extended version.
+.TP
+.B \fB\-W\fP
+Displays the configuration report.
+.TP
+.B \fB\-d\fP
+Enables the debug mode with extra verbosity.
+.TP
+.B \fB\-c config\-file\fP
+Specifies the configuration file with the configuration for the DHCPv4 server. It
+may also contain configuration entries for other Kea services.
+.TP
+.B \fB\-t config\-file\fP
+Checks the configuration file and reports the first error, if any. Note
+that not all parameters are completely checked; in particular,
+service and control channel sockets are not opened, and hook
+libraries are not loaded.
+.TP
+.B \fB\-T config\-file\fP
+Checks the configuration file and reports the first error, if any.
+It performs extra checks beyond what \-t offers, such as establishing
+database connections (for the lease backend, host reservations backend,
+configuration backend, and forensic logging backend), loading hook libraries,
+parsing hook\-library configurations, etc. It does not open UNIX or TCP/UDP sockets,
+nor does it open or rotate files, as any of these actions could interfere
+with a running process on the same machine.
+.TP
+.B \fB\-p server\-port\-number\fP
+Specifies the server port number (1\-65535) on which the server listens. This is
+useful for testing purposes only.
+.TP
+.B \fB\-P client\-port\-number\fP
+Specifies the client port number (1\-65535) to which the server responds. This is
+useful for testing purposes only.
+.UNINDENT
+.SH DOCUMENTATION
+.sp
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software \- compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+\fI\%https://kea.readthedocs.io\fP\&.
+.sp
+Kea source code is documented in the Kea Developer\(aqs Guide,
+available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&.
+.sp
+The Kea project website is available at \fI\%https://kea.isc.org\fP\&.
+.SH MAILING LISTS AND SUPPORT
+.sp
+There are two public mailing lists available for the Kea project. \fBkea\-users\fP
+(kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP
+(kea\-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+\fI\%https://lists.isc.org\fP\&. The community provides best\-effort support
+on both of those lists.
+.sp
+ISC provides professional support for Kea services. See
+\fI\%https://www.isc.org/kea/\fP for details.
+.SH HISTORY
+.sp
+The \fBb10\-dhcp4\fP daemon was first coded in November 2011 by Tomek
+Mrugalski.
+.sp
+In mid\-2014, Kea was decoupled from the BIND 10 framework and became a
+standalone DHCP server. The DHCPv4 server binary was renamed to
+\fBkea\-dhcp4\fP\&. Kea 1.0.0 was released in December 2015.
+.SH SEE ALSO
+.sp
+\fBkea\-dhcp6(8)\fP, \fBkea\-dhcp\-ddns(8)\fP,
+\fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkeactrl(8)\fP,
+\fBperfdhcp(8)\fP, \fBkea\-netconf(8)\fP, \fBkea\-lfc(8)\fP,
+Kea Administrator Reference Manual.
+.SH AUTHOR
+Internet Systems Consortium
+.SH COPYRIGHT
+2019-2024, Internet Systems Consortium
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/sphinx/_build/man/kea-dhcp6.8 b/doc/sphinx/_build/man/kea-dhcp6.8
new file mode 100644
index 0000000..abf1658
--- /dev/null
+++ b/doc/sphinx/_build/man/kea-dhcp6.8
@@ -0,0 +1,128 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KEA-DHCP6" "8" "Mar 22, 2024" "2.5.7" "Kea"
+.SH NAME
+kea-dhcp6 \- DHCPv6 server in Kea
+.SH SYNOPSIS
+.sp
+\fBkea\-dhcp6\fP [\fB\-v\fP] [\fB\-V\fP] [\fB\-W\fP] [\fB\-d\fP] [\fB\-c\fP config\-file] [\fB\-t\fP config\-file] [\fB\-p\fP server\-port\-number] [\fB\-P\fP client\-port\-number]
+.SH DESCRIPTION
+.sp
+The \fBkea\-dhcp6\fP daemon provides the DHCPv6 server implementation.
+.SH ARGUMENTS
+.sp
+The arguments are as follows:
+.INDENT 0.0
+.TP
+.B \fB\-v\fP
+Displays the version.
+.TP
+.B \fB\-V\fP
+Displays the extended version.
+.TP
+.B \fB\-W\fP
+Displays the configuration report.
+.TP
+.B \fB\-d\fP
+Enables the debug mode with extra verbosity.
+.TP
+.B \fB\-c config\-file\fP
+Specifies the configuration file with the configuration for the DHCPv6 server. It
+may also contain configuration entries for other Kea services.
+.TP
+.B \fB\-t config\-file\fP
+Checks the configuration file and reports the first error, if any. Note
+that not all parameters are completely checked; in particular,
+service and control channel sockets are not opened, and hook
+libraries are not loaded.
+.TP
+.B \fB\-T config\-file\fP
+Checks the configuration file and reports the first error, if any.
+It performs extra checks beyond what \-t offers, such as establishing
+database connections (for the lease backend, host reservations backend,
+configuration backend, and forensic logging backend), loading hook libraries,
+parsing hook\-library configurations, etc. It does not open UNIX or TCP/UDP sockets, nor
+does it open or rotate files, as any of these actions could interfere with
+a running process on the same machine.
+.TP
+.B \fB\-p server\-port\-number\fP
+Specifies the server port number (1\-65535) on which the server listens. This is
+useful for testing purposes only.
+.TP
+.B \fB\-P client\-port\-number\fP
+Specifies the client port number (1\-65535) to which the server responds. This is
+useful for testing purposes only.
+.UNINDENT
+.SH DOCUMENTATION
+.sp
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software \- compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+\fI\%https://kea.readthedocs.io\fP\&.
+.sp
+Kea source code is documented in the Kea Developer\(aqs Guide,
+available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&.
+.sp
+The Kea project website is available at \fI\%https://kea.isc.org\fP\&.
+.SH MAILING LISTS AND SUPPORT
+.sp
+There are two public mailing lists available for the Kea project. \fBkea\-users\fP
+(kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP
+(kea\-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+\fI\%https://lists.isc.org\fP\&. The community provides best\-effort support
+on both of those lists.
+.sp
+ISC provides professional support for Kea services. See
+\fI\%https://www.isc.org/kea/\fP for details.
+.SH HISTORY
+.sp
+The \fBb10\-dhcp6\fP daemon was first coded in June 2011 by Tomek
+Mrugalski.
+.sp
+In mid\-2014, Kea was decoupled from the BIND 10 framework and became a
+standalone DHCP server. The DHCPv6 server binary was renamed to
+\fBkea\-dhcp6\fP\&. Kea 1.0.0 was released in December 2015.
+.SH SEE ALSO
+.sp
+\fBkea\-dhcp4(8)\fP, \fBkea\-dhcp\-ddns(8)\fP,
+\fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkeactrl(8)\fP,
+\fBperfdhcp(8)\fP, \fBkea\-netconf(8)\fP, \fBkea\-lfc(8)\fP,
+Kea Administrator Reference Manual.
+.SH AUTHOR
+Internet Systems Consortium
+.SH COPYRIGHT
+2019-2024, Internet Systems Consortium
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/sphinx/_build/man/kea-lfc.8 b/doc/sphinx/_build/man/kea-lfc.8
new file mode 100644
index 0000000..2713ca4
--- /dev/null
+++ b/doc/sphinx/_build/man/kea-lfc.8
@@ -0,0 +1,144 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KEA-LFC" "8" "Mar 22, 2024" "2.5.7" "Kea"
+.SH NAME
+kea-lfc \- Lease File Cleanup process in Kea
+.SH SYNOPSIS
+.sp
+\fBkea\-lfc\fP [\fB\-4**|\fP\-6**] [\fB\-c\fP config\-file] [\fB\-p\fP pid\-file] [\fB\-x\fP previous\-file] [\fB\-i\fP copy\-file] [\fB\-o\fP output\-file] [\fB\-f\fP finish\-file] [\fB\-v\fP] [\fB\-V\fP] [\fB\-W\fP] [\fB\-d\fP] [\fB\-h\fP]
+.SH DESCRIPTION
+.sp
+The \fBkea\-lfc\fP service process removes redundant information from the
+files used to provide persistent storage for the memfile database
+backend. The service is written to run as a stand\-alone process. While
+it can be started externally, there is usually no need to do this. It
+is run periodically by the Kea DHCP servers.
+.SH ARGUMENTS
+.sp
+The arguments are as follows:
+.INDENT 0.0
+.TP
+.B \fB\-4 | \-6\fP
+Indicates the protocol version of the lease files; must be either 4 or 6.
+.TP
+.B \fB\-c config\-file\fP
+Specifies the file with the configuration for the \fBkea\-lfc\fP
+process. It may also contain configuration entries for other Kea
+services. Currently \fBkea\-lfc\fP gets all of its arguments from the
+command line.
+.TP
+.B \fB\-p pid\-file\fP
+Specifies the PID file. When the \fBkea\-lfc\fP process starts, it attempts to
+determine if another instance of the process is already running, by
+examining the PID file. If one is already running, the new process is
+terminated. If one is not running, Kea writes its PID into the PID file.
+.TP
+.B \fB\-x previous\-file\fP
+Specifies the previous or ex\-lease file. When \fBkea\-lfc\fP starts, this is the
+result of any previous run of \fBkea\-lfc\fP; when \fBkea\-lfc\fP finishes,
+it is the result of the current run. If \fBkea\-lfc\fP is interrupted before
+completing, this file may not exist.
+.TP
+.B \fB\-i copy\-file\fP
+Specifies the input or copy of lease file. Before the DHCP server invokes
+\fBkea\-lfc\fP, it moves the current lease file here and then calls
+\fBkea\-lfc\fP with this file.
+.TP
+.B \fB\-o output\-file\fP
+Specifies the output lease file, which is the temporary file \fBkea\-lfc\fP should use to
+write the leases. Once this file is finished writing, it is
+moved to the finish file (see below).
+.TP
+.B \fB\-f finish\-file\fP
+Specifies the finish or completion file, another temporary file \fBkea\-lfc\fP uses
+for bookkeeping. When \fBkea\-lfc\fP finishes writing the output file,
+it moves it to this file name. After \fBkea\-lfc\fP finishes deleting
+the other files (previous and input), it moves this file to the previous
+lease file. By moving the files in this fashion, the \fBkea\-lfc\fP and
+the DHCP server processes can determine the correct file to use even
+if one of the processes was interrupted before completing its task.
+.TP
+.B \fB\-v\fP
+Causes the version stamp to be printed.
+.TP
+.B \fB\-V\fP
+Causes a longer form of the version stamp to be printed.
+.TP
+.B \fB\-W\fP
+Displays the configuration report.
+.TP
+.B \fB\-d\fP
+Sets the logging level to debug with extra verbosity. This is primarily for
+development purposes in stand\-alone mode.
+.TP
+.B \fB\-h\fP
+Causes the usage string to be printed.
+.UNINDENT
+.SH DOCUMENTATION
+.sp
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software \- compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+\fI\%https://kea.readthedocs.io\fP\&.
+.sp
+Kea source code is documented in the Kea Developer\(aqs Guide,
+available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&.
+.sp
+The Kea project website is available at \fI\%https://kea.isc.org\fP\&.
+.SH MAILING LISTS AND SUPPORT
+.sp
+There are two public mailing lists available for the Kea project. \fBkea\-users\fP
+(kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP
+(kea\-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+\fI\%https://lists.isc.org\fP\&. The community provides best\-effort support
+on both of those lists.
+.sp
+ISC provides professional support for Kea services. See
+\fI\%https://www.isc.org/kea/\fP for details.
+.SH HISTORY
+.sp
+The \fBkea\-lfc\fP process was first coded in January 2015 by the ISC
+Kea/DHCP team.
+.SH SEE ALSO
+.sp
+\fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP, \fBkea\-dhcp\-ddns(8)\fP,
+\fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkeactrl(8)\fP,
+\fBperfdhcp(8)\fP, \fBkea\-netconf(8)\fP, Kea Administrator Reference Manual.
+.SH AUTHOR
+Internet Systems Consortium
+.SH COPYRIGHT
+2019-2024, Internet Systems Consortium
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/sphinx/_build/man/kea-netconf.8 b/doc/sphinx/_build/man/kea-netconf.8
new file mode 100644
index 0000000..fcd87be
--- /dev/null
+++ b/doc/sphinx/_build/man/kea-netconf.8
@@ -0,0 +1,108 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KEA-NETCONF" "8" "Mar 22, 2024" "2.5.7" "Kea"
+.SH NAME
+kea-netconf \- NETCONF agent for configuring Kea
+.SH SYNOPSIS
+.sp
+\fBkea\-netconf\fP [\fB\-v\fP] [\fB\-V\fP] [\fB\-W\fP] [\fB\-d\fP] [\fB\-c\fP config\-file] [\fB\-t\fP config\-file]
+.SH DESCRIPTION
+.sp
+The \fBkea\-netconf\fP agent provides a YANG/NETCONF interface for the Kea
+environment.
+.SH ARGUMENTS
+.sp
+The arguments are as follows:
+.INDENT 0.0
+.TP
+.B \fB\-v\fP
+Displays the version.
+.TP
+.B \fB\-V\fP
+Displays the extended version.
+.TP
+.B \fB\-W\fP
+Displays the configuration report.
+.TP
+.B \fB\-d\fP
+Enables the debug mode with extra verbosity.
+.TP
+.B \fB\-c config\-file\fP
+Specifies the file with the configuration for the NETCONF agent.
+.TP
+.B \fB\-t config\-file\fP
+Checks the syntax of the configuration file and reports the first error,
+if any. Note that not all parameters are completely checked; in
+particular, service and client sockets are not opened, and hook
+libraries are not loaded.
+.UNINDENT
+.SH DOCUMENTATION
+.sp
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software \- compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+\fI\%https://kea.readthedocs.io\fP\&.
+.sp
+Kea source code is documented in the Kea Developer\(aqs Guide,
+available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&.
+.sp
+The Kea project website is available at \fI\%https://kea.isc.org\fP\&.
+.SH MAILING LISTS AND SUPPORT
+.sp
+There are two public mailing lists available for the Kea project. \fBkea\-users\fP
+(kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP
+(kea\-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+\fI\%https://lists.isc.org\fP\&. The community provides best\-effort support
+on both of those lists.
+.sp
+ISC provides professional support for Kea services. See
+\fI\%https://www.isc.org/kea/\fP for details.
+.SH HISTORY
+.sp
+Early prototypes of \fBkea\-netconf\fP implementation were written during IETF
+Hackathons in Berlin, London, and Montreal. An actual production\-ready
+implementation was started in August 2018 by Tomek Mrugalski and Francis
+Dupont.
+.SH SEE ALSO
+.sp
+\fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP, \fBkea\-dhcp\-ddns(8)\fP,
+\fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkeactrl(8)\fP,
+\fBperfdhcp(8)\fP, \fBkea\-lfc(8)\fP, Kea Administrator Reference Manual.
+.SH AUTHOR
+Internet Systems Consortium
+.SH COPYRIGHT
+2019-2024, Internet Systems Consortium
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/sphinx/_build/man/kea-shell.8 b/doc/sphinx/_build/man/kea-shell.8
new file mode 100644
index 0000000..f1b2aed
--- /dev/null
+++ b/doc/sphinx/_build/man/kea-shell.8
@@ -0,0 +1,140 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KEA-SHELL" "8" "Mar 22, 2024" "2.5.7" "Kea"
+.SH NAME
+kea-shell \- Text client for Control Agent process
+.SH SYNOPSIS
+.sp
+\fBkea\-shell\fP [\fB\-h\fP] [\fB\-v\fP] [\fB\-\-host\fP] [\fB\-\-port\fP] [\fB\-\-path\fP] [\fB\-\-ca\fP] [\fB\-\-cert\fP] [\fB\-\-key\fP] [\fB\-\-auth\-user\fP] [\fB\-\-auth\-password\fP] [\fB\-\-timeout\fP] [\fB\-\-service\fP] [command]
+.SH DESCRIPTION
+.sp
+The \fBkea\-shell\fP provides a REST client for the Kea Control Agent (CA).
+It takes commands as a command\-line parameter that is sent to the CA
+with proper JSON encapsulation. Optional arguments may be specified on
+the standard input. The request is sent via HTTP and a response is
+retrieved, displayed on the standard output. Basic HTTP authentication
+and HTTPS, i.e. TLS transport, are supported.
+.SH ARGUMENTS
+.sp
+The arguments are as follows:
+.INDENT 0.0
+.TP
+.B \fB\-h\fP
+Displays help regarding command\-line parameters.
+.TP
+.B \fB\-v\fP
+Displays the version.
+.TP
+.B \fB\-\-host\fP
+Specifies the host to connect to. The Control Agent must be running at the
+specified host. If not specified, 127.0.0.1 is used.
+.TP
+.B \fB\-\-port\fP
+Specifies the TCP port to connect to. Control Agent must be listening
+at the specified port. If not specified, 8000 is used.
+.TP
+.B \fB\-\-path\fP
+Specifies the path in the URL to connect to. If not specified, an empty
+path is used. As Control Agent listens at the empty path, this
+parameter is useful only with a reverse proxy.
+.TP
+.B \fB\-\-ca\fP
+Specifies the file or directory name of the Certification Authority.
+If not specified, HTTPS is not used.
+.TP
+.B \fB\-\-cert\fP
+Specifies the file name of the user end\-entity public key certificate.
+If specified, the file name of the user key must also be specified.
+.TP
+.B \fB\-\-key\fP
+Specifies the file name of the user key file. If specified, the file
+name of the user certificate must also be specified.
+Encrypted key files are not supported.
+.TP
+.B \fB\-\-auth\-user\fP
+Specifies the user ID for basic HTTP authentication. If not specified,
+or specified as the empty string, authentication is not used.
+.TP
+.B \fB\-\-auth\-password\fP
+Specifies the password for basic HTTP authentication. If not specified
+but the user ID is specified, an empty password is used.
+.TP
+.B \fB\-\-timeout\fP
+Specifies the connection timeout, in seconds. The default is 10.
+.TP
+.B \fB\-\-service\fP
+Specifies the service that is the target of a command. If not
+specified, the Control Agent itself is targeted. May be used more than once
+to specify multiple targets.
+.TP
+.B \fBcommand\fP
+Specifies the command to be sent to the CA. If not specified,
+\fBlist\-commands\fP is used.
+.UNINDENT
+.SH DOCUMENTATION
+.sp
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software \- compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+\fI\%https://kea.readthedocs.io\fP\&.
+.sp
+Kea source code is documented in the Kea Developer\(aqs Guide,
+available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&.
+.sp
+The Kea project website is available at \fI\%https://kea.isc.org\fP\&.
+.SH MAILING LISTS AND SUPPORT
+.sp
+There are two public mailing lists available for the Kea project. \fBkea\-users\fP
+(kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP
+(kea\-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+\fI\%https://lists.isc.org\fP\&. The community provides best\-effort support
+on both of those lists.
+.sp
+ISC provides professional support for Kea services. See
+\fI\%https://www.isc.org/kea/\fP for details.
+.SH HISTORY
+.sp
+The \fBkea\-shell\fP was first coded in March 2017 by Tomek Mrugalski.
+.SH SEE ALSO
+.sp
+\fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP, \fBkea\-dhcp\-ddns(8)\fP,
+\fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkeactrl(8)\fP,
+\fBperfdhcp(8)\fP, \fBkea\-lfc(8)\fP, Kea Administrator Reference Manual.
+.SH AUTHOR
+Internet Systems Consortium
+.SH COPYRIGHT
+2019-2024, Internet Systems Consortium
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/sphinx/_build/man/keactrl.8 b/doc/sphinx/_build/man/keactrl.8
new file mode 100644
index 0000000..22a9b3e
--- /dev/null
+++ b/doc/sphinx/_build/man/keactrl.8
@@ -0,0 +1,143 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KEACTRL" "8" "Mar 22, 2024" "2.5.7" "Kea"
+.SH NAME
+keactrl \- Shell script for managing Kea
+.SH SYNOPSIS
+.sp
+\fBkeactrl\fP [\fBcommand\fP] [\fB\-c\fP keactrl\-config\-file] [\fB\-s\fP server[,server,...]] [\fB\-v\fP]
+.SH DESCRIPTION
+.sp
+\fBkeactrl\fP is a shell script which controls the startup, shutdown, and
+reconfiguration of the Kea servers (\fBkea\-dhcp4\fP, \fBkea\-dhcp6\fP,
+\fBkea\-dhcp\-ddns\fP, \fBkea\-ctrl\-agent\fP, and \fBkea\-netconf\fP). It also
+provides a way to check the current status of the servers and
+determine the configuration files in use.
+.SH CONFIGURATION FILE
+.sp
+Depending on the user\(aqs requirements, not all of the available servers need be run.
+The \fBkeactrl\fP configuration file specifies which servers are enabled and which
+are disabled. By default the configuration file is
+\fB[kea\-install\-dir]/etc/kea/keactrl.conf\fP\&.
+.sp
+See the Kea Administrator Reference Manual for documentation of the
+parameters in the \fBkeactrl\fP configuration file.
+.SH OPTIONS
+.INDENT 0.0
+.TP
+.B \fBcommand\fP
+Specifies the command to be issued to the servers. It can be one of the following:
+.INDENT 7.0
+.TP
+.B \fBstart\fP
+Starts the servers.
+.TP
+.B \fBstop\fP
+Stops the servers.
+.TP
+.B \fBreload\fP
+Instructs the servers to re\-read the Kea configuration file. This
+command is not supported by the NETCONF agent.
+.TP
+.B \fBstatus\fP
+Prints the status of the servers.
+.UNINDENT
+.TP
+.B \fB\-c|\-\-ctrl\-config keactrl\-config\-file\fP
+Specifies the \fBkeactrl\fP configuration file. Without this switch,
+\fBkeactrl\fP uses the file
+\fB[kea\-install\-dir]/etc/kea/keactrl.conf\fP\&.
+.TP
+.B \fB\-s|\-\-server server[,server,...]\fP
+Specifies a subset of the enabled servers to which the command should
+be issued. The list of servers should be separated by commas, with no
+intervening spaces. Acceptable values are:
+.INDENT 7.0
+.TP
+.B \fBdhcp4\fP
+DHCPv4 server (\fBkea\-dhcp4\fP).
+.TP
+.B \fBdhcp6\fP
+DHCPv6 server (\fBkea\-dhcp6\fP).
+.TP
+.B \fBdhcp_ddns\fP
+DHCP DDNS server (\fBkea\-dhcp\-ddns\fP).
+.TP
+.B \fBctrl_agent\fP
+Control Agent (\fBkea\-ctrl\-agent\fP).
+.TP
+.B \fBnetconf\fP
+NETCONF agent (\fBkea\-netconf\fP).
+.TP
+.B \fBall\fP
+All servers, including NETCONF if it was configured to be
+built. This is the default.
+.UNINDENT
+.TP
+.B \fB\-v|\-\-version\fP
+Prints the \fBkeactrl\fP version and quits.
+.UNINDENT
+.SH DOCUMENTATION
+.sp
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software \- compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+\fI\%https://kea.readthedocs.io\fP\&.
+.sp
+Kea source code is documented in the Kea Developer\(aqs Guide,
+available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&.
+.sp
+The Kea project website is available at \fI\%https://kea.isc.org\fP\&.
+.SH MAILING LISTS AND SUPPORT
+.sp
+There are two public mailing lists available for the Kea project. \fBkea\-users\fP
+(kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP
+(kea\-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+\fI\%https://lists.isc.org\fP\&. The community provides best\-effort support
+on both of those lists.
+.sp
+ISC provides professional support for Kea services. See
+\fI\%https://www.isc.org/kea/\fP for details.
+.SH SEE ALSO
+.sp
+\fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP, \fBkea\-dhcp\-ddns(8)\fP,
+\fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkea\-netconf(8)\fP,
+\fBperfdhcp(8)\fP, \fBkea\-lfc(8)\fP, Kea Administrator Reference Manual.
+.SH AUTHOR
+Internet Systems Consortium
+.SH COPYRIGHT
+2019-2024, Internet Systems Consortium
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/sphinx/_build/man/perfdhcp.8 b/doc/sphinx/_build/man/perfdhcp.8
new file mode 100644
index 0000000..46544fa
--- /dev/null
+++ b/doc/sphinx/_build/man/perfdhcp.8
@@ -0,0 +1,596 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "PERFDHCP" "8" "Mar 22, 2024" "2.5.7" "Kea"
+.SH NAME
+perfdhcp \- DHCP benchmarking tool
+.SH SYNOPSIS
+.sp
+\fBperfdhcp\fP [\fB\-1\fP] [\fB\-4\fP | \fB\-6\fP] [\fB\-A\fP encapsulation\-level] [\fB\-b\fP base] [\fB\-B\fP] [\fB\-c\fP] [\fB\-C\fP separator] [\fB\-d\fP drop\-time] [\fB\-D\fP max\-drop] [\-e lease\-type] [\fB\-E\fP time\-offset] [\fB\-f\fP renew\-rate] [\fB\-F\fP release\-rate] [\fB\-g\fP thread\-mode] [\fB\-h\fP] [\fB\-i\fP] [\fB\-I\fP ip\-offset] [\fB\-J\fP remote\-address\-list\-file] [\fB\-l\fP local\-address|interface] [\fB\-L\fP local\-port] [\fB\-M\fP mac\-list\-file] [\fB\-n\fP num\-request] [\fB\-N\fP remote\-port] [\fB\-O\fP random\-offset] [\fB\-o\fP code,hexstring] [\fB\-\-or\fP encapsulation\-level:code,hexstring] [\fB\-p\fP test\-period] [\fB\-P\fP preload] [\fB\-r\fP rate] [\fB\-R\fP num\-clients] [\fB\-s\fP seed] [\fB\-S\fP srvid\-offset] [\fB\-\-scenario\fP name] [\fB\-t\fP report] [\fB\-T\fP template\-file] [\fB\-u\fP] [\fB\-v\fP] [\fB\-W\fP exit\-wait\-time] [\fB\-w\fP script_name] [\fB\-x\fP diagnostic\-selector] [\fB\-X\fP xid\-offset] [server]
+.SH DESCRIPTION
+.sp
+\fBperfdhcp\fP is a DHCP benchmarking tool. It provides a way to measure
+the performance of DHCP servers by generating large amounts of traffic
+from multiple simulated clients. It is able to test both IPv4 and IPv6
+servers, and provides statistics concerning response times and the
+number of requests that are dropped.
+.sp
+The tool supports two different scenarios, which offer certain behaviors to be tested.
+By default (the basic scenario), tests are run using the full four\-packet exchange sequence
+(DORA for DHCPv4, SARR for DHCPv6). An option is provided to run tests
+using the initial two\-packet exchange (DO and SA) instead. It is also
+possible to configure \fBperfdhcp\fP to send DHCPv6 RENEW and RELEASE messages
+at a specified rate, in parallel with the DHCPv6 four\-way exchanges. By
+default, if there is no response received with one second, a response is
+considered lost and \fBperfdhcp\fP continues with other transactions.
+.sp
+A second scenario, called avalanche, is selected via \fB\-\-scenario avalanche\fP\&.
+It first sends the number of Discovery or Solicit messages specified by the \fB\-R\fP option; then
+a retransmission (with an exponential back\-off mechanism) is used for each simulated client, until all requests are
+answered. It generates a report when all clients receive their addresses, or when
+it is manually stopped. This scenario attempts to replicate a
+case where the server is not able to handle the traffic swiftly
+enough. Real clients will assume the packet or response was lost
+and will retransmit, further increasing DHCP traffic. This is
+sometimes called an avalanche effect, thus the scenario name.
+Option \fB\-p\fP is ignored in the avalanche scenario.
+.sp
+When running a performance test, \fBperfdhcp\fP exchanges packets with
+the server under test as quickly as possible, unless the \fB\-r\fP parameter is used to
+limit the request rate. The length of the test can be limited by setting
+a threshold on any or all of the number of requests made by
+\fBperfdhcp\fP, the elapsed time, or the number of requests dropped by the
+server.
+.SH TEMPLATES
+.sp
+To allow the contents of packets sent to the server to be customized,
+\fBperfdhcp\fP allows the specification of template files that determine
+the contents of the packets. For example, the customized packet may
+contain a DHCPv6 ORO to request a set of options to be returned by the
+server, or it may contain the Client FQDN option to request that the server
+perform DNS updates. This may be used to discover performance
+bottlenecks for different server configurations (e.g. DDNS enabled or
+disabled).
+.sp
+Up to two template files can be specified on the command line, with each file
+representing the contents of a particular type of packet, and the type being
+determined by the test being carried out. For example, if testing
+DHCPv6:
+.INDENT 0.0
+.IP \(bu 2
+With no template files specified on the command line, \fBperfdhcp\fP
+generates both Solicit and Request packets.
+.IP \(bu 2
+With one template file specified, that file is used as the
+pattern for Solicit packets: \fBperfdhcp\fP generates the Request
+packets.
+.IP \(bu 2
+With two template files given on the command line, the first is
+used as the pattern for Solicit packets, and the second as the pattern
+for Request packets.
+.UNINDENT
+.sp
+(A similar determination applies to DHCPv4\(aqs DHCPDISCOVER and DHCPREQUEST
+packets.)
+.sp
+The template file holds the DHCP packet, represented as a stream of ASCII
+hexadecimal digits; it excludes any IP/UDP stack headers. The
+template file must not contain any characters other than hexadecimal
+digits and spaces. Spaces are discarded when the template file is parsed;
+in the file, \fB12B4\fP is the same as \fB12 B4\fP, which is the same as
+\fB1 2 B 4\fP\&.
+.sp
+The template files should be used in conjunction with the command\-line
+parameters which specify offsets of the data fields being modified in
+outbound packets. For example, the \fB\-E time\-offset\fP switch specifies
+the offset of the DHCPv6 Elapsed Time option in the packet template.
+If the offset is specified, \fBperfdhcp\fP injects the current elapsed\-time
+value into this field before sending the packet to the server.
+.sp
+In many scenarios, \fBperfdhcp\fP needs to simulate multiple clients,
+each having a unique client identifier. Since packets for each client are
+generated from the same template file, it is necessary to randomize the
+client identifier (or HW address in DHCPv4) in the packet created from
+it. The \fB\-O random\-offset\fP option allows specification of the offset in
+the template where randomization should be performed. It is important to
+note that this offset points to the end (not the beginning) of the
+client identifier (or HW address field). The number of bytes being
+randomized depends on the number of simulated clients. If the number of
+simulated clients is between 1 and 255, only one byte (to which the
+randomization offset points) is randomized. If the number of
+simulated clients is between 256 and 65535, two bytes are
+randomized. Note that the last two bytes of the client identifier are
+randomized in this case: the byte which the randomization offset parameter
+points to, and the one which precedes it (random\-offset \- 1). If the
+number of simulated clients exceeds 65535, three bytes are
+randomized, and so on.
+.sp
+\fBperfdhcp\fP can simulate traffic from multiple subnets by enabling option
+\fB\-J\fP and passing a path to a file that contains v4 or v6 addresses to be
+used as relays in generated messages. That enables testing of vast numbers
+of Kea shared networks. While testing DHCPv4, Kea should be started with the
+\fBKEA_TEST_SEND_RESPONSES_TO_SOURCE\fP environment variable, to force Kea
+to send generated messages to the source address of the incoming packet.
+.sp
+Templates may currently be used to generate packets being sent to the
+server in 4\-way exchanges, i.e. Solicit, Request (DHCPv6) and DHCPDISCOVER,
+DHCPREQUEST (DHCPv4). They cannot be used when Renew or DHCPRELEASE packets are
+being sent.
+.SH OPTIONS
+.INDENT 0.0
+.TP
+.B \fB\-1\fP
+Takes the \fBserver\-id\fP option from the first received message.
+.TP
+.B \fB\-4\fP
+Establishes DHCPv4 operation; this is the default. It is incompatible with the
+\fB\-6\fP option.
+.TP
+.B \fB\-6\fP
+Establishes DHCPv6 operation. It is incompatible with the \fB\-4\fP option.
+.TP
+.B \fB\-b basetype=value\fP
+Indicates the base MAC or DUID used to simulate different clients. The basetype
+may be \(dqmac\(dq or \(dqduid\(dq. (The keyword \(dqether\(dq may alternatively used
+for MAC.) The \fB\-b\fP option can be specified multiple times. The MAC
+address must consist of six octets separated by single (:) or double
+(::) colons; for example: mac=00:0c:01:02:03:04. The DUID value is a
+hexadecimal string; it must be at least six octets long and not
+longer than 64 bytes, and the length must be less than 128
+hexadecimal digits. For example: duid=0101010101010101010110111F14.
+.TP
+.B \fB\-d drop\-time\fP
+Specifies the time after which a request is treated as having been
+lost. The value is given in seconds and may contain a fractional
+component. The default is 1.
+.TP
+.B \fB\-e lease\-type\fP
+Specifies the type of lease being requested from the server. It may
+be one of the following:
+.INDENT 7.0
+.TP
+.B \fBaddress\-only\fP
+Only regular addresses (v4 or v6) are requested.
+.TP
+.B \fBprefix\-only\fP
+Only IPv6 prefixes are requested.
+.TP
+.B \fBaddress\-and\-prefix\fP
+Both IPv6 addresses and prefixes are requested.
+.UNINDENT
+.sp
+The \fB\-e prefix\-only\fP and \fB\-e address\-and\-prefix\fP forms may not be used
+with the \fB\-4\fP option.
+.TP
+.B \fB\-F release\-rate\fP
+Specifies the rate at which DHCPv4 DHCPRELEASE or DHCPv6 Release requests are sent to a server. This value
+is only valid when used in conjunction with the exchange rate (given
+by \fB\-r rate\fP). Furthermore, the sum of this value and the renew\-rate
+(given by \fB\-f rate\fP) must be equal to or less than the exchange
+rate value.
+.TP
+.B \fB\-f renew\-rate\fP
+Specifies the rate at which DHCPv4 DHCPREQUEST or DHCPv6 Renew requests are sent to a server.
+This value is only valid when used in conjunction with the exchange
+rate (given by \fB\-r rate\fP). Furthermore, the sum of this value and
+the release\-rate (given by \fB\-F rate\fP) must be equal to or less than the
+exchange rate.
+.TP
+.B \fB\-g thread\-mode\fP
+Allows selection of thread\-mode, which can be either \fBsingle\fP or \fBmulti\fP\&. In multi\-thread mode,
+packets are received in a separate thread, which allows better
+utilisation of CPUs. In a single\-CPU system it is better to run in one
+thread, to avoid threads blocking each other. If more than one CPU is
+present in the system, multi\-thread mode is the default; otherwise
+single\-thread is the default.
+.TP
+.B \fB\-h\fP
+Prints help and exits.
+.TP
+.B \fB\-i\fP
+Performs only the initial part of the exchange: DISCOVER\-OFFER if \fB\-4\fP is
+selected, Solicit\-Advertise if \fB\-6\fP is chosen.
+.sp
+\fB\-i\fP is incompatible with the following options: \fB\-1\fP, \fB\-d\fP,
+\fB\-D\fP, \fB\-E\fP, \fB\-S\fP, \fB\-I\fP and \fB\-F\fP\&. In addition, it cannot be
+used with multiple instances of \fB\-O\fP, \fB\-T\fP, and \fB\-X\fP\&.
+.TP
+.B \fB\-J remote\-address\-list\-file\fP
+Specifies a text file that includes multiple addresses, and is
+designed to test shared networks. If provided, \fBperfdhcp\fP
+randomly chooses one of the addresses for each exchange, to generate traffic
+from multiple subnets. When testing DHCPv4, it
+should be started with the \fBKEA_TEST_SEND_RESPONSES_TO_SOURCE=ENABLE\fP
+environment variable; otherwise, \fBperfdhcp\fP will not be able to receive responses.
+.TP
+.B \fB\-l local\-addr|interface\fP
+For DHCPv4 operation, specifies the local hostname/address to use when
+communicating with the server. By default, the interface address
+through which traffic would normally be routed to the server is used.
+For DHCPv6 operation, specifies the name of the network interface
+through which exchanges are initiated.
+.TP
+.B \fB\-L local\-port\fP
+Specifies the local port to use. This must be zero or a positive
+integer up to 65535. A value of 0 (the default) allows \fBperfdhcp\fP
+to choose its own port.
+.TP
+.B \fB\-M mac\-list\-file\fP
+Specifies a text file containing a list of MAC addresses, one per line. If
+provided, a MAC address is chosen randomly from this list for
+every new exchange. In DHCPv6, MAC addresses are used to
+generate DUID\-LLs. This parameter must not be used in conjunction
+with the \fB\-b\fP parameter.
+.TP
+.B \fB\-N remote\-port\fP
+Specifies the remote port to use. This must be zero or a positive
+integer up to 65535. A value of 0 (the default) allows \fBperfdhcp\fP
+to choose the standard service port.
+.TP
+.B \fB\-o code,hexstring\fP
+Forces \fBperfdhcp\fP to insert the specified extra option (or options if
+used several times) into packets being transmitted. The code
+specifies the option code and the hexstring is a hexadecimal string that
+defines the content of the option. Care should be taken as \fBperfdhcp\fP
+does not offer any kind of logic behind those options; they are simply
+inserted into packets and sent as is. Be careful not to duplicate
+options that are already inserted. For example, to insert client
+class identifier (option code 60) with a string \(dqdocsis\(dq, use
+\(dq\-o 60,646f63736973\(dq. The \fB\-o\fP may be used multiple times. It is
+necessary to specify the protocol family (either \fB\-4\fP or \fB\-6\fP) before
+using \fB\-o\fP\&.
+.TP
+.B \fB\-P preload\fP
+Initiates preload exchanges back\-to\-back at startup. Must be 0
+(the default) or a positive integer.
+.TP
+.B \fB\-r rate\fP
+Initiates the rate of DORA/SARR (or if \fB\-i\fP is given, DO/SA) exchanges per
+second. A periodic report is generated showing the number of
+exchanges which were not completed, as well as the average response
+latency. The program continues until interrupted, at which point a
+final report is generated.
+.TP
+.B \fB\-R num\-clients\fP
+Specifies how many different clients are used. With a value of 1 (the
+default), all requests appear to come from the same client.
+Must be a positive number.
+.TP
+.B \fB\-s seed\fP
+Specifies the seed for randomization, making runs of \fBperfdhcp\fP
+repeatable. This must be 0 or a positive integer. The value 0 means that a
+seed is not used; this is the default.
+.TP
+.B \fB\-\-scenario name\fP
+Specifies the type of scenario, and can be \fBbasic\fP (the default) or \fBavalanche\fP\&.
+.TP
+.B \fB\-T template\-file\fP
+Specifies a file containing the template to use as a stream of
+hexadecimal digits. This may be specified up to two times and
+controls the contents of the packets sent (see the \(dqTemplates\(dq
+section above).
+.TP
+.B \fB\-u\fP
+Enables checks for address uniqueness. The lease valid\-lifetime should not be shorter
+than the test duration, and clients should not request an address more than once without
+releasing it.
+.TP
+.B \fB\-v\fP
+Prints the version of this program.
+.TP
+.B \fB\-W exit\-wait\-time\fP
+Specifies the exit\-wait\-time parameter, which causes \fBperfdhcp\fP to wait for
+a certain amount of time after an exit condition has been met, to receive all
+packets without sending any new packets. Expressed in microseconds.
+If not specified, 0 is used (i.e. exit immediately after exit
+conditions are met).
+.TP
+.B \fB\-w script_name\fP
+Specifies the name of the script to be run before/after \fBperfdhcp\fP\&.
+When called, the script is passed a single parameter, either \(dqstart\(dq or
+\(dqstop\(dq, indicating whether it is being called before or after \fBperfdhcp\fP\&.
+.TP
+.B \fB\-x diagnostic\-selector\fP
+Includes extended diagnostics in the output. This is a
+string of single keywords specifying the operations for which verbose
+output is desired. The selector key letters are:
+.INDENT 7.0
+.TP
+.B \fBa\fP
+Prints the decoded command\-line arguments.
+.TP
+.B \fBe\fP
+Prints the exit reason.
+.TP
+.B \fBi\fP
+Prints the rate\-processing details.
+.TP
+.B \fBl\fP
+Prints the received leases.
+.TP
+.B \fBs\fP
+Prints the first server ID.
+.TP
+.B \fBt\fP
+When finished, prints timers of all successful exchanges.
+.TP
+.B \fBT\fP
+When finished, prints templates.
+.UNINDENT
+.TP
+.B \fB\-Y seconds\fP
+Time in seconds after which \fBperfdhcp\fP starts simulating the client waiting longer for server responses. This increases the
+\fBsecs\fP field in DHCPv4 and sends increased values in the \fBElapsed Time\fP option in DHCPv6. Must be used with \fB\-y\fP\&.
+.TP
+.B \fB\-y seconds\fP
+Time in seconds during which \fBperfdhcp\fP simulates the client waiting longer for server responses. This increases
+the \fBsecs\fP field in DHCPv4 and sends increased values in the \fBElapsed Time\fP option in DHCPv6. Must be used with \fB\-Y\fP\&.
+.UNINDENT
+.SH DHCPV4-ONLY OPTIONS
+.sp
+The following options only apply for DHCPv4 (i.e. when \fB\-4\fP is given).
+.INDENT 0.0
+.TP
+.B \fB\-B\fP
+Forces broadcast handling.
+.UNINDENT
+.SH DHCPV6-ONLY OPTIONS
+.sp
+The following options only apply for DHCPv6 (i.e. when \fB\-6\fP is given).
+.INDENT 0.0
+.TP
+.B \fB\-c\fP
+Adds a rapid\-commit option (exchanges are Solicit\-Advertise).
+.TP
+.B \fB\-A encapsulation\-level\fP
+Specifies that relayed traffic must be generated. The argument
+specifies the level of encapsulation, i.e. how many relay agents are
+simulated. Currently the only supported encapsulation\-level value is
+1, which means that the generated traffic is equivalent to the amount of
+traffic passing through a single relay agent.
+.TP
+.B \fB\-\-or encapsulation\-level:code,hexstring\fP
+This option is very similar to \fB\-o\fP, only that it forces \fBperfdhcp\fP
+to insert the specified extra option (or options if used several times)
+into relayed DHCPv6 message at given level of encapsulation (currently
+the only supported encapsulation\-level value is 1). The code
+specifies the option code and the hexstring is a hexadecimal string that
+defines the content of the option. Care should be taken as \fBperfdhcp\fP
+does not offer any kind of logic behind those options; they are simply
+inserted into packets and sent as is. Please notice that \fBencapsulation\-level:\fP
+is optional and if omitted, default encapsulation\-level value 1 is used.
+For example, to insert Subscriber identifier (option code 38) with a
+value 1234 at first level of encapsulation, use \fB\-\-or 38,31323334\fP
+or \fB\-\-or 1:38,31323334\fP\&. The \fB\-\-or\fP may be used multiple times.
+It must be used together with \fB\-A\fP\&.
+.UNINDENT
+.SH TEMPLATE-RELATED OPTIONS
+.sp
+The following options may only be used in conjunction with \fB\-T\fP and
+control how \fBperfdhcp\fP modifies the template. The options may be
+specified multiple times on the command line; each occurrence affects
+the corresponding template file (see \(dqTemplates\(dq above).
+.INDENT 0.0
+.TP
+.B \fB\-E time\-offset\fP
+Specifies the offset of the \fBsecs\fP field (DHCPv4) or \fBElapsed Time\fP option (DHCPv6) in the
+second (i.e. Request) template; must be 0 or a positive integer. A
+value of 0 disables this.
+.TP
+.B \fB\-I ip\-offset\fP
+Specifies the offset of the IP address (DHCPv4) in the \fBrequested\-ip\fP
+option or \fBIA_NA\fP option (DHCPv6) in the second (Request) template.
+.TP
+.B \fB\-O random\-offset\fP
+Specifies the offset of the last octet to randomize in the template. This
+must be an integer greater than 3. The \fB\-T\fP switch must be given to
+use this option.
+.TP
+.B \fB\-S srvid\-offset\fP
+Specifies the offset of the \fBserver\-id\fP option in the second (Request) template.
+This must be a positive integer, and the switch can only be used
+when the template option (\fB\-T\fP) is also given.
+.TP
+.B \fB\-X xid\-offset\fP
+Specifies the offset of the transaction ID (xid) in the template. This must be a
+positive integer, and the switch can only be used when the template
+option (\fB\-T\fP) is also given.
+.UNINDENT
+.SH OPTIONS CONTROLLING A TEST
+.INDENT 0.0
+.TP
+.B \fB\-D max\-drop\fP
+Aborts the test immediately if \(dqmax\-drop\(dq requests have been dropped.
+Use \fB\-D 1\fP to abort if even a single request has
+been dropped. \(dqmax\-drop\(dq must be a positive integer. If \(dqmax\-drop\(dq
+includes the suffix \fB%\fP, it specifies the maximum percentage of
+requests that may be dropped before aborting. In this case, testing of
+the threshold begins after 10 requests are expected to have been
+received.
+.TP
+.B \fB\-n num\-requests\fP
+Initiates \(dqnum\-request\(dq transactions. No report is generated until all
+transactions have been initiated/waited\-for, after which a report is
+generated and the program terminates.
+.TP
+.B \fB\-p test\-period\fP
+Sends requests for \(dqtest\-period\(dq, which is specified in the same manner
+as \fB\-d\fP\&. This can be used as an alternative to \fB\-n\fP, or both
+options can be given, in which case the testing is completed when
+either limit is reached.
+.TP
+.B \fB\-t interval\fP
+Sets the delay (in seconds) between two successive reports.
+.TP
+.B \fB\-C separator\fP
+Suppresses the preliminary output and causes the interim data to
+only contain the values delimited by \fBseparator\fP\&. Used in
+conjunction with \fB\-t\fP to produce easily parsable
+reports at \fB\-t\fP intervals.
+.UNINDENT
+.SH ARGUMENTS
+.INDENT 0.0
+.TP
+.B \fBserver\fP
+Indicates the server to test, specified as an IP address. In the DHCPv6 case, the
+special name \fBall\fP can be used to refer to
+\fBAll_DHCP_Relay_Agents_and_Servers\fP (the multicast address FF02::1:2),
+or the special name \fBservers\fP to refer to \fBAll_DHCP_Servers\fP (the
+multicast address FF05::1:3). The server is mandatory except where
+the \fB\-l\fP option is given to specify an interface, in which case it
+defaults to \fBall\fP\&.
+.UNINDENT
+.SH ERRORS
+.sp
+\fBperfdhcp\fP can report the following errors in the packet exchange:
+.INDENT 0.0
+.TP
+.B tooshort
+A message was received that was too short.
+.TP
+.B orphans
+A message was received which does not match one sent to the server (i.e.
+it is a duplicate message, a message that has arrived after an
+excessive delay, or one that is just not recognized).
+.TP
+.B locallimit
+Local system limits have been reached when sending a message.
+.UNINDENT
+.SH EXIT STATUS
+.sp
+\fBperfdhcp\fP exits with one of the following status codes:
+.INDENT 0.0
+.TP
+.B 0
+Success.
+.TP
+.B 1
+General error.
+.TP
+.B 2
+Error in command\-line arguments.
+.TP
+.B 3
+No general failures in operation, but one or more exchanges were
+unsuccessful.
+.UNINDENT
+.SH USAGE EXAMPLES
+.sp
+Here is an example that simulates regular DHCPv4 traffic of 100 DHCPv4 devices (\-R 100),
+10 packets per second (\-r 10), shows the query/response rate details (\-xi),
+shows a report every 2 seconds (\-t 2), and sends the packets to the IP 192.0.2.1:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+sudo perfdhcp \-xi \-t 2 \-r 10 \-R 100 192.0.2.1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Here\(aqs a similar case, but for DHCPv6. Note that the DHCPv6 protocol uses link\-local
+addresses, so the interface (eth0 in this example) must be specified on which to send the
+traffic. \fBall\fP is a convenience alias for \fBAll_DHCP_Relay_Agents_and_Servers\fP
+(the multicast address FF02::1:2). It is also possible to use the \fBservers\fP alias
+to refer to \fBAll_DHCP_Servers\fP (the multicast address FF05::1:3). The default is \fBall\fP\&.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+sudo perfdhcp \-6 \-xi \-t 1 \-r 1 \-R 10 \-l eth0 all
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+The following examples simulate normal DHCPv4 and DHCPv6 traffic that, after 3 seconds,
+starts pretending not to receive any responses from the server for 10 seconds. The
+DHCPv4 protocol signals this by an increased \fBsecs\fP field, while DHCPv6 uses the
+\fBElapsed Time\fP option. In real networks, this indicates that clients are not getting
+responses in a timely matter. This can be used to simulate some HA scenarios, as Kea
+uses the \fBsecs\fP field and \fBElapsed Time\fP option value as one of the indicators
+that the HA partner is not responding. When enabled with \fB\-y\fP and \fB\-Y\fP, the \fBsecs\fP
+and \fBElapsed Time\fP values increase steadily.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+sudo perfdhcp \-xi \-t 1 \-r 1 \-y 10 \-Y 3 192.0.2.1
+
+sudo perfdhcp \-6 \-xi \-t 1 \-r 1 \-y 10 \-Y 3 2001:db8::1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SH DOCUMENTATION
+.sp
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software \- compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+\fI\%https://kea.readthedocs.io\fP\&.
+.sp
+Kea source code is documented in the Kea Developer\(aqs Guide,
+available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&.
+.sp
+The Kea project website is available at \fI\%https://kea.isc.org\fP\&.
+.SH MAILING LISTS AND SUPPORT
+.sp
+There are two public mailing lists available for the Kea project. \fBkea\-users\fP
+(kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP
+(kea\-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+\fI\%https://lists.isc.org\fP\&. The community provides best\-effort support
+on both of those lists.
+.sp
+ISC provides professional support for Kea services. See
+\fI\%https://www.isc.org/kea/\fP for details.
+.SH HISTORY
+.sp
+The \fBperfdhcp\fP tool was initially coded in October 2011 by John
+DuBois, Francis Dupont, and Marcin Siodelski of ISC. Kea 1.0.0, which
+included \fBperfdhcp\fP, was released in December 2015.
+.SH SEE ALSO
+.sp
+\fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP, \fBkea\-dhcp\-ddns(8)\fP,
+\fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkea\-netconf(8)\fP,
+\fBkeactrl(8)\fP, \fBkea\-lfc(8)\fP, Kea Administrator Reference Manual.
+.SH AUTHOR
+Internet Systems Consortium
+.SH COPYRIGHT
+2019-2024, Internet Systems Consortium
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/sphinx/api-files.txt b/doc/sphinx/api-files.txt
new file mode 100644
index 0000000..e0019f1
--- /dev/null
+++ b/doc/sphinx/api-files.txt
@@ -0,0 +1,197 @@
+src/share/api/build-report.json
+src/share/api/cache-clear.json
+src/share/api/cache-flush.json
+src/share/api/cache-get-by-id.json
+src/share/api/cache-get.json
+src/share/api/cache-insert.json
+src/share/api/cache-load.json
+src/share/api/cache-remove.json
+src/share/api/cache-size.json
+src/share/api/cache-write.json
+src/share/api/class-add.json
+src/share/api/class-del.json
+src/share/api/class-get.json
+src/share/api/class-list.json
+src/share/api/class-update.json
+src/share/api/config-backend-pull.json
+src/share/api/config-get.json
+src/share/api/config-hash-get.json
+src/share/api/config-reload.json
+src/share/api/config-set.json
+src/share/api/config-test.json
+src/share/api/config-write.json
+src/share/api/dhcp-disable.json
+src/share/api/dhcp-enable.json
+src/share/api/extended-info4-upgrade.json
+src/share/api/extended-info6-upgrade.json
+src/share/api/gss-tsig-get-all.json
+src/share/api/gss-tsig-get.json
+src/share/api/gss-tsig-key-del.json
+src/share/api/gss-tsig-key-expire.json
+src/share/api/gss-tsig-key-get.json
+src/share/api/gss-tsig-list.json
+src/share/api/gss-tsig-purge-all.json
+src/share/api/gss-tsig-purge.json
+src/share/api/gss-tsig-rekey-all.json
+src/share/api/gss-tsig-rekey.json
+src/share/api/ha-continue.json
+src/share/api/ha-heartbeat.json
+src/share/api/ha-maintenance-cancel.json
+src/share/api/ha-maintenance-notify.json
+src/share/api/ha-maintenance-start.json
+src/share/api/ha-reset.json
+src/share/api/ha-scopes.json
+src/share/api/ha-sync-complete-notify.json
+src/share/api/ha-sync.json
+src/share/api/lease4-add.json
+src/share/api/lease4-del.json
+src/share/api/lease4-get-all.json
+src/share/api/lease4-get-by-client-id.json
+src/share/api/lease4-get-by-hostname.json
+src/share/api/lease4-get-by-hw-address.json
+src/share/api/lease4-get-page.json
+src/share/api/lease4-get.json
+src/share/api/lease4-resend-ddns.json
+src/share/api/lease4-update.json
+src/share/api/lease4-wipe.json
+src/share/api/lease4-write.json
+src/share/api/lease6-add.json
+src/share/api/lease6-bulk-apply.json
+src/share/api/lease6-del.json
+src/share/api/lease6-get-all.json
+src/share/api/lease6-get-by-duid.json
+src/share/api/lease6-get-by-hostname.json
+src/share/api/lease6-get-page.json
+src/share/api/lease6-get.json
+src/share/api/lease6-resend-ddns.json
+src/share/api/lease6-update.json
+src/share/api/lease6-wipe.json
+src/share/api/lease6-write.json
+src/share/api/leases-reclaim.json
+src/share/api/libreload.json
+src/share/api/list-commands.json
+src/share/api/network4-add.json
+src/share/api/network4-del.json
+src/share/api/network4-get.json
+src/share/api/network4-list.json
+src/share/api/network4-subnet-add.json
+src/share/api/network4-subnet-del.json
+src/share/api/network6-add.json
+src/share/api/network6-del.json
+src/share/api/network6-get.json
+src/share/api/network6-list.json
+src/share/api/network6-subnet-add.json
+src/share/api/network6-subnet-del.json
+src/share/api/remote-class4-del.json
+src/share/api/remote-class4-get-all.json
+src/share/api/remote-class4-get.json
+src/share/api/remote-class4-set.json
+src/share/api/remote-class6-del.json
+src/share/api/remote-class6-get-all.json
+src/share/api/remote-class6-get.json
+src/share/api/remote-class6-set.json
+src/share/api/remote-global-parameter4-del.json
+src/share/api/remote-global-parameter4-get-all.json
+src/share/api/remote-global-parameter4-get.json
+src/share/api/remote-global-parameter4-set.json
+src/share/api/remote-global-parameter6-del.json
+src/share/api/remote-global-parameter6-get-all.json
+src/share/api/remote-global-parameter6-get.json
+src/share/api/remote-global-parameter6-set.json
+src/share/api/remote-network4-del.json
+src/share/api/remote-network4-get.json
+src/share/api/remote-network4-list.json
+src/share/api/remote-network4-set.json
+src/share/api/remote-network6-del.json
+src/share/api/remote-network6-get.json
+src/share/api/remote-network6-list.json
+src/share/api/remote-network6-set.json
+src/share/api/remote-option-def4-del.json
+src/share/api/remote-option-def4-get-all.json
+src/share/api/remote-option-def4-get.json
+src/share/api/remote-option-def4-set.json
+src/share/api/remote-option-def6-del.json
+src/share/api/remote-option-def6-get-all.json
+src/share/api/remote-option-def6-get.json
+src/share/api/remote-option-def6-set.json
+src/share/api/remote-option4-global-del.json
+src/share/api/remote-option4-global-get-all.json
+src/share/api/remote-option4-global-get.json
+src/share/api/remote-option4-global-set.json
+src/share/api/remote-option4-network-del.json
+src/share/api/remote-option4-network-set.json
+src/share/api/remote-option4-pool-del.json
+src/share/api/remote-option4-pool-set.json
+src/share/api/remote-option4-subnet-del.json
+src/share/api/remote-option4-subnet-set.json
+src/share/api/remote-option6-global-del.json
+src/share/api/remote-option6-global-get-all.json
+src/share/api/remote-option6-global-get.json
+src/share/api/remote-option6-global-set.json
+src/share/api/remote-option6-network-del.json
+src/share/api/remote-option6-network-set.json
+src/share/api/remote-option6-pd-pool-del.json
+src/share/api/remote-option6-pd-pool-set.json
+src/share/api/remote-option6-pool-del.json
+src/share/api/remote-option6-pool-set.json
+src/share/api/remote-option6-subnet-del.json
+src/share/api/remote-option6-subnet-set.json
+src/share/api/remote-server4-del.json
+src/share/api/remote-server4-get-all.json
+src/share/api/remote-server4-get.json
+src/share/api/remote-server4-set.json
+src/share/api/remote-server6-del.json
+src/share/api/remote-server6-get-all.json
+src/share/api/remote-server6-get.json
+src/share/api/remote-server6-set.json
+src/share/api/remote-subnet4-del-by-id.json
+src/share/api/remote-subnet4-del-by-prefix.json
+src/share/api/remote-subnet4-get-by-id.json
+src/share/api/remote-subnet4-get-by-prefix.json
+src/share/api/remote-subnet4-list.json
+src/share/api/remote-subnet4-set.json
+src/share/api/remote-subnet6-del-by-id.json
+src/share/api/remote-subnet6-del-by-prefix.json
+src/share/api/remote-subnet6-get-by-id.json
+src/share/api/remote-subnet6-get-by-prefix.json
+src/share/api/remote-subnet6-list.json
+src/share/api/remote-subnet6-set.json
+src/share/api/reservation-add.json
+src/share/api/reservation-del.json
+src/share/api/reservation-get-all.json
+src/share/api/reservation-get-by-address.json
+src/share/api/reservation-get-by-hostname.json
+src/share/api/reservation-get-by-id.json
+src/share/api/reservation-get-page.json
+src/share/api/reservation-get.json
+src/share/api/reservation-update.json
+src/share/api/server-tag-get.json
+src/share/api/shutdown.json
+src/share/api/stat-lease4-get.json
+src/share/api/stat-lease6-get.json
+src/share/api/statistic-get-all.json
+src/share/api/statistic-get.json
+src/share/api/statistic-remove-all.json
+src/share/api/statistic-remove.json
+src/share/api/statistic-reset-all.json
+src/share/api/statistic-reset.json
+src/share/api/statistic-sample-age-set-all.json
+src/share/api/statistic-sample-age-set.json
+src/share/api/statistic-sample-count-set-all.json
+src/share/api/statistic-sample-count-set.json
+src/share/api/status-get.json
+src/share/api/subnet4-add.json
+src/share/api/subnet4-del.json
+src/share/api/subnet4-delta-add.json
+src/share/api/subnet4-delta-del.json
+src/share/api/subnet4-get.json
+src/share/api/subnet4-list.json
+src/share/api/subnet4-update.json
+src/share/api/subnet6-add.json
+src/share/api/subnet6-del.json
+src/share/api/subnet6-delta-add.json
+src/share/api/subnet6-delta-del.json
+src/share/api/subnet6-get.json
+src/share/api/subnet6-list.json
+src/share/api/subnet6-update.json
+src/share/api/version-get.json
diff --git a/doc/sphinx/api2doc.py b/doc/sphinx/api2doc.py
new file mode 100755
index 0000000..527e453
--- /dev/null
+++ b/doc/sphinx/api2doc.py
@@ -0,0 +1,209 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2019-2023 Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http:#mozilla.org/MPL/2.0/.
+
+# Produce API Reference
+# - reads *.json files (each file describes a single command)
+# - produces .rst file suitable for Sphinx as output
+
+import os
+import json
+import argparse
+import collections
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(description='Convert set of *.json files to .rst documentation format')
+ parser.add_argument('-o', '--output', help='Output file name (default to stdout).')
+ parser.add_argument('files', help='Input API .json files.', nargs='+')
+
+ args = parser.parse_args()
+ return args
+
+
+def read_input_files(files):
+ apis = {}
+ for f in files:
+ name = os.path.basename(f)[:-5]
+ # Skip special names starting with _ (such as _template.json)
+ if name.startswith('_'):
+ print("Skipping %s (starts with underscore)" % f)
+ continue
+ with open(f) as fp:
+ print("Processing %s" % f)
+ # use OrderedDict to preserve order of fields in cmd-syntax
+ try:
+ descr = json.load(fp, object_pairs_hook=collections.OrderedDict)
+ except:
+ print('\nError while processing %s\n\n' % f)
+ raise
+ assert name == descr['name']
+ apis[name] = descr
+
+ return apis
+
+
+def generate_rst(apis):
+ rst = '''.. _api:
+
+API Reference
+=============
+
+'''
+
+ daemons = {}
+ hooks = {}
+ for func in apis.values():
+ for dm in func['support']:
+ if dm not in daemons:
+ daemons[dm] = []
+ daemons[dm].append(func)
+
+ if 'hook' in func:
+ if func['hook'] not in hooks:
+ hooks[func['hook']] = []
+ hooks[func['hook']].append(func)
+
+ rst += 'Kea currently supports %d commands in %s daemons and %s hook libraries.\n\n' % (
+ len(apis),
+ ", ".join([':ref:`%s <commands-%s>`' % (m, m) for m in sorted(daemons.keys())]),
+ ", ".join([':ref:`%s <commands-%s>`' % (m, m) for m in sorted(hooks.keys())]))
+
+ for dm, funcs in sorted(daemons.items()):
+ rst += '.. _commands-%s:\n\n' % dm
+ rst += 'Commands supported by `%s` daemon: ' % dm
+ funcs = sorted([ ':ref:`%s <ref-%s>`' % (f['name'], f['name']) for f in funcs])
+ rst += ', '.join(funcs)
+ rst += '.\n\n'
+
+ for h, funcs in sorted(hooks.items()):
+ rst += '.. _commands-%s:\n\n' % h
+ rst += 'Commands supported by `%s` hook library: ' % h
+ funcs = sorted([ ':ref:`%s <ref-%s>`' % (f['name'], f['name']) for f in funcs])
+ rst += ', '.join(funcs)
+ rst += '.\n\n'
+
+ for func in sorted(apis.values(), key=lambda f: f['name']):
+ # "name" is visible in the ARM. "real_name" is used to provide links
+ # to commands. Keep both even if they're the same for when you want to
+ # make changes to "name" to change the way it's seen in the ARM.
+ name = func['name']
+ real_name = func['name']
+
+ rst += '.. _ref-%s:\n\n' % real_name
+ rst += name + '\n'
+ rst += '-' * len(name) + '\n\n'
+
+ # command overview
+ for brief_line in func['brief']:
+ rst += '%s\n' % brief_line
+ rst += '\n'
+
+ # command can be issued to the following daemons
+ rst += 'Supported by: '
+ rst += ', '.join(sorted([':ref:`%s <commands-%s>`' % (dm, dm) for dm in func['support']]))
+ rst += '\n\n'
+
+ # availability
+ rst += 'Availability: %s ' % func['avail']
+ rst += '(:ref:`%s <commands-%s>` hook library)' % (func['hook'], func['hook']) if 'hook' in func else '(built-in)'
+ rst += '\n\n'
+
+ # access
+ try:
+ access = func['access']
+ except:
+ print('\naccess missing in %s\n\n' % name)
+ raise
+ if not access in ['read', 'write']:
+ print('\nUnknown access %s in %s\n\n' % (access, name))
+ raise ValueError('access must be read or write')
+ rst += 'Access: %s *(parameter ignored in this Kea version)* \n\n' % access
+
+ # description and examples
+ rst += 'Description and examples: see :ref:`%s command <command-%s>`\n\n' % (name, real_name)
+
+ # command syntax
+ rst += 'Command syntax:\n\n'
+ rst += '::\n\n'
+ if 'cmd-syntax' in func:
+ cmd_syntaxes = [func['cmd-syntax']]
+ if isinstance(cmd_syntaxes, dict):
+ cmd_syntaxes = [cmd_syntax]
+ for cmd_syntax in cmd_syntaxes:
+ if 'comment' in cmd_syntax:
+ rst += cmd_syntax['comment']
+ rst += '\n\n'
+ del cmd_syntax['comment']
+
+ for line in cmd_syntax:
+ rst += ' %s\n' % line
+ else:
+ rst += ' {\n'
+ rst += ' "command": \"%s\"\n' % name
+ rst += ' }'
+ rst += '\n\n'
+
+ if 'cmd-comment' in func:
+ for l in func['cmd-comment']:
+ rst += "%s\n" % l
+ rst += '\n'
+
+ # response syntax
+ rst += 'Response syntax:\n\n'
+ rst += '::\n\n'
+ if 'resp-syntax' in func:
+ resp_syntaxes = [func['resp-syntax']]
+ if isinstance(resp_syntaxes, dict):
+ resp_syntaxes = [resp_syntax]
+ for resp_syntax in resp_syntaxes:
+
+ for line in resp_syntax:
+ rst += ' %s\n' % line
+
+ else:
+ rst += ' {\n'
+ rst += ' "result": <integer>,\n'
+ rst += ' "text": "<string>"\n'
+ rst += ' }'
+ rst += '\n\n'
+
+ if 'resp-comment' in func:
+ for resp_comment_line in func['resp-comment']:
+ rst += "%s\n" % resp_comment_line
+ rst += '\n\n'
+ else:
+ rst += 'Result is an integer representation of the status. Currently supported statuses are:\n\n'
+ rst += '- 0 - success\n'
+ rst += '- 1 - error\n'
+ rst += '- 2 - unsupported\n'
+ rst += '- 3 - empty (command was completed successfully, but no data was affected or returned)\n'
+ rst += '- 4 - conflict (command could not apply requested configuration changes because they were in conflict with the server state)\n\n'
+
+ return rst
+
+
+def generate(in_files, out_file):
+ apis = read_input_files(in_files)
+
+ rst = generate_rst(apis)
+
+ if out_file:
+ with open(out_file, 'w') as f:
+ f.write(rst)
+ print('Wrote generated RST content to: %s' % out_file)
+ else:
+ print(rst)
+
+
+def main():
+ args = parse_args()
+ generate(args.files, args.output)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/sphinx/arm/acknowledgments.rst b/doc/sphinx/arm/acknowledgments.rst
new file mode 100644
index 0000000..0df58dd
--- /dev/null
+++ b/doc/sphinx/arm/acknowledgments.rst
@@ -0,0 +1,29 @@
+Acknowledgments
+===============
+
+Kea is an open source project designed, developed, and maintained by
+Internet Systems Consortium, Inc, a 501(c)3 non-profit organization. ISC
+is primarily funded by revenues from support subscriptions for our open
+source, and we encourage all professional users to consider this option.
+To learn more, see \ https://www.isc.org/support/.
+
+We thank all the organizations and individuals who have helped to make
+Kea possible. `Comcast <https://www.comcast.com/>`__ and the Comcast
+Innovation Fund provided major support for the development of Kea's
+DHCPv4, DHCPv6, and DDNS modules. Mozilla funded initial work on the
+RESTful API via a MOSS award.
+
+Kea was initially implemented as a collection of applications within the
+BIND 10 framework. We thank the founding sponsors of the BIND 10
+project: `Afilias <https://www.afilias.info/>`__,
+`IIS.SE <https://www.iis.se/>`__,
+`Nominet <https://www.nominet.uk/>`__,
+`SIDN <https://www.sidn.nl/>`__, `JPRS <https://jprs.co.jp/>`__,
+and `CIRA <https://cira.ca/>`__; and additional sponsors
+`AFNIC <https://www.afnic.fr/>`__,
+`CNNIC <https://www.cnnic.net.cn/>`__, `CZ.NIC <https://www.nic.cz/>`__,
+`DENIC eG <https://www.denic.de/>`__,
+`Google <https://www.google.com/>`__, `RIPE
+NCC <https://www.ripe.net/>`__, `Registro.br <https://registro.br/>`__,
+`.nz Registry Services <https://nzrs.net.nz/>`__, and `Technical Center
+of Internet <https://www.tcinet.ru/>`__.
diff --git a/doc/sphinx/arm/admin.rst b/doc/sphinx/arm/admin.rst
new file mode 100644
index 0000000..aac1b81
--- /dev/null
+++ b/doc/sphinx/arm/admin.rst
@@ -0,0 +1,678 @@
+.. _admin:
+
+***************************
+Kea Database Administration
+***************************
+
+.. _kea-database-version:
+
+Databases and Schema Versions
+=============================
+
+Kea may be configured to use a database as storage for leases or as a
+source of servers' configurations and host reservations (i.e. static
+assignments of addresses, prefixes, options, etc.). As Kea is
+updated, new database schemas are introduced to facilitate new
+features and correct discovered issues with the existing schemas.
+
+Each version of Kea expects a particular schema structure and checks for this by
+examining the version of the database it is using. Separate version numbers are
+maintained for the schemas, independent of the version of Kea itself. It is
+possible that the schema version will stay the same through several Kea
+revisions; similarly, it is possible that the version of the schema may go up
+several revisions during a single Kea version upgrade. Versions for each backend
+type are also independent, so an increment in the MySQL backend version does not
+imply an increment in that of PostgreSQL.
+
+Schema versions are specified in a major.minor format. For the most recent
+versions, the minor version is always zero and only the major version is
+incremented.
+
+Historically, the minor version used to be incremented when backward-compatible
+changes were introduced to the schema: for example - when a new index is added.
+This was opposed to incrementing the major version which implied an incompatible
+schema change: for example - changing the type of an existing column. If Kea
+attempts to run on a schema that is too old, as indicated by a mismatched schema
+version, it will fail; administrative action is required to upgrade the schema.
+
+.. _kea-admin:
+
+The :iscman:`kea-admin` Tool
+============================
+
+To manage the databases, Kea provides the :iscman:`kea-admin` tool. It can
+initialize a new backend, check its version number, perform a backend
+upgrade, and dump lease data to a text file.
+
+:iscman:`kea-admin` takes two mandatory parameters: ``command`` and
+``backend``. Additional, non-mandatory options may be specified. The
+currently supported commands are:
+
+- ``db-init`` — initializes a new database schema. This is useful
+ during a new Kea installation. The database is initialized to the
+ latest version supported by the version of the software being installed.
+ Called automatically on startup or reconfiguration of Kea DHCP servers if
+ required.
+
+- ``db-version`` — reports the database backend version number. This
+ is not necessarily equal to the Kea version number, as each backend
+ has its own versioning scheme.
+
+- ``db-upgrade`` — conducts a database schema upgrade. This is
+ useful when upgrading Kea.
+
+- ``lease-dump`` — dumps the contents of the lease database (for MySQL or
+ PostgreSQL backends) to a CSV (comma-separated values) text file.
+
+ The first line of the file contains the column names. This can be used
+ as a way to switch from a database backend to a memfile backend.
+ Alternatively, it can be used as a diagnostic tool, so it provides a portable
+ form of the lease data.
+
+- ``lease-upload`` — uploads leases from a CSV (comma-separated values) text
+ file to a MySQL or a PostgreSQL lease database. The CSV file needs to be in
+ memfile format.
+
+``backend`` specifies the type of backend database. The currently
+supported types are:
+
+- ``memfile`` — lease information is stored on disk in a text file.
+
+- ``mysql`` — information is stored in a MySQL relational database.
+
+- ``pgsql`` — information is stored in a PostgreSQL relational
+ database.
+
+Additional parameters may be needed, depending on the setup and
+specific operation: username, password, and database name or the
+directory where specific files are located. See the appropriate manual
+page for details (``man 8 kea-admin``).
+
+.. _supported-databases:
+
+Supported Backends
+==================
+
+The following table presents the capabilities of available backends.
+Please refer to the specific sections dedicated to each backend to
+better understand their capabilities and limitations. Choosing the right
+backend is essential for the success of the deployment.
+
+.. table:: List of available backends
+
+ +---------------+----------------+----------------+---------------+
+ | Feature | Memfile | MySQL | PostgreSQL |
+ | | | | |
+ +===============+================+================+===============+
+ | Status | Stable | Stable | Stable |
+ | | | | |
+ +---------------+----------------+----------------+---------------+
+ | Data format | CSV file | SQL RMDB | SQL RMDB |
+ | | | | |
+ | | | | |
+ +---------------+----------------+----------------+---------------+
+ | Leases | yes | yes | yes |
+ +---------------+----------------+----------------+---------------+
+ | Host | no | yes | yes |
+ | reservations | | | |
+ | | | | |
+ +---------------+----------------+----------------+---------------+
+ | Options | no | yes | yes |
+ | defined on | | | |
+ | per host | | | |
+ | basis | | | |
+ +---------------+----------------+----------------+---------------+
+ | Configuration | no | yes | yes |
+ | backend | | | |
+ | | | | |
+ +---------------+----------------+----------------+---------------+
+
+Memfile
+-------
+
+The memfile backend is able to store lease information, but cannot
+store host reservation details; these must be stored in the
+configuration file. (There are no plans to add a host reservations
+storage capability to this backend.)
+
+No special initialization steps are necessary for the memfile backend.
+During the first run, both :iscman:`kea-dhcp4` and :iscman:`kea-dhcp6` create
+an empty lease file if one is not present. Necessary disk-write
+permission is required.
+
+.. _memfile-upgrade:
+
+Upgrading Memfile Lease Files From an Earlier Version of Kea
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are no special steps required to upgrade memfile lease files
+between versions of Kea. During startup, the
+servers check the schema version of the lease files against their
+own. If there is a mismatch, the servers automatically launch the
+LFC process to convert the files to the server's schema version. While
+this mechanism is primarily meant to ease the process of upgrading to
+newer versions of Kea, it can also be used for downgrading should the
+need arise. When upgrading, any values not present in the original lease
+files are assigned appropriate default values. When downgrading, any
+data present in the files but not in the server's schema are
+dropped. To convert the files manually prior to starting the
+servers, run the lease file cleanup (LFC) process. See :ref:`kea-lfc` for more information.
+
+.. _mysql-database:
+
+MySQL
+-----
+
+MySQL is able to store leases, host reservations, options defined on a
+per-host basis, and a subset of the server configuration parameters
+(serving as a configuration backend).
+
+.. _mysql-database-engine:
+
+MySQL 5.7 vs MySQL 8 vs MariaDB 10 and 11
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In our Kea performance testing, MySQL 8 shows a 60-90% drop in speed
+in comparison with MySQL 5.7.
+Due to the upcoming MySQL 5.7 EOL, we recommend using MariaDB instead of MySQL 8.
+
+MySQL 5.7, MySQL 8, MariaDB 10, and MariaDB 11 are fully compatible,
+interchangeable, and tested with Kea.
+
+.. _mysql-database-create:
+
+First-Time Creation of the MySQL Database
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Before preparing any Kea-specific database and tables, the MySQL database
+must be configured to use the system timezone. It is recommended to use UTC
+as the timezone for both the system and the MySQL database.
+
+To check the system timezone:
+
+ .. code-block:: console
+
+ date +%Z
+
+To check the MySQL timezone:
+
+ .. code-block:: mysql
+
+ mysql> SELECT @@system_time_zone;
+ mysql> SELECT @@global.time_zone;
+ mysql> SELECT @@session.time_zone;
+
+To configure the MySQL timezone for a specific server, please refer to the
+installed version documentation.
+
+Usually the setting is configured in the [mysqld] section in ``/etc/mysql/my.cnf``,
+``/etc/mysql/mysql.cnf``, ``/etc/mysql/mysqld.cnf``, or
+``/etc/mysql/mysql.conf.d/mysqld.cnf``.
+
+ .. code-block:: ini
+
+ [mysqld]
+ # using default-time-zone
+ default-time-zone='+00:00'
+
+ # or using timezone
+ timezone='UTC'
+
+When setting up the MySQL database for the first time, the
+database area must be created within MySQL, and the MySQL user ID under
+which Kea will access the database must be set up. This needs to be done manually,
+rather than via :iscman:`kea-admin`.
+
+To create the database:
+
+1. Log into MySQL as "root":
+
+ .. code-block:: console
+
+ $ mysql -u root -p
+ Enter password:
+ mysql>
+
+2. Create the MySQL database:
+
+ .. code-block:: mysql
+
+ mysql> CREATE DATABASE database_name;
+
+ (``database_name`` is the name chosen for the database.)
+
+3. Create the user under which Kea will access the database (and give it
+ a password), then grant it access to the database tables:
+
+ .. code-block:: mysql
+
+ mysql> CREATE USER 'user-name'@'localhost' IDENTIFIED BY 'password';
+ mysql> GRANT ALL ON database-name.* TO 'user-name'@'localhost';
+
+ (``user-name`` and ``password`` are the user ID and password used to
+ allow Kea access to the MySQL instance. All apostrophes in the
+ command lines above are required.)
+
+4. Create the database.
+
+ Exit the MySQL client
+
+ .. code-block:: mysql
+
+ mysql> quit
+ Bye
+
+ Then use the :iscman:`kea-admin` tool to create the database.
+
+ .. code-block:: console
+
+ $ kea-admin db-init mysql -u database-user -p database-password -n database-name
+
+ While it is possible to create the database from within the MySQL client, we recommend
+ using the :iscman:`kea-admin` tool as it performs some necessary validations to ensure Kea can
+ access the database at runtime. Among those checks is verification that the schema does not contain
+ any pre-existing tables; any pre-existing tables must be removed
+ manually. An additional check examines the user's ability to create functions and
+ triggers. The following error indicates that the user does not have the necessary
+ permissions to create functions or triggers:
+
+ .. code-block:: console
+
+ ERROR 1419 (HY000) at line 1: You do not have the SUPER privilege and binary logging is
+ enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
+ ERROR/kea-admin: mysql_can_create cannot trigger, check user permissions, mysql status = 1
+ mysql: [Warning] Using a password on the command line interface can be insecure.
+ ERROR/kea-admin: Create failed, the user, keatest, has insufficient privileges.
+
+ The simplest way around this is to set the global MySQL variable,
+ ``log_bin_trust_function_creators``, to 1 via the MySQL client.
+ Note this must be done as a user with SUPER privileges:
+
+ .. code-block:: mysql
+
+ mysql> set @@global.log_bin_trust_function_creators = 1;
+ Query OK, 0 rows affected (0.00 sec)
+
+ To create the database with MySQL directly, follow these steps:
+
+ .. code-block:: mysql
+
+ mysql> CONNECT database-name;
+ mysql> SOURCE path-to-kea/share/kea/scripts/mysql/dhcpdb_create.mysql
+
+ (where ``path-to-kea`` is the location where Kea is installed.)
+
+ The database may also be dropped manually as follows:
+
+ .. code-block:: mysql
+
+ mysql> CONNECT database-name;
+ mysql> SOURCE path-to-kea/share/kea/scripts/mysql/dhcpdb_drop.mysql
+
+ (where ``path-to-kea`` is the location where Kea is installed.)
+
+.. warning::
+
+ Dropping the database results in the unrecoverable loss of any data it contains.
+
+
+5. Exit MySQL:
+
+ .. code-block:: mysql
+
+ mysql> quit
+ Bye
+
+If the tables were not created in Step 4, run the :iscman:`kea-admin` tool
+to create them now:
+
+.. code-block:: console
+
+ $ kea-admin db-init mysql -u database-user -p database-password -n database-name
+
+Do not do this if the tables were created in Step 4. :iscman:`kea-admin`
+implements rudimentary checks; it will refuse to initialize a database
+that contains any existing tables. To start from scratch,
+all data must be removed manually. (This process is a manual operation
+on purpose, to avoid accidentally irretrievable mistakes by :iscman:`kea-admin`.)
+
+.. _mysql-upgrade:
+
+Upgrading a MySQL Database From an Earlier Version of Kea
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes a new Kea version uses a newer database schema, so the
+existing database needs to be upgraded. This can be done using the
+``kea-admin db-upgrade`` command.
+
+To check the current version of the database, use the following command:
+
+.. code-block:: console
+
+ $ kea-admin db-version mysql -u database-user -p database-password -n database-name
+
+(See :ref:`kea-database-version`
+for a discussion about versioning.) If the version does not match the
+minimum required for the new version of Kea (as described in the release
+notes), the database needs to be upgraded.
+
+Before upgrading, please make sure that the database is backed up. The
+upgrade process does not discard any data, but depending on the nature
+of the changes, it may be impossible to subsequently downgrade to an
+earlier version.
+
+To perform an upgrade, issue the following command:
+
+.. code-block:: console
+
+ $ kea-admin db-upgrade mysql -u database-user -p database-password -n database-name
+
+.. note::
+
+ To search host reservations by hostname, it is critical that the collation of
+ the hostname column in the host table be case-insensitive. Fortunately, that
+ is the default in MySQL, but it can be verified via this command:
+
+ .. code-block:: mysql
+
+ mysql> SELECT COLLATION('');
+ +-----------------+
+ | COLLATION('') |
+ +-----------------+
+ | utf8_general_ci |
+ +-----------------+
+
+ According to mysql's naming convention, when the name ends in ``_ci``,
+ the collation is case-insensitive.
+
+.. _mysql-performance:
+
+Improved Performance With MySQL
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Changing the MySQL internal value ``innodb_flush_log_at_trx_commit`` from the default value
+of 1 to 2 can result in a huge gain in Kea performance. In some deployments, the
+gain was over 1000% (10 times faster when set to 2, compared to the default value of 1).
+It can be set per-session for testing:
+
+.. code-block:: mysql
+
+ mysql> SET GLOBAL innodb_flush_log_at_trx_commit=2;
+ mysql> SHOW SESSION VARIABLES LIKE 'innodb_flush_log%';
+
+or permanently in ``/etc/mysql/my.cnf``:
+
+.. code-block:: ini
+
+ [mysqld]
+ innodb_flush_log_at_trx_commit=2
+
+Be aware that changing this value can cause problems during data recovery
+after a crash, so we recommend checking the `MySQL documentation
+<https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit>`__.
+With the default value of 1, MySQL writes changes to disk after every INSERT or UPDATE query
+(in Kea terms, every time a client gets a new lease or renews an existing lease). When
+``innodb_flush_log_at_trx_commit`` is set to 2, MySQL writes the changes at intervals
+no longer than 1 second. Batching writes gives a substantial performance boost. The trade-off,
+however, is that in the worst-case scenario, all changes in the last second before crash
+could be lost. Given the fact that Kea is stable software and crashes very rarely,
+most deployments find it a beneficial trade-off.
+
+.. _pgsql-database:
+
+PostgreSQL
+----------
+
+PostgreSQL can store leases, host reservations, and options
+defined on a per-host basis.
+
+.. _pgsql-database-create:
+
+First-Time Creation of the PostgreSQL Database
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Before preparing any Kea-specific database and tables, the PostgreSQL database
+must be configured to use the system timezone. It is recommended to use UTC
+as the timezone for both the system and the PostgreSQL database.
+
+To check the system timezone:
+
+ .. code-block:: console
+
+ date +%Z
+
+To check the PostgreSQL timezone:
+
+ .. code-block:: psql
+
+ postgres=# show timezone;
+ postgres=# SELECT * FROM pg_timezone_names WHERE name = current_setting('TIMEZONE');
+
+To configure the PostgreSQL timezone for a specific server, please refer to the
+installed version documentation.
+
+Usually the setting is configured in the ``postgresql.conf`` with the varying
+version path ``/etc/postgresql/<version>/main/postgresql.conf``, but on some systems
+the files may be located in ``/var/lib/pgsql/data``.
+
+ .. code-block:: ini
+
+ timezone = 'UTC'
+
+The first task is to create both the database and the user under
+which the servers will access it. A number of steps are required:
+
+1. Log into PostgreSQL as "root":
+
+ .. code-block:: console
+
+ $ sudo -u postgres psql postgres
+ Enter password:
+ postgres=#
+
+2. Create the database:
+
+ .. code-block:: psql
+
+ postgres=# CREATE DATABASE database-name;
+ CREATE DATABASE
+ postgres=#
+
+ (``database-name`` is the name chosen for the database.)
+
+3. Create the user under which Kea will access the database (and give it
+ a password), then grant it access to the database:
+
+ .. code-block:: psql
+
+ postgres=# CREATE USER user-name WITH PASSWORD 'password';
+ CREATE ROLE
+ postgres=# GRANT ALL PRIVILEGES ON DATABASE database-name TO user-name;
+ GRANT
+ postgres=#
+
+4. Exit PostgreSQL:
+
+ .. code-block:: psql
+
+ postgres=# \q
+ Bye
+ $
+
+5. At this point, create the database tables either
+ using the :iscman:`kea-admin` tool, as explained in the next section
+ (recommended), or manually. To create the tables manually, enter the
+ following command. PostgreSQL will prompt the administrator to enter the
+ new user's password that was specified in Step 3. When the command
+ completes, Kea will return to the shell prompt. The
+ output should be similar to the following:
+
+ .. code-block:: console
+
+ $ psql -d database-name -U user-name -f path-to-kea/share/kea/scripts/pgsql/dhcpdb_create.pgsql
+ Password for user user-name:
+ CREATE TABLE
+ CREATE INDEX
+ CREATE INDEX
+ CREATE TABLE
+ CREATE INDEX
+ CREATE TABLE
+ START TRANSACTION
+ INSERT 0 1
+ INSERT 0 1
+ INSERT 0 1
+ COMMIT
+ CREATE TABLE
+ START TRANSACTION
+ INSERT 0 1
+ COMMIT
+ $
+
+ (``path-to-kea`` is the location where Kea is installed.)
+
+ If instead an error is encountered, such as:
+
+ ::
+
+ psql: FATAL: no pg_hba.conf entry for host "[local]", user "user-name", database "database-name", SSL off
+
+ ... the PostgreSQL configuration will need to be altered. Kea uses
+ password authentication when connecting to the database and must have
+ the appropriate entries added to PostgreSQL's pg_hba.conf file. This
+ file is normally located in the primary data directory for the
+ PostgreSQL server. The precise path may vary depending on the
+ operating system and version, but the default location for PostgreSQL is
+ ``/etc/postgresql/*/main/postgresql.conf``. However, on some systems, the
+ file may reside in ``/var/lib/pgsql/data``.
+
+ Assuming Kea is running on the same host as PostgreSQL, adding lines
+ similar to the following should be sufficient to provide
+ password-authenticated access to Kea's database:
+
+ ::
+
+ local database-name user-name password
+ host database-name user-name 127.0.0.1/32 password
+ host database-name user-name ::1/128 password
+
+ These edits are primarily intended as a starting point, and are not a
+ definitive reference on PostgreSQL administration or database
+ security. Please consult the PostgreSQL user manual before making
+ these changes, as they may expose other databases that are running. It
+ may be necessary to restart PostgreSQL for the changes to
+ take effect.
+
+Initialize the PostgreSQL Database Using :iscman:`kea-admin`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If the tables were not created manually, do so now by
+running the :iscman:`kea-admin` tool:
+
+.. code-block:: console
+
+ $ kea-admin db-init pgsql -u database-user -p database-password -n database-name
+
+Do not do this if the tables were already created manually. :iscman:`kea-admin`
+implements rudimentary checks; it will refuse to initialize a database
+that contains any existing tables. To start from scratch,
+all data must be removed manually. (This process is a manual operation
+on purpose, to avoid accidentally irretrievable mistakes by :iscman:`kea-admin`.)
+
+.. _pgsql-upgrade:
+
+Upgrading a PostgreSQL Database From an Earlier Version of Kea
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The PostgreSQL database schema can be upgraded using the same tool and
+commands as described in :ref:`mysql-upgrade`, with the exception that the "pgsql"
+database backend type must be used in the commands.
+
+Use the following command to check the current schema version:
+
+.. code-block:: console
+
+ $ kea-admin db-version pgsql -u database-user -p database-password -n database-name
+
+Use the following command to perform an upgrade:
+
+.. code-block:: console
+
+ $ kea-admin db-upgrade pgsql -u database-user -p database-password -n database-name
+
+.. _pgsl-ssl:
+
+PostgreSQL without OpenSSL support
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Usually the PostgreSQL database client library is built with the OpenSSL
+support but Kea can be configured to handle the case where it is not
+supported:
+
+.. code-block:: console
+
+ $ ./configure [other-options] --disable-pgsql-ssl
+
+.. _pgsql-performance:
+
+Improved Performance With PostgreSQL
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Changing the PostgreSQL internal value ``synchronous_commit`` from the default value
+of ON to OFF can result in significant gains in Kea performance; on slow systems, the gain
+can be over 1000%. It can be set per-session for testing:
+
+.. code-block:: psql
+
+ postgres=# SET synchronous_commit = OFF;
+
+or permanently via command (preferred method):
+
+.. code-block:: psql
+
+ postgres=# ALTER SYSTEM SET synchronous_commit=OFF;
+
+or permanently in ``/etc/postgresql/[version]/main/postgresql.conf``:
+
+.. code-block:: ini
+
+ synchronous_commit = off
+
+Changing this value can cause problems during data recovery
+after a crash, so we recommend a careful read of the `PostgreSQL documentation
+<https://www.postgresql.org/docs/current/wal-async-commit.html>`__.
+With the default value of ON, PostgreSQL writes changes to disk after every INSERT or UPDATE query
+(in Kea terms, every time a client gets a new lease or renews an existing lease). When
+``synchronous_commit`` is set to OFF, PostgreSQL adds some delay before writing the changes.
+Batching writes gives a substantial performance boost,
+but in the worst-case scenario, all changes in the last moment before a crash
+could be lost. Since Kea is stable software and crashes very rarely,
+most deployments find the performance benefits outweigh the potential risks.
+
+Using Read-Only Databases With Host Reservations
+------------------------------------------------
+
+If a read-only database is used for storing host reservations, Kea must
+be explicitly configured to operate on the database in read-only mode.
+Sections :ref:`read-only-database-configuration4` and
+:ref:`read-only-database-configuration6` describe when such
+a configuration may be required, and how to configure Kea to operate in
+this way for both DHCPv4 and DHCPv6.
+
+Limitations Related to the Use of SQL Databases
+-----------------------------------------------
+
+Year 2038 Issue
+~~~~~~~~~~~~~~~
+
+The lease expiration time in Kea is stored in the SQL database for each lease
+as a timestamp value. Kea developers have observed that the MySQL database
+does not accept timestamps beyond 2147483647 seconds (the maximum signed
+32-bit number) from the beginning of the UNIX epoch (00:00:00 on 1
+January 1970). Some versions of PostgreSQL do accept greater values, but
+the value is altered when it is read back. For this reason, the lease
+database backends put a restriction on the maximum timestamp to be
+stored in the database, which is equal to the maximum signed 32-bit
+number. This effectively means that the current Kea version cannot store
+leases whose expiration time is later than 2147483647 seconds since the
+beginning of the epoch (around the year 2038). This will be fixed when
+database support for longer timestamps is available.
diff --git a/doc/sphinx/arm/agent.rst b/doc/sphinx/arm/agent.rst
new file mode 100644
index 0000000..328c03a
--- /dev/null
+++ b/doc/sphinx/arm/agent.rst
@@ -0,0 +1,314 @@
+.. _kea-ctrl-agent:
+
+*********************
+The Kea Control Agent
+*********************
+
+.. _agent-overview:
+
+Overview of the Kea Control Agent
+=================================
+
+The Kea Control Agent (CA) is a daemon which exposes a RESTful control
+interface for managing Kea servers. The daemon can receive control
+commands over HTTP and either forward these commands to the respective
+Kea servers or handle these commands on its own. The determination
+whether the command should be handled by the CA or forwarded is made by
+checking the value of the ``service`` parameter, which may be included in
+the command from the controlling client. The details of the supported
+commands, as well as their structures, are provided in
+:ref:`ctrl-channel`.
+
+The CA can use hook libraries to provide support for additional commands
+or to program custom behavior of existing commands. Such hook libraries must
+implement callouts for the ``control_command_receive`` hook point. Details
+about creating new hook libraries and supported hook points can be found
+in the `Kea Developer's
+Guide <https://reports.kea.isc.org/dev_guide/>`__.
+
+The CA processes received commands according to the following algorithm:
+
+- Pass command into any installed hooks (regardless of service
+ value(s)). If the command is handled by a hook, return the response.
+
+- If the service specifies one or more services, forward the command to
+ the specified services and return the accumulated responses.
+
+- If the service is not specified or is an empty list, handle the
+ command if the CA supports it.
+
+.. _agent-configuration:
+
+Configuration
+=============
+
+The following example demonstrates the basic CA configuration.
+
+.. code-block:: json
+
+ {
+ "Control-agent": {
+ "http-host": "10.20.30.40",
+ "http-port": 8000,
+ "trust-anchor": "/path/to/the/ca-cert.pem",
+ "cert-file": "/path/to/the/agent-cert.pem",
+ "key-file": "/path/to/the/agent-key.pem",
+ "cert-required": true,
+ "authentication": {
+ "type": "basic",
+ "realm": "kea-control-agent",
+ "clients": [
+ {
+ "user": "admin",
+ "password": "1234"
+ } ]
+ },
+
+ "control-sockets": {
+ "dhcp4": {
+ "comment": "main server",
+ "socket-type": "unix",
+ "socket-name": "/path/to/the/unix/socket-v4"
+ },
+ "dhcp6": {
+ "socket-type": "unix",
+ "socket-name": "/path/to/the/unix/socket-v6",
+ "user-context": { "version": 3 }
+ },
+ "d2": {
+ "socket-type": "unix",
+ "socket-name": "/path/to/the/unix/socket-d2"
+ }
+ },
+
+ "hooks-libraries": [
+ {
+ "library": "/opt/local/custom_hooks_example.so",
+ "parameters": {
+ "param1": "foo"
+ }
+ } ],
+
+ "loggers": [ {
+ "name": "kea-ctrl-agent",
+ "severity": "INFO"
+ } ]
+ }
+ }
+
+The ``http-host`` and ``http-port`` parameters specify an IP address and
+port to which HTTP service will be bound. In the example configuration
+provided above, the RESTful service will be available at the URL
+``https://10.20.30.40:8000/``. If these parameters are not specified, the
+default URL is ``http://127.0.0.1:8000/``.
+
+When using Kea's HA hook library with multi-threading,
+the address:port combination used for CA must be
+different from the HA peer URLs, which are strictly
+for internal HA traffic between the peers. User commands should
+still be sent via the CA.
+
+The ``trust-anchor``, ``cert-file``, ``key-file``, and ``cert-required``
+parameters specify the TLS setup for HTTP, i.e. HTTPS. If these parameters
+are not specified, HTTP is used. The TLS/HTTPS support in Kea is
+described in :ref:`tls`.
+
+As mentioned in :ref:`agent-overview`, the CA can forward
+received commands to the Kea servers for processing. For example,
+:isccmd:`config-get` is sent to retrieve the configuration of one of the Kea
+services. When the CA receives this command, including a ``service``
+parameter indicating that the client wishes to retrieve the
+configuration of the DHCPv4 server, the CA forwards the command to that
+server and passes the received response back to the client. More about
+the ``service`` parameter and the general structure of commands can be
+found in :ref:`ctrl-channel`.
+
+The CA uses UNIX domain sockets to forward control commands and receive
+responses from other Kea services. The ``dhcp4``, ``dhcp6``, and ``d2``
+maps specify the files to which UNIX domain sockets are bound. In the
+configuration above, the CA connects to the DHCPv4 server via
+``/path/to/the/unix/socket-v4`` to forward the commands to it.
+Obviously, the DHCPv4 server must be configured to listen to connections
+via this same socket. In other words, the command-socket configuration
+for the DHCPv4 server and the CA (for that server) must match. Consult
+:ref:`dhcp4-ctrl-channel`, :ref:`dhcp6-ctrl-channel`, and
+:ref:`d2-ctrl-channel` to learn how the socket configuration is
+specified for the DHCPv4, DHCPv6, and D2 services.
+
+User contexts can store arbitrary data as long as they are in valid JSON
+syntax and their top-level element is a map (i.e. the data must be
+enclosed in curly brackets). Some hook libraries may expect specific
+formatting; please consult the relevant hook library documentation for
+details.
+
+User contexts can be specified on either global scope, control socket,
+basic authentication, or loggers. One other useful feature is the
+ability to store comments or descriptions; the parser translates a
+"comment" entry into a user context with the entry, which allows a
+comment to be attached within the configuration itself.
+
+Basic HTTP authentication protects
+against unauthorized uses of the control agent by local users. For
+protection against remote attackers, HTTPS and reverse proxy of
+:ref:`agent-secure-connection` provide stronger security.
+
+The authentication is described in the ``authentication`` block
+with the mandatory ``type`` parameter, which selects the authentication.
+Currently only the basic HTTP authentication (type basic) is supported.
+
+The ``realm`` authentication parameter is used for error messages when
+the basic HTTP authentication is required but the client is not
+authorized.
+
+When the ``clients`` authentication list is configured and not empty,
+basic HTTP authentication is required. Each element of the list
+specifies a user ID and a password. The user ID is mandatory, must
+be not empty, and must not contain the colon (:) character. The
+password is optional; when it is not specified an empty password
+is used.
+
+.. note::
+
+ The basic HTTP authentication user ID and password are encoded
+ in UTF-8, but the current Kea JSON syntax only supports the Latin-1
+ (i.e. 0x00..0xff) Unicode subset.
+
+To avoid exposing the user ID and/or the associated
+password, these values can be read from files. The syntax is extended by:
+
+- The ``directory`` authentication parameter, which handles the common
+ part of file paths. The default value is the empty string.
+
+- The ``password-file`` client parameter, which, alongside the ``directory``
+ parameter, specifies the path of a file that can contain the password,
+ or when no user ID is given, the whole basic HTTP authentication secret.
+
+- The ``user-file`` client parameter, which, with the ``directory`` parameter,
+ specifies the path of a file where the user ID can be read.
+
+When files are used, they are read when the configuration is loaded,
+to detect configuration errors as soon as possible.
+
+Hook libraries can be loaded by :iscman:`kea-ctrl-agent` in the same way as
+they are loaded by :iscman:`kea-dhcp4` and :iscman:`kea-dhcp6`. The CA currently
+supports one hook point - ``control_command_receive`` - which makes it
+possible to delegate the processing of some commands to the hook library.
+The ``hooks-libraries`` list contains the list of hook libraries that
+should be loaded by :iscman:`kea-ctrl-agent`, along with their configuration information
+specified with ``parameters``.
+
+Please consult :ref:`logging` for the details on how to configure
+logging. The CA's root logger's name is :iscman:`kea-ctrl-agent`, as given in
+the example above.
+
+.. _agent-secure-connection:
+
+Secure Connections
+==================
+
+The Kea Control Agent natively supports secure
+HTTP connections using TLS. This allows protection against users from
+the node where the agent runs, something that a reverse proxy cannot
+provide. More about TLS/HTTPS support in Kea can be found in :ref:`tls`.
+
+TLS is configured using three string parameters with file names, and
+a boolean parameter:
+
+- The ``trust-anchor`` specifies the Certification Authority file name or
+ directory path.
+
+- The ``cert-file`` specifies the server certificate file name.
+
+- The ``key-file`` specifies the private key file name. The file must not
+ be encrypted.
+
+- The ``cert-required`` specifies whether client certificates are required
+ or optional. The default is to require them and to perform mutual
+ authentication.
+
+The file format is PEM. Either all the string parameters are specified and
+HTTP over TLS (HTTPS) is used, or none is specified and plain HTTP is used.
+Configuring only one or two string parameters results in an error.
+
+.. note::
+
+ When client certificates are not required, only the server side is
+ authenticated, i.e. the communication is encrypted with an unknown
+ client. This protects only against passive attacks; active
+ attacks, such as "man-in-the-middle," are still possible.
+
+.. note::
+
+ No standard HTTP authentication scheme cryptographically binds its end
+ entity with TLS. This means that the TLS client and server can be
+ mutually authenticated, but there is no proof they are the same as
+ for the HTTP authentication.
+
+The :iscman:`kea-shell` tool also supports TLS.
+
+.. _agent-launch:
+
+Starting and Stopping the Control Agent
+=======================================
+
+:iscman:`kea-ctrl-agent` accepts the following command-line switches:
+
+- ``-c file`` - specifies the configuration file.
+
+- ``-d`` - specifies whether the agent logging should be switched to
+ debug/verbose mode. In verbose mode, the logging severity and
+ debuglevel specified in the configuration file are ignored and
+ "debug" severity and the maximum debuglevel (99) are assumed. The
+ flag is convenient for temporarily switching the server into maximum
+ verbosity, e.g. when debugging.
+
+- ``-t file`` - specifies the configuration file to be tested.
+ :iscman:`kea-netconf` attempts to load it and conducts sanity checks;
+ certain checks are possible only while running the actual server. The
+ actual status is reported with exit code (0 = configuration appears valid,
+ 1 = error encountered). Kea prints out log messages to standard
+ output and error to standard error when testing the configuration.
+
+- ``-v`` - displays the version of :iscman:`kea-ctrl-agent` and exits.
+
+- ``-V`` - displays the extended version information for :iscman:`kea-ctrl-agent`
+ and exits. The listing includes the versions of the libraries
+ dynamically linked to Kea.
+
+- ``-W`` - displays the Kea configuration report and exits. The report
+ is a copy of the ``config.report`` file produced by ``./configure``;
+ it is embedded in the executable binary.
+
+ The contents of the ``config.report`` file may also be accessed by examining
+ certain libraries in the installation tree or in the source tree.
+
+ .. code-block:: shell
+
+ # from installation using libkea-process.so
+ $ strings ${prefix}/lib/libkea-process.so | sed -n 's/;;;; //p'
+
+ # from sources using libkea-process.so
+ $ strings src/lib/process/.libs/libkea-process.so | sed -n 's/;;;; //p'
+
+ # from sources using libkea-process.a
+ $ strings src/lib/process/.libs/libkea-process.a | sed -n 's/;;;; //p'
+
+ # from sources using libcfgrpt.a
+ $ strings src/lib/process/cfgrpt/.libs/libcfgrpt.a | sed -n 's/;;;; //p'
+
+The CA is started by running its binary and specifying the configuration
+file it should use. For example:
+
+.. code-block:: console
+
+ $ ./kea-ctrl-agent -c /usr/local/etc/kea/kea-ctrl-agent.conf
+
+It can be started by :iscman:`keactrl` as well (see :ref:`keactrl`).
+
+.. _agent-clients:
+
+Connecting to the Control Agent
+===============================
+
+For an example of a tool that can take advantage of the RESTful API, see
+:ref:`kea-shell`.
diff --git a/doc/sphinx/arm/classify.rst b/doc/sphinx/arm/classify.rst
new file mode 100644
index 0000000..7f011ec
--- /dev/null
+++ b/doc/sphinx/arm/classify.rst
@@ -0,0 +1,1265 @@
+.. _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.
+
+Client classification can be used to change the behavior of almost any
+part of the DHCP message processing. There are currently nine
+mechanisms that take advantage of client classification:
+
+- Dropping queries.
+
+- Subnet selection.
+
+- Pool selection.
+
+- Lease limiting.
+
+- Rate limiting.
+
+- DDNS tuning.
+
+- Defining DHCPv4 private (codes 224-254) and code 43 options.
+
+- Assigning different options.
+
+- Setting specific options for use with the TFTP server address
+ and the boot file field for DHCPv4 cable modems.
+
+.. _classify-classification-steps:
+
+Classification Steps
+--------------------
+
+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`` (a 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. When the incoming packet belongs to the special class ``DROP``, it is
+ dropped and an informational message is logged with the packet
+ information.
+
+.. note::
+
+ The ``pkt4_receive`` and ``pkt6_receive`` callouts are called here.
+
+6. When the ``early-global-reservations-lookup`` global parameter is
+ configured to ``true``, the process looks up global reservations and
+ partially performs steps 8, 9, and 10. The lookup is limited to
+ global reservations; if one is found the ``KNOWN`` class is set,
+ but if none is found the ``UNKNOWN`` class is **not** set.
+
+7. 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 has no
+ class associated with it, or has a class which matches one of the
+ packet's classes.
+
+.. note::
+
+ The ``subnet4_select`` and ``subnet6_select`` callouts are called here.
+
+8. 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.
+
+9. 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`` (a 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 the ``UNKNOWN`` class to select a shared network or
+ a subnet. For the ``KNOWN`` class, only global reservations are used and the
+ ``early-global-reservations-lookup`` parameter must be configured to
+ ``true``.
+
+10. When the incoming packet belongs to the special class ``DROP``, it is
+ dropped and an informational message is logged with the packet
+ information. Since Kea version 1.9.8, it is permissible to make the ``DROP``
+ class dependent on the ``KNOWN``/``UNKNOWN`` classes.
+
+11. If needed, addresses and prefixes from pools are assigned, possibly
+ based on the class information when some pools are reserved for
+ class members.
+
+.. note::
+
+ The ``lease4_select``, ``lease4_renew``, ``lease6_select``, ``lease6_renew``, and ``lease6_rebind``
+ callouts are called here.
+
+12. 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.
+
+13. 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 option
+ definitions specified within classes.
+
+.. 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.
+
+.. _built-in-client-classes:
+
+Built-in Client Classes
+=======================
+
+Some classes are built-in, so they do not need to be explicitly defined. They
+can be defined if there is a need to associate lease lifetimes, option data,
+etc. with them.
+
+Vendor class information is the primary example: 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 that packet. The content that is
+considered is the whole class identifier for DHCPv4, and the first vendor class
+data field for DHCPv6. The enterprise number and subsequent vendor class data
+fields are not used for the purpose of classification. For example, modern cable
+modems send such options with value ``docsis3.0``, so the packet belongs to
+class ``VENDOR_CLASS_docsis3.0``.
+
+The ``HA_`` prefix is used by :ischooklib:`libdhcp_ha.so` 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:`hooks-high-availability`.
+
+The ``SPAWN_`` prefix is used by template classes to generate spawned class
+names at runtime. The spawned class name is constructed by prepending the
+``SPAWN_`` prefix to the template class name and the evaluated value:
+``SPAWN_<template-class-name>_<evaluated-value>``.
+More details can be found in :ref:`classification-configuring`.
+
+The ``BOOTP`` class is used by :ischooklib:`libdhcp_bootp.so` to classify and
+respond to inbound BOOTP queries.
+
+The ``SKIP_DDNS`` class is used by the DDNS-tuning hook library to suppress
+DDNS updates on a per client basis.
+
+Other examples are the ``ALL`` class, to which all incoming packets belong,
+and the ``KNOWN`` class, assigned when host reservations exist for a
+particular client. By convention, the names of built-in classes begin with all
+capital letters.
+
+Currently recognized built-in class names are ``ALL``, ``KNOWN`` and ``UNKNOWN``,
+and the prefixes ``VENDOR_CLASS_``, ``HA_``, ``AFTER_``, ``EXTERNAL_``,
+``SKIP_DDNS``. 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' |
+ +-----------------------+-------------------------------+-----------------------+
+ | Binary content of the | option[12].option[34].hex | '(content of the |
+ | sub-option | | sub-option)' |
+ +-----------------------+-------------------------------+-----------------------+
+ | Sub-Option existence | option[12].option[34].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[*].exists | 'true' |
+ | existence (any | | |
+ | 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 does not 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 with ``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
+ the ``KNOWN`` class (directly or indirectly) 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 does not contain a RAI option, or the RAI
+ option does not 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 relays to be specified counting from the DHCPv6 client, with -1 indicating
+ the relay closest to the client. If the requested
+ encapsulation does not 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`` is
+ ``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 the
+ 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 the 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
+ the 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>`__ to request
+ 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 |
+ +-----------------------+-------------------------+-----------------------+
+ | Concat (operator +) | '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. 66-6F-6F |
+ +-----------------------+-------------------------+-----------------------+
+ | Lcase | lcase('LoWeR') | Converts the value of |
+ | | | a string expression |
+ | | | to lower case e.g. |
+ | | | 'lower' |
+ +-----------------------+-------------------------+-----------------------+
+ | Ucase | ucase('uPpEr') | Converts the value of |
+ | | | a string expression |
+ | | | to upper case e.g. |
+ | | | 'UPPER' |
+ +-----------------------+-------------------------+-----------------------+
+ | Split | split('foo.bar', '.', 2)| Return the second |
+ | | | field, splitting on |
+ | | | dots. |
+ +-----------------------+-------------------------+-----------------------+
+
+.. table:: List of conversion-to-text expressions
+
+ +-----------------------+---------------------------+------------------------+
+ | Name | Example | Description |
+ +=======================+===========================+========================+
+ | AddressToText | addrtotext (192.10.0.1) | Represent the 4 bytes |
+ | | addrtotext (2003:db8::) | of an IPv4 address or |
+ | | | the 16 bytes of an |
+ | | | IPv6 address in human |
+ | | | readable format |
+ +-----------------------+---------------------------+------------------------+
+ | Int8ToText | int8totext (-1) | Represents the 8-bit |
+ | | | signed integer in text |
+ | | | format |
+ +-----------------------+---------------------------+------------------------+
+ | Int16ToText | int16totext (-1) | Represents the 16-bit |
+ | | | signed integer in text |
+ | | | format |
+ +-----------------------+---------------------------+------------------------+
+ | Int32ToText | int32totext (-1) | Represents the 32-bit |
+ | | | signed integer in text |
+ | | | format |
+ +-----------------------+---------------------------+------------------------+
+ | UInt8ToText | uint8totext (255) | Represents the 8-bit |
+ | | | unsigned integer in |
+ | | | text format |
+ +-----------------------+---------------------------+------------------------+
+ | UInt16ToText | uint16totext (65535) | Represents the 16-bit |
+ | | | unsigned integer in |
+ | | | text format |
+ +-----------------------+---------------------------+------------------------+
+ | UInt32ToText | uint32totext (4294967295) | Represents the 32-bit |
+ | | | unsigned integer in |
+ | | | text format |
+ +-----------------------+---------------------------+------------------------+
+
+Notes:
+
+The conversion operators can be used to transform data from binary to the text
+representation. The only requirement is that the input data type length matches
+an expected value.
+
+The ``AddressToText`` token expects 4 bytes for IPv4 addresses or 16 bytes for IPv6
+addresses. The ``Int8ToText`` and ``UInt8ToText`` tokens expect 1 byte, the ``Int16ToText`` and
+``UInt16ToText`` tokens expect 2 bytes, and ``Int32ToText`` and ``UInt32ToText`` expect 4 bytes.
+For all conversion tokens, if the data length is 0, the result string is empty.
+
+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'
+
+For user convenience, Kea version 1.9.8 added an associative operator
+version of the concat function. For instance:
+::
+
+ 'abc' + 'def' + 'ghi' + 'jkl' + '...'
+
+is the same as:
+::
+
+ concat(concat(concat(concat('abc', 'def'), 'ghi'), 'jkl'), '...')
+
+or:
+::
+
+ concat('abc', concat('def', concat('ghi', concat('jkl', '...'))))
+
+or:
+::
+
+ 'abcdefghijkl...'
+
+Split
+---------
+
+The split operator ``split(value, delimiters, field-number)`` accepts a list
+of characters to use as delimiters and a positive field number of the
+desired field when the value is split into fields separated by the delimiters.
+Adjacent delimiters are not compressed out, rather they result in an empty
+string for that field number. If value is an empty string, the result will be an
+empty string. If the delimiters list is empty, the result will be the original
+value. If the field-number is less than one or larger than the number of
+fields, the result will be an empty string. Some examples follow:
+::
+
+ split ('one.two..four', '.', 1) == 'one'
+ split ('one.two..four', '.', 2) == 'two'
+ split ('one.two..four', '.', 3) == ''
+ split ('one.two..four', '.', 4) == 'four'
+ split ('one.two..four', '.', 5) == ''
+
+.. note::
+
+ To use a hard-to-escape character as a delimiter, use its ASCII hex value.
+ For example, split by ``single quote`` using ``0x27``:
+ ``split(option[39].text, 0x27, 1)``
+
+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 client class definition can contain the following properties:
+ - The ``name`` parameter is mandatory and must be unique among all classes.
+ - The ``test`` expression is not mandatory and represents a string containing the
+ logical expression used to determine membership in the class. The entire
+ expression is included in double quotes (``"``). The result should evaluate
+ to a boolean value (``true`` or ``false``).
+ - The ``template-test`` expression is not mandatory and represents a string
+ containing the logical expression used to generate a spawning class. The
+ entire expression is included in double quotes (``"``). The result should
+ evaluate to a string value representing the variable part of the spawned
+ class name. If the resulting string is empty, no spawning class is generated.
+ The resulting spawned class has the following generated name format:
+ ``SPAWN_<template-class-name>_<evaluated-value>``.
+ After classes are evaluated and a spawned class is generated, the corresponding
+ template class name is also associated with the packet.
+ - The ``option-data`` list is not mandatory and contains options that should be
+ assigned to members of this class. In the case of a template class, these
+ options are assigned to the generated spawned class.
+ - The ``option-def`` list is not mandatory and is used to define custom options.
+ - The ``only-if-required`` flag is not mandatory; when its value is set to
+ ``false`` (the default), membership is determined during classification and is
+ available for subnet selection, for instance. When the value is set to
+ ``true``, membership is evaluated only when required and is usable only for
+ option configuration.
+ - The ``user-context`` is not mandatory and represents a map with user-defined data
+ and possibly configuration options for hook libraries.
+ - The ``next-server`` parameter is not mandatory and configures the ``siaddr`` field in
+ packets associated with this class. It is used in DHCPv4 only.
+ - The ``server-hostname`` is not mandatory and configures the ``sname`` field in
+ packets associated with this class. It is used in DHCPv4 only.
+ - The ``boot-file-name`` is not mandatory and configures the ``file`` field in
+ packets associated with this class. It is used in DHCPv4 only.
+ - The ``valid-lifetime``, ``min-valid-lifetime``, and ``max-valid-lifetime`` are
+ not mandatory and configure the valid lifetime fields for this client class.
+ - The ``preferred-lifetime``, ``min-preferred-lifetime``, and
+ ``max-preferred-lifetime`` are not mandatory and configure the preferred
+ lifetime fields for this client class. It is used in DHCPv6 only.
+
+
+.. note::
+
+ ``test`` and ``template-test`` are mutually exclusive in a client class
+ definition. Use either one, or neither, but not both. If both are provided,
+ the configuration is rejected.
+
+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"
+ }
+ ]
+ },
+ ...
+ ],
+ ...
+ }
+
+The next 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
+``0x0002AABBCCDD``). Members of this class will be given 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"
+ }
+ ]
+ },
+ ...
+ ],
+ ...
+ }
+
+It is also possible to have both left and right operands of the evaluated
+expression processed at runtime. Expressions related to packets can appear in
+the expression as many times as needed; there is no limit. However, each token
+has a small impact on performance and excessively complex expressions may cause a
+bottleneck.
+
+::
+
+ "Dhcp4": {
+ "client-classes": [
+ {
+ "name": "Infrastructure",
+ "test": "option[82].option[2].hex == pkt4.mac",
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+.. _template-classes:
+
+Template Classes
+----------------
+
+The ``template-test`` parameter indicates that the class is a template class.
+
+::
+
+ "Dhcp4": {
+ "client-classes": [
+ {
+ "name": "Client-ID",
+ "template-test": "substring(option[61].hex,0,3)",
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+If the received DHCPv4 packet contains option 61, then the first three bytes represent
+the value ``foo`` in ASCII, and the spawned class uses the
+``SPAWN_Client-ID_foo`` name.
+Both the ``SPAWN_Client-ID_foo`` and ``Client-ID`` classes are associated with
+the packet.
+
+.. note ::
+
+ Template classes can also be used to spawn classes which match regular
+ classes, effectively associating the regular class to the packet.
+ To achieve this, the regular class must also contain the fixed part of the
+ spawned class name:
+
+ ``SPAWN_<template-class-name-used-to-activate-this-regular-class>_<evaluated-value-filtering-this-regular-class>``
+
+::
+
+ "Dhcp6": {
+ "client-classes": [
+ {
+ "name": "SPAWN_Client-ID_foobar",
+ "test": "substring(option[1].hex,0,6) == 0x0002AABBCCDD",
+ ...
+ },
+ {
+ "name": "Client-ID",
+ "template-test": "substring(option[1].hex,0,6)",
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+If the received DHCPv6 packet contains option 1 (client identifier) with hex
+value ``0x0002AABBCCDD``, then the ``SPAWN_Client-ID_foobar`` is associated
+with the packet. Moreover, if the first six bytes represent value ``foobar`` in
+ASCII, then the spawned class uses the ``SPAWN_Client-ID_foobar`` name,
+effectively associating the regular class to the packet. In this second case,
+both the ``SPAWN_Client-ID_foobar`` and ``Client-ID`` classes are associated
+with the packet.
+The ``test`` expression on the regular class ``SPAWN_Client-ID_foobar`` is not
+mandatory and can be omitted, but it is used here with a different match
+expression for example purposes.
+
+Usually the ``test`` and ``template-test`` expressions are 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. For this purpose, the ``only-if-required`` flag, which is
+``false`` by default, allows the evaluation of the ``test`` expression or the
+``template-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 reverse-precedence
+order of the option data, i.e. an option data item in a subnet takes precedence over
+one in a shared network, but a 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``.
+
+.. note ::
+
+ The ``template-test`` expression can also be used to filter generated spawned
+ classes, so that they are created only when needed by using the ``ifelse``
+ instruction.
+
+::
+
+ "Dhcp4": {
+ "client-classes": [
+ {
+ "name": "Client-ID",
+ "template-test": "ifelse(substring(option[61].hex,4,3) == 'foo', substring(option[12].hex,0,12), '')",
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+.. note ::
+
+ The template classes can be used to configure limits which, just like
+ options, are associated with the spawned class. This permits the configuration of
+ limits that apply to all packets associated with a class spawned at
+ runtime, according to the ``template-test`` expression in the parent template
+ class. For a more detailed description of how to configure limits using the
+ limits hook library, see :ref:`hooks-limits-configuration`.
+ For example, using the configuration below, ingress DHCPv6 packets that have
+ client ID values (in the format expressed by the Kea evaluator) ``foobar``
+ and ``foofoo`` both amount to the same limit of 60 packets per day, while
+ other packets that have the first three hextets different than ``foo`` are put
+ in separate rate-limiting buckets.
+
+::
+
+ "Dhcp6": {
+ "client-classes": [
+ {
+ "name": "Client-ID",
+ "template-test": "substring(option[1].hex,0,3)",
+ "user-context" : {
+ "limits": {
+ "rate-limit": "60 packets per day"
+ }
+ },
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+
+.. _classification-using-host-reservations:
+
+Using Static Host Reservations in Classification
+================================================
+
+Classes can be statically assigned to clients using techniques
+described in :ref:`reservation4-client-classes` and
+:ref:`reservation6-client-classes`.
+
+Subnet host reservations are searched after subnet selection.
+Global host reservations are searched at the same time by default but
+the ``early-global-reservations-lookup`` allows to change this behavior
+into searching them before the subnet selection.
+
+Pool selection is performed after all host reservations lookups.
+
+.. _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 will be managed by the DHCPv4
+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": [
+ {
+ "id": 1,
+ "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 restricts use of the addresses in the range 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": [
+ {
+ "id": 1,
+ "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
+DHCPv4 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": [
+ {
+ "id": 1,
+ "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 restricts use of the addresses in the range 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": [
+ {
+ "id": 1,
+
+ "subnet": "2001:db8:1::/64",
+
+ "pools": [
+ {
+ "pool": "2001:db8:1::-2001:db8:1::ffff",
+ "client-class": "Client_foo"
+ }
+ ]
+ },
+ ...
+ ],
+ ...
+ }
+
+Class Priority
+==============
+
+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 client-class information (comprising of
+options, lease lifetimes or DHCPv4 field values) that is part of class
+definitions, 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 class information, the value from the first assigned class is used.
+``ALL`` is always the first class, hence the class with the highest
+priority, and matching required classes are last, so they have the
+lowest priority.
+
+Optons defined in classes override any global options, and in turn will be
+overridden by options defined for an individual subnet, shared network, pool or
+reservation.
+
+On the other hand, lease lifetimes and DHCPv4 field values defined at class
+scope override any values defined globally, in a subnet scope, or in a
+shared-network scope.
+
+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 determined as explained in the previous paragraphs.
+
+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](https://gitlab.isc.org/isc-projects/kea/-/wikis/designs/client-classification-design).
+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 is
+traversed in order; as each token is executed, it is 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
+emits 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 are 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 are 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 multiple
+ expressions to evaluate; it is intended as an aid in helping
+ create and debug expressions. Administrators should plan to disable debug
+ logging when 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.
diff --git a/doc/sphinx/arm/config-backend.rst b/doc/sphinx/arm/config-backend.rst
new file mode 100644
index 0000000..f83bf5c
--- /dev/null
+++ b/doc/sphinx/arm/config-backend.rst
@@ -0,0 +1,327 @@
+.. _config-backend:
+
+Kea Configuration Backend
+=========================
+
+.. _cb-applicability:
+
+Applicability
+-------------
+
+Kea Configuration Backend (CB or config backend) gives Kea servers the ability
+to manage and fetch their configuration from one or more databases. In
+this documentation, the term "Configuration Backend" may also refer to
+the particular Kea module providing support to manage and fetch the
+configuration information from the particular database type. For
+example, the MySQL Configuration Backend is the logic implemented within
+:ischooklib:`libdhcp_mysql_cb.so`, which provides a complete set of functions to
+manage and fetch the configuration information from the MySQL database.
+The PostgreSQL Configuration Backend is the logic implemented within
+:ischooklib:`libdhcp_pgsql_cb.so`, which provides a complete set of functions to
+manage and fetch the configuration information from the PostgreSQL database.
+From herein, the term "database" is used to refer to either a MySQL or
+PostgreSQL database.
+
+In small deployments, e.g. those comprising a single DHCP server
+instance with limited and infrequently changing number of subnets, it
+may be impractical to use the CB as a configuration repository because
+it requires additional third-party software to be installed and
+configured - in particular the database server, client and libraries.
+Once the number of DHCP servers and/or the number of managed subnets in the
+network grows, the usefulness of the CB becomes obvious.
+
+One use case for the CB is a pair of Kea DHCP servers that are configured
+to support High Availability as described in
+:ref:`hooks-high-availability`. The configurations of both servers
+(including the value of the ``server-tag`` parameter)
+are almost exactly the same: they may differ by the server identifier
+and designation of the server as a primary or standby (or secondary), and/or
+by their interfaces' configuration. Typically, the
+subnets, shared networks, option definitions, and global parameters are the
+same for both servers and can be sourced from a single database instance
+to both Kea servers.
+
+Using the database as a single source of configuration for subnets
+and/or other configuration information supported by the CB has the
+advantage that any modifications to the configuration in the database are
+automatically applied to both servers.
+
+Another case when the centralized configuration repository is useful is
+in deployments including a large number of DHCP servers, possibly
+using a common lease database to provide redundancy. New servers can
+be added to the pool frequently to fulfill growing scalability
+requirements. Adding a new server does not require replicating the
+entire configuration to the new server when a common database is used.
+
+Using the database as a configuration repository for Kea servers also
+brings other benefits, such as:
+
+- the ability to use database specific tools to access the configuration
+ information;
+
+- the ability to create customized statistics based on the information
+ stored in the database; and
+
+- the ability to backup the configuration information using the database's
+ built-in replication mechanisms.
+
+.. _cb-limitations:
+
+CB Capabilities and Limitations
+-------------------------------
+
+Currently, the Kea CB has the following limitations:
+
+- It is only supported for MySQL and PostgreSQL databases.
+
+- It is only supported for the DHCPv4 and DHCPv6 daemons; the Control Agent,
+ D2 daemon, and the NETCONF daemon cannot be configured from the database,
+
+- Only certain DHCP configuration parameters can be set in the
+ database: global parameters, option definitions, global options, client
+ classes, shared networks, and subnets. Other configuration parameters
+ must be sourced from a JSON configuration file.
+
+Kea CB stores data in a schema that is public. It is possible to
+insert configuration data into the tables manually or automatically
+using SQL scripts, but this requires SQL and schema knowledge.
+The supported method for managing the data is through :ischooklib:`libdhcp_cb_cmds.so`,
+which provides management commands for config backends. It simplifies many
+typical operations, such as listing, adding, retrieving, and deleting global
+parameters, shared networks, subnets, pools, options, option definitions, and
+client classes. In addition, it provides essential business logic that ensures
+the logical integrity of the data. See commands starting with ``remote-`` in
+Appendix A of this manual for a complete list.
+
+.. note::
+
+ :ischooklib:`libdhcp_cb_cmds.so` is available only to ISC customers with
+ a paid support contract. For more information on subscription options, please
+ complete the form at https://www.isc.org/contact.
+
+The schema creation scripts can be found at
+`dhcpdb_create.mysql <https://gitlab.isc.org/isc-projects/kea/blob/master/src/share/database/scripts/mysql/dhcpdb_create.mysql>`__
+and
+`dhcpdb_create.pgsql <https://gitlab.isc.org/isc-projects/kea/blob/master/src/share/database/scripts/pgsql/dhcpdb_create.pgsql>`__.
+Other related design documents are stored in our GitLab:
+`CB Design <https://gitlab.isc.org/isc-projects/kea/wikis/designs/configuration-in-db-design>`__
+and
+`Client Classes in CB Design <https://gitlab.isc.org/isc-projects/kea/wikis/designs/client-classes-in-cb>`__.
+
+We strongly recommend against duplication of configuration information
+in both the file and the database. For example, when specifying subnets
+for the DHCP server, please store them in either the configuration backend
+or in the configuration file, not both. Storing some subnets in the database
+and others in the file may put users at risk of potential configuration
+conflicts. Note that the configuration instructions from the database take
+precedence over instructions from the file, so parts of the configuration
+specified in the file may be overridden if contradicted by information in
+the database.
+
+Although it is not recommended, it is possible to specify certain parameter
+types both in a configuration file and the database. For example, a subnet
+can be specified in the configuration file and another subnet in the database;
+in this case, the server will use both subnets. DHCP client classes, however,
+must not be specified in both the configuration file and the database, even if
+they do not overlap. If any client classes are specified in the database
+for a particular DHCP server, this server will use these classes and ignore
+all classes present in its configuration file. This behavior was introduced
+to ensure that the server receives a consistent set of client classes
+specified in an expected order with all inter-class dependencies fulfilled.
+It is impossible to guarantee consistency when client classes are specified
+in two independent configuration sources.
+
+.. note::
+
+ It is recommended that :ischooklib:`libdhcp_subnet_cmds.so` not be used to
+ manage subnets when the configuration backend is used as a source
+ of information about the subnets. :ischooklib:`libdhcp_subnet_cmds.so`
+ modifies the local subnets configuration in the server's memory,
+ not in the database. Use :ischooklib:`libdhcp_cb_cmds.so` to manage the
+ subnets information in the database instead.
+
+.. note::
+
+ Using custom option formats requires creating definitions for these options.
+ Suppose a user wishes to set option data in the configuration backend. In
+ that case, we recommend specifying the definition for that option in the
+ configuration backend as well. It is essential when multiple servers are
+ managed via the configuration backend, and may differ in their
+ configurations. The option data parser can search for an option definition
+ appropriate for the server for which the option data is specified.
+
+ In a single-server deployment, or when all servers share the same
+ configuration file information, it is possible to specify option
+ definitions in the configuration files and option data in the configuration
+ backend. The server receiving a command to set option data must have a
+ valid definition in its configuration file, even when it sets option data
+ for another server.
+
+ It is not supported to specify option definitions in the configuration
+ backend and the corresponding option data in the server configuration files.
+
+CB Components
+-------------
+
+To use a MySQL configuration backend you must compile
+:ischooklib:`libdhcp_mysql_cb.so` and configure the DHCP servers to load it.
+It is compiled when the ``--with-mysql`` configuration switch is used during the Kea build.
+The MySQL C client libraries must be installed, as explained in :ref:`dhcp-install-configure`.
+
+To use a PostgreSQL configuration backend you must compile :ischooklib:`libdhcp_pgsql_cb.so`
+and configure the DHCP servers to load it. It is compiled when
+the ``--with-pgsql`` configuration switch is used during the Kea build. The PostgreSQL
+C client libraries must be installed, as explained in :ref:`dhcp-install-configure`.
+
+.. note::
+
+ An existing database schema must be upgraded to the latest schema
+ required by the particular Kea version using the :iscman:`kea-admin` tool,
+ as described in :ref:`kea-admin`.
+
+:ischooklib:`libdhcp_cb_cmds.so` provides a complete set of commands to manage the
+servers' configuration information within the database. This library can
+be attached to both DHCPv4 and DHCPv6 server instances. While it is
+possible to manage the configuration information without :ischooklib:`libdhcp_cb_cmds.so`
+with commonly available tools, such as MySQL Workbench or
+the command-line MySQL client, or by directly working with the database;
+these avenues are neither recommended nor supported.
+
+The DHCPv4 and DHCPv6 server-specific configurations of the CB, as well as
+the list of supported configuration parameters, can be found in
+:ref:`dhcp4-cb` and :ref:`dhcp6-cb`, respectively.
+
+.. _cb-sharing:
+
+Configuration Sharing and Server Tags
+-------------------------------------
+
+The configuration database is designed to store configuration information
+for multiple Kea servers. Depending on the use case, the entire configuration
+may be shared by all servers; parts of the configuration may be shared by
+multiple servers and the rest of the configuration may be different for these
+servers; or each server may have its own non-shared configuration.
+
+The configuration elements in the database are associated with the servers
+by "server tags." The server tag is an arbitrary string holding the name
+of the Kea server instance. The tags of the DHCPv4 and DHCPv6 servers are
+independent in the database, i.e. the same server tag can be created for
+both the DHCPv4 and the DHCPv6 server. The value is configured
+using the ``server-tag`` parameter in the ``Dhcp4`` or ``Dhcp6`` scope. The current
+server tag can be checked with the :isccmd:`server-tag-get` command.
+
+The server definition, which consists of the server tag and the server
+description, must be stored in the configuration database prior to creating
+the dedicated configuration for that server. In cases when all servers use
+the same configuration, e.g. a pair of servers running as High Availability
+peers, there is no need to configure the server tags for these
+servers in the database.
+
+Commands which contain the logical server `all` are applied to all servers
+connecting to the database. The `all` server cannot be
+deleted or modified, and it is not returned among other servers
+as a result of the :isccmd:`remote-server4-get-all`, :isccmd:`remote-server6-get-all` commands.
+
+In most cases, there are no server tags defined in the configuration
+database; all connecting servers get the same configuration
+regardless of the server tag they use. The server tag that a
+particular Kea instance presents to the database to fetch its configuration
+is specified in the Kea configuration file, using the
+`config-control` map (please refer to the :ref:`dhcp4-cb-json` and
+:ref:`dhcp6-cb-json` for details). All Kea instances presenting the same
+server tag to the configuration database
+are given the same configuration.
+
+It is the administrator's choice whether
+multiple Kea instances use the same server tag or each Kea instance uses
+a different server tag. There is no requirement that the instances
+running on the same physical or virtual machine use the same server tag. It is
+even possible to configure the Kea server without assigning it a server tag.
+In such a case the server will be given the configuration specified for `all`
+servers.
+
+To differentiate between different Kea server configurations, a
+list of the server tags used by the servers must be stored in the
+database. For the DHCPv4 and DHCPv6 servers, it can be done using the
+:isccmd:`remote-server4-set` and :isccmd:`remote-server6-set` commands. The
+server tags can then be used to associate the configuration information with
+the servers. However, it is important to note that some DHCP
+configuration elements may be associated with multiple server tags (known
+as "shareable" elements), while
+other configuration elements may be associated with only one
+server tag ("non-shareable" elements). The :ref:`dhcp4-cb`
+and :ref:`dhcp6-cb` sections list the DHCP-specific shareable and
+non-shareable configuration elements; however, in this section we
+briefly explain the differences between them.
+
+A shareable configuration element is one which has some unique
+property identifying it, and which may appear only once in
+the database. An example of a shareable DHCP element is a subnet
+instance: the subnet is a part of the network topology and we assume
+that any particular subnet may have only one definition within this
+network. Each subnet has two unique identifiers: the subnet identifier and the
+subnet prefix. The subnet identifier is used in Kea to uniquely
+identify the subnet within the network and to connect it with other configuration elements,
+e.g. in host reservations. Some commands provided by
+:ischooklib:`libdhcp_cb_cmds.so` allow the subnet
+information to be accessed by either subnet identifier or prefix, and explicitly prohibit
+using the server tag to access the subnet. This is because, in
+general, the subnet definition is associated with multiple servers
+rather than a single server. In fact, it may even be associated
+with no servers (unassigned). Still, the unassigned subnet has an
+identifier and prefix which can be used to access the subnet.
+
+A shareable configuration element may be associated with multiple
+servers, one server, or no servers. Deletion of the server which is
+associated with the shareable element does not cause the deletion of
+the shareable element. It merely deletes the association of the
+deleted server with the element.
+
+Unlike a shareable element, a non-shareable element must not be
+explicitly associated with more than one server and must not exist
+after the server is deleted (must not remain unassigned). A
+non-shareable element only exists within the context of the server.
+An example of a non-shareable element in DHCP is a global
+parameter, e.g. `renew-timer`. The renew timer
+is the value to be used by a particular server and only this
+server. Other servers may have their respective renew timers
+set to the same or different values. The renew timer
+parameter has no unique identifier by which it could be
+accessed, modified, or otherwise used. Global parameters like
+the renew timer can be accessed by the parameter name and the
+tag of the server for which they are configured. For example, the
+:isccmd:`remote-global-parameter4-get` and
+:isccmd:`remote-global-parameter6-get` commands allow
+the value of the global parameter to be fetched by the parameter name and
+the server name. Getting the global parameter only by its name (without
+specifying the server tag) is not possible, because there may be many
+global parameters with a given name in the database.
+
+When the server associated with a non-shareable configuration element
+is deleted, the configuration element is automatically deleted from
+the database along with the server because the non-shareable element
+must be always assigned to a server (or the logical server `all`).
+
+The terms "shareable" and "non-shareable" only apply to associations
+with user-defined servers; all configuration elements associated with
+the logical server `all` are by definition shareable. For example: the
+`renew-timer` associated with `all` servers is used
+by all servers connecting to the database which do not have their specific
+renew timers defined. In a special case, when none of the configuration
+elements are associated with user-defined servers, the entire
+configuration in the database is shareable because all its pieces
+belong to `all` servers.
+
+.. note::
+
+ Be very careful when associating configuration elements with
+ different server tags. The configuration backend does not protect
+ against some possible misconfigurations that may arise from the
+ wrong server tags' assignments. For example: if a shared
+ network is assigned to one server and the subnets belonging to this shared network
+ to another server, the servers will fail upon trying to fetch and
+ use this configuration. The server fetching the subnets will be
+ aware that the subnets are associated with the shared network, but
+ the shared network will not be found by this server since it doesn't
+ belong to it. In such a case, both the shared network and the subnets
+ should be assigned to the same set of servers.
diff --git a/doc/sphinx/arm/config-templates.rst b/doc/sphinx/arm/config-templates.rst
new file mode 100644
index 0000000..caff036
--- /dev/null
+++ b/doc/sphinx/arm/config-templates.rst
@@ -0,0 +1,74 @@
+.. _config-templates:
+
+Configuration Templates
+=======================
+
+The following sections include configuration templates for
+certain deployment types. The example configuration files are also available in the Kea sources,
+in the ``doc/examples`` directory.
+
+.. include:: template-power-user-home.md
+
+Some tweaking of these templates may be required to match specific system needs: at a
+minimum, the lines highlighted in yellow must be adjusted to match the actual deployment.
+
+Server1's Control Agent configuration file:
+
+.. literalinclude:: template-power-user-home-ca-1.conf
+ :language: javascript
+ :emphasize-lines: 9, 12
+ :linenos:
+
+Server1's DHCPv4 configuration file:
+
+.. literalinclude:: template-power-user-home-dhcp4-1.conf
+ :language: javascript
+ :emphasize-lines: 25, 79, 84, 124, 136, 151, 155, 158-162, 170-184, 194-203
+ :linenos:
+
+Server2's Control Agent configuration file:
+
+.. literalinclude:: template-power-user-home-ca-2.conf
+ :language: javascript
+ :emphasize-lines: 9, 12
+ :linenos:
+
+Server2's DHCPv4 configuration file:
+
+.. literalinclude:: template-power-user-home-dhcp4-2.conf
+ :language: javascript
+ :emphasize-lines: 25, 79, 84, 124, 136, 151, 155, 158-162, 170-184, 194-203
+ :linenos:
+
+.. include:: template-ha-mt-tls.md
+
+Some tweaking of these templates may be required to match specific system needs: at a
+minimum, the lines highlighted in yellow must be adjusted to match the actual deployment.
+
+Server1's Control Agent configuration file:
+
+.. literalinclude:: template-ha-mt-tls-ca-1.conf
+ :language: javascript
+ :emphasize-lines: 10, 14, 18, 21, 24, 36
+ :linenos:
+
+Server1's DHCPv4 configuration file:
+
+.. literalinclude:: template-ha-mt-tls-dhcp4-1.conf
+ :language: javascript
+ :emphasize-lines: 25, 38-54, 98, 103, 139-152, 161, 163, 165, 167, 181, 183, 185, 187, 204, 208, 211-215
+ :linenos:
+
+Server2's Control Agent configuration file:
+
+.. literalinclude:: template-ha-mt-tls-ca-2.conf
+ :language: javascript
+ :emphasize-lines: 10, 14, 18, 21, 24, 36
+ :linenos:
+
+Server2's DHCPv4 configuration file:
+
+.. literalinclude:: template-ha-mt-tls-dhcp4-2.conf
+ :language: javascript
+ :emphasize-lines: 25, 38-54, 98, 103, 139-152, 161, 163, 165, 167, 181, 183, 185, 187, 204, 208, 211-215
+ :linenos:
diff --git a/doc/sphinx/arm/config.rst b/doc/sphinx/arm/config.rst
new file mode 100644
index 0000000..46833a3
--- /dev/null
+++ b/doc/sphinx/arm/config.rst
@@ -0,0 +1,339 @@
+.. _kea-config:
+
+*****************
+Kea Configuration
+*****************
+
+Kea uses JSON structures to represent server configurations. The
+following sections describe how the configuration structures are
+organized.
+
+.. _json:
+
+JSON Configuration
+==================
+
+JSON is the notation used throughout the Kea project. The most obvious
+usage is for the configuration file, but JSON is also used for sending
+commands over the Management API (see :ref:`ctrl-channel`) and for
+communicating between DHCP servers and the DDNS update daemon.
+
+Typical usage assumes that the servers are started from the command
+line, either directly or using a script, e.g. :iscman:`keactrl`. The
+configuration file is specified upon startup using the ``-c`` parameter.
+
+.. _json-format:
+
+JSON Syntax
+-----------
+
+Configuration files for the DHCPv4, DHCPv6, DDNS, Control Agent, and
+NETCONF modules are defined in an extended JSON format. Basic JSON is
+defined in `RFC 7159 <https://tools.ietf.org/html/rfc7159>`__ and `ECMA
+404 <https://www.ecma-international.org/publications/standards/Ecma-404.htm>`__.
+In particular, the only boolean values allowed are true or false (all
+lowercase). The capitalized versions (True or False) are not accepted.
+
+Even though the JSON standard (ECMA 404) does not require JSON objects
+(i.e. name/value maps) to have unique entries, Kea implements them
+using a C++ STL map with unique entries. Therefore, if there are multiple
+values for the same name in an object/map, the last value overwrites previous values.
+Since Kea 1.9.0, configuration file parsers raise a syntax error in such cases.
+
+Kea components use extended JSON with additional features allowed:
+
+- Shell comments: any text after the hash (#) character is ignored.
+
+- C comments: any text after the double slashes (//) character is
+ ignored.
+
+- Multiline comments: any text between /\* and \*/ is ignored. This
+ comment can span multiple lines.
+
+- File inclusion: JSON files can include other JSON files by using a
+ statement of the form \<?include "file.json"?\>.
+
+- Extra commas: to remove the inconvenience of errors caused by leftover commas
+ after making changes to configuration. While parsing, a warning is printed
+ with the location of the comma to give the user the ability to correct a
+ potential mistake.
+
+.. warning::
+
+ These features are meant to be used in a JSON configuration file.
+ Their usage in any other way may result in errors.
+
+The configuration file consists of a single object (often colloquially
+called a map) started with a curly bracket. It comprises only one of
+the "Dhcp4", "Dhcp6", "DhcpDdns", "Control-agent", or "Netconf" objects.
+It is possible to define additional elements but they will be ignored.
+
+A very simple configuration for DHCPv4 could look like this:
+
+::
+
+ # The whole configuration starts here.
+ {
+ # DHCPv4 specific configuration starts here.
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [ "eth0" ],
+ "dhcp-socket-type": "raw"
+ },
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+ "subnet4": [{
+ "pools": [ { "pool": "192.0.2.1-192.0.2.200" } ],
+ "subnet": "192.0.2.0/24",
+ "id": 1
+ }],
+
+ # Now loggers are inside the DHCPv4 object.
+ "loggers": [{
+ "name": "*",
+ "severity": "DEBUG"
+ }]
+ }
+
+ # The whole configuration structure ends here.
+ }
+
+More examples are available in the installed ``share/doc/kea/examples``
+directory.
+
+To avoid repetition of mostly similar structures, examples in the rest
+of this guide will showcase only the subset of parameters appropriate
+for a given context. For example, when discussing the IPv6 subnets
+configuration in DHCPv6, only subnet6 parameters will be mentioned. It
+is implied that the remaining elements (the global map that holds Dhcp6)
+are present, but they are omitted for clarity. Usually,
+locations where extra parameters may appear are denoted by an ellipsis
+(...).
+
+.. _user-context:
+
+Comments and User Context
+-------------------------
+
+Shell, C, or C++ style comments are all permitted in the JSON configuration file if
+the file is used locally. This is convenient and works in simple cases where
+the configuration is kept statically using a local file. However, since comments
+are not part of JSON syntax, most JSON tools detect them as
+errors. Another problem with them is that once Kea loads its configuration, the
+shell, C, and C++ style comments are ignored. If commands such as
+:isccmd:`config-get` or :isccmd:`config-write` are used, those comments are lost. An example of such
+comments was presented in the previous section.
+
+Historically, to address the problem, Kea code allowed the use of `comment` strings
+as valid JSON entities. This had the benefit of being retained through various
+operations (such as :isccmd:`config-get`), or allowing processing by JSON tools. An
+example JSON comment looks like this:
+
+::
+
+ "Dhcp4": {
+ "subnet4": [{
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "pools": [{ "pool": "192.0.2.10 - 192.0.2.20" }],
+ "comment": "second floor"
+ }]
+ }
+
+However, the facts that the comment could only be a single line, and that it was not
+possible to add any other information in a more structured form, were frustrating. One specific
+example was a request to add floor levels and building numbers to subnets. This
+was one of the reasons why the concept of user context was introduced. It
+allows adding an arbitrary JSON structure to most Kea configuration structures.
+
+This has a number of benefits compared to earlier approaches. First, it is fully
+compatible with JSON tools and Kea commands. Second, it allows storing simple
+comment strings, but it can also store much more complex data, such as
+multiple lines (as a string array), extra typed data (such as floor numbers being
+actual numbers), and more. Third, the data is exposed to hooks, so it is possible
+to develop third-party hooks that take advantage of that extra information. An
+example user context looks like this:
+
+::
+
+ "Dhcp4": {
+ "subnet4": [{
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "pools": [{ "pool": "192.0.2.10 - 192.0.2.20" }],
+ "user-context": {
+ "comment": "second floor",
+ "floor": 2
+ }
+ }]
+ }
+
+User contexts can store an arbitrary data file as long as it has valid JSON
+syntax and its top-level element is a map (i.e. the data must be enclosed in
+curly brackets). However, some hook libraries may expect specific formatting;
+please consult the specific hook library documentation for details.
+
+In a sense the user-context mechanism has superseded the JSON comment
+capabilities; ISC encourages administrators to use user-context instead of
+the older mechanisms. To promote this way of storing comments, Kea compared
+converts JSON comments to user-context on the fly.
+
+However, if the configuration uses the old JSON
+comment, the :isccmd:`config-get` command returns a slightly modified
+configuration. It is not uncommon for a call for :isccmd:`config-set` followed by a
+:isccmd:`config-get` to receive a slightly different structure.
+The best way to avoid this problem is simply to abandon JSON comments and
+use user-context.
+
+Kea supports user contexts at the following levels: global scope,
+interfaces configuration, shared networks,
+subnets, client classes, option data and definitions, host
+reservations, control socket, DHCP-DDNS, loggers, leases, and server ID. These
+are supported in both DHCPv4 and DHCPv6, with the exception of server ID,
+which is DHCPv6 only.
+
+User context can be added and edited in structures supported by commands.
+
+We encourage Kea users to utilize these functions to store information
+used by other systems and custom hooks.
+
+For example, the :isccmd:`subnet4-update` command can be used to add user context data
+to an existing subnet.
+
+::
+
+ {
+ "subnet4": [ {
+ "id": 1,
+ "subnet": "10.20.30.0/24",
+ "user-context": {
+ "building": "Main",
+ "floor": 1
+ }
+ } ]
+ }
+
+The same can be done with many other commands, like :isccmd:`lease6-add`, etc.
+
+Kea also uses user context to store non-standard data.
+Currently, only :ref:`dhcp4-store-extended-info` uses this feature.
+
+When enabled, it adds the ISC key in ``user-context`` to differentiate automatically
+added content.
+
+Example of relay information stored in a lease:
+
+::
+
+ {
+ "arguments": {
+ "client-id": "42:42:42:42:42:42:42:42",
+ "cltt": 12345678,
+ "fqdn-fwd": false,
+ "fqdn-rev": true,
+ "hostname": "myhost.example.com.",
+ "hw-address": "08:08:08:08:08:08",
+ "ip-address": "192.0.2.1",
+ "state": 0,
+ "subnet-id": 44,
+ "valid-lft": 3600,
+ "user-context": {
+ "ISC": {
+ "relays": [
+ {
+ "hop": 2,
+ "link": "2001:db8::1",
+ "peer": "2001:db8::2"
+ },
+ {
+ "hop": 1,
+ "link": "2001:db8::3",
+ "options": "0x00C800080102030405060708",
+ "peer": "2001:db8::4"
+ }]
+ }
+ }
+ }
+ }
+
+
+User context can store configuration for multiple hooks and comments at once.
+
+For a discussion about user context used in hooks, see :ref:`user-context-hooks`.
+
+
+Simplified Notation
+-------------------
+
+It is sometimes convenient to refer to a specific element in the
+configuration hierarchy. Each hierarchy level is separated by a slash.
+If there is an array, a specific instance within that array is
+referenced by a number in square brackets (with numbering starting at
+zero). For example, in the above configuration the valid-lifetime in the
+Dhcp4 component can be referred to as Dhcp4/valid-lifetime, and the pool
+in the first subnet defined in the DHCPv4 configuration as
+Dhcp4/subnet4[0]/pool.
+
+
+.. include:: config-backend.rst
+
+
+Configuration Files Inclusion
+-----------------------------
+
+The parser provides the ability to include files. The syntax was chosen
+to look similar to how Apache includes PHP scripts in HTML code. This particular
+syntax was chosen to emphasize that the include directive is an additional
+feature and not a part of JSON syntax.
+
+The inclusion is implemented as a stack of files. You can use the include directive
+in nested includes. Up to ten nesting levels are supported. This arbitrarily chosen
+limit is protection against recursive inclusions.
+
+The include directive has the form:
+
+::
+
+ <?include "[PATH]"?>
+
+The *[PATH]* pattern should be replaced with an absolute path or a path relative to
+the current working directory at the time the Kea process was launched.
+
+To include one file from another, use the following syntax:
+
+.. code-block:: javascript
+
+ {
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ "*" ]},
+ "preferred-lifetime": 3000,
+ "rebind-timer": 2000,
+ "renew-timer": 1000,
+ <?include "subnets.json"?>
+ "valid-lifetime": 4000
+ }
+ }
+
+where the content of "subnets.json" may be:
+
+::
+
+ {
+ "subnet4": [
+ {
+ "id": 123,
+ "subnet": "192.0.2.0/24"
+ },
+ {
+ "id": 234,
+ "subnet": "192.0.3.0/24"
+ },
+ {
+ "id": 345,
+ "subnet": "10.0.0.0/8"
+ }
+ ],
+ ...
+ }
diff --git a/doc/sphinx/arm/congestion-handling.rst b/doc/sphinx/arm/congestion-handling.rst
new file mode 100644
index 0000000..68b7913
--- /dev/null
+++ b/doc/sphinx/arm/congestion-handling.rst
@@ -0,0 +1,129 @@
+.. _congestion-handling:
+
+*******************
+Congestion Handling
+*******************
+
+.. _congestion-handling-background:
+
+What is Congestion?
+===================
+
+Congestion occurs when servers are subjected to client queries faster
+than those queries can be processed. As a result, the servers begin accumulating
+a backlog of pending queries. The longer the high rate of traffic
+continues, the farther behind the servers fall. Depending on the client
+implementations, those that fail to get leases either give up or simply
+continue to retry forever. In the former case, the server may eventually
+recover, but the latter case is a vicious cycle from which the server is
+unable to escape.
+
+Congestion typically occurs when there is a network event that causes overly large
+numbers of clients to simultaneously need leases, such as recovery after
+a network outage. In a well-planned deployment, the number and capacity of servers is
+matched to the maximum expected client load. If the load is routinely too
+heavy, then the deployment needs to be re-evaluated.
+
+The goal of congestion handling is to help servers mitigate the peak in
+traffic by fulfilling as many of the most relevant requests as possible
+until the congestion subsides.
+
+.. _congestion-handling-solution:
+
+Configuring Congestion Handling
+===============================
+
+Congestion handling
+offers the ability to configure the server to use a separate thread to
+read packets from the interface socket buffers. As the thread reads
+packets from the buffers, they are added to an internal packet queue,
+and the server's main application thread processes packets from this
+queue rather than from the socket buffers. By structuring it this way, a
+configurable layer has been introduced which can make decisions on which
+packets to process, how to store them, and the order in which they are
+processed by the server.
+
+The default packet queue implementation for both :iscman:`kea-dhcp4` and :iscman:`kea-dhcp6`
+is a simple ring buffer. Once it reaches capacity, new packets get added
+to the back of the queue by discarding packets from the front of the
+queue. Rather than always discarding the newest packets, Kea now always
+discards the oldest packets. The capacity of the buffer, i.e. the maximum
+number of packets the buffer can contain, is configurable. A reasonable
+starting point is to match the capacity to the number of leases
+per second a specific installation of Kea can handle. This
+figure varies widely depending on the specifics of an individual deployment.
+
+As there is no one algorithm that can best handle the dynamics of all
+sites, and because over time new approaches will evolve, the packet
+queue is implemented as a plug-in, which can be replaced by a custom queue
+implementation via a hook library. This should make it straightforward
+for interested parties to experiment with their own solutions.
+(Developers can refer to ``isc::dhcp::PacketQueue`` and
+``isc::dhcp::PacketQueueMgr``, described in the
+`Kea Developer's Guide <https://reports.kea.isc.org/dev_guide/index.html>`__.)
+
+Packet queue behavior is configured in both :iscman:`kea-dhcp4` and :iscman:`kea-dhcp6`
+servers through an optional, top-level, configuration element,
+``dhcp-queue-control``. Omitting this element disables packet queueing:
+
+::
+
+ "dhcp-queue-control": {
+ "enable-queue": true, // true|false
+ "queue-type": "queue type",
+ "capacity" : 256 // n packets
+ }
+
+where:
+
+- ``enable-queue`` - enables or disables packet queueing.
+ When ``true``, the server processes packets from the packet queue, which
+ is filled by a separate thread. When ``false``, the server processes
+ packets directly from the socket buffers in the main thread. It is
+ disabled (``false``) by default.
+
+- ``queue-type`` - the name of the queue implementation to use. This value
+ exists so that custom implementations can be registered (via a hook
+ library) and then selected. There is a default packet queue
+ implementation that is pre-registered during server start up:
+ "kea-ring4" for :iscman:`kea-dhcp4` and "kea-ring6" for :iscman:`kea-dhcp6`.
+
+- ``capacity`` - this is the maximum number of packets the
+ queue can hold before packets are discarded. The optimal value for
+ this is extremely site-dependent. The default value is 64 for both
+ "kea-ring4" and "kea-ring6".
+
+The following example enables the default packet queue for :iscman:`kea-dhcp4`,
+with a queue capacity of 250 packets:
+
+::
+
+ "Dhcp4":
+ {
+ "dhcp-queue-control": {
+ "enable-queue": true,
+ "queue-type": "kea-ring4",
+ "capacity" : 250
+ },
+ ...
+ }
+
+The following example enables the default packet queue for :iscman:`kea-dhcp6`,
+with a queue capacity of 300 packets:
+
+::
+
+ "Dhcp6":
+ {
+ "dhcp-queue-control": {
+ "enable-queue": true,
+ "queue-type": "kea-ring6",
+ "capacity" : 300
+ },
+ ...
+ }
+
+.. note::
+
+ Congestion handling is currently incompatible with multi-threading;
+ when both are enabled, congestion handling is silently disabled.
diff --git a/doc/sphinx/arm/ctrl-channel.rst b/doc/sphinx/arm/ctrl-channel.rst
new file mode 100644
index 0000000..b6a5cd8
--- /dev/null
+++ b/doc/sphinx/arm/ctrl-channel.rst
@@ -0,0 +1,906 @@
+.. _ctrl-channel:
+
+**************
+Management API
+**************
+
+A classic approach to daemon configuration assumes that the server's
+configuration is stored in configuration files and, when the
+configuration is changed, the daemon is restarted. This approach has the
+significant disadvantage of introducing periods of downtime when client
+traffic is not handled. Another risk is that if the new configuration is
+invalid for any reason, the server may refuse to start, which will
+further extend the downtime period until the issue is resolved.
+
+To avoid such problems, the DHCPv4, DHCPv6, and D2 servers in Kea include
+support for a mechanism that allows online reconfiguration without
+requiring server shutdown. Both servers can be instructed to open
+control sockets, which is a communications channel. The server is able
+to receive commands on that channel, act on them, and report back
+status.
+
+The DHCPv4, DHCPv6, and D2 servers receive commands over the UNIX domain
+sockets. For details on how to configure these sockets, see
+:ref:`dhcp4-ctrl-channel` and :ref:`dhcp6-ctrl-channel`. While
+it is possible to control the servers directly using UNIX domain sockets,
+that requires that the controlling client be running on the same machine
+as the server. SSH is usually used to connect remotely to the controlled
+machine.
+
+Network administrators usually prefer using some form of a RESTful API
+to control the servers, rather than using UNIX domain sockets directly.
+Therefore, Kea includes a component called the Control Agent (CA), which
+exposes a RESTful API to the controlling clients and can forward
+commands to the respective Kea services over the UNIX domain sockets.
+The CA configuration is described in
+:ref:`agent-configuration`.
+
+The HTTP requests received by the CA contain the control commands
+encapsulated within HTTP requests. Simply speaking, the CA is
+responsible for stripping the HTTP layer from the received commands and
+forwarding the commands in a JSON format over the UNIX domain sockets to
+the respective services. Because the CA receives commands for all
+services, it requires additional "forwarding" information to be included
+in the client's messages. This forwarding information is carried within
+the ``service`` parameter of the received command. If the ``service``
+parameter is not included, or if the parameter is a blank list, the CA
+assumes that the control command is targeted at the CA itself and
+attempts to respond.
+
+Control connections over both HTTP and UNIX domain sockets are guarded
+with timeouts. The timeout value is set to 10 seconds and is not
+configurable.
+
+This API can be used by external tools to manage and monitor Kea operation.
+An example of such a monitoring tool is ISC's Stork. For details, see
+:ref:`stork`.
+
+.. _ctrl-channel-syntax:
+
+Data Syntax
+===========
+
+Communication over the control channel is conducted using JSON
+structures. If configured, Kea opens a socket and listens for
+incoming connections. A process connecting to this socket is expected to
+send JSON commands structured as follows:
+
+::
+
+ {
+ "command": "foo",
+ "service": [ "dhcp4" ],
+ "arguments": {
+ "param1": "value1",
+ "param2": "value2",
+ ...
+ }
+ }
+
+The same command sent over the RESTful interface to the CA has the
+following structure:
+
+::
+
+ POST / HTTP/1.1\r\n
+ Content-Type: application/json\r\n
+ Content-Length: 147\r\n\r\n
+ {
+ "command": "foo",
+ "service": [ "dhcp4" ],
+ "arguments": {
+ "param1": "value1",
+ "param2": "value2",
+ ...
+ }
+ }
+
+The ``command`` parameter contains the name of the command to execute and it
+is mandatory.
+The ``arguments`` map contains the parameters required to carry out the
+given command. The exact content and format of the map are command-specific.
+
+The ``service`` list contains the servers at which the control command is
+targeted. In the example above, the control command is targeted at the
+DHCPv4 server. In most cases, the CA simply forwards this command to
+the DHCPv4 server for processing via a UNIX domain socket. Sometimes,
+the command including a service value may also be processed by the CA,
+if the CA is running a hook library which handles such a command for
+the given server. As an example, the hook library loaded by the CA may
+perform some operations on the database, such as adding host
+reservations, modifying leases, etc. An advantage of performing
+DHCPv4-specific administrative operations in the CA, rather than
+forwarding it to the DHCPv4 server, is the ability to perform these
+operations without disrupting the DHCPv4 service, since the DHCPv4
+server does not have to stop processing DHCP messages to apply changes to
+the database. Nevertheless, these situations are rather rare; in
+most cases, when the ``service`` parameter contains a name of the
+service, the commands are simply forwarded by the CA. The forwarded
+command includes the ``service`` parameter, but this parameter is ignored
+by the receiving server. This parameter is only meaningful to the CA.
+
+If the command received by the CA does not include a ``service``
+parameter or this list is empty, the CA simply processes this message on
+its own. For example, a :isccmd:`config-get` command which includes no service
+parameter returns the Control Agent's own configuration. The :isccmd:`config-get`
+command with a service value "dhcp4" is forwarded to the DHCPv4 server and
+returns the DHCPv4 server's configuration.
+
+The following list shows the mapping of the values carried within the
+``service`` parameter to the servers to which the commands are
+forwarded:
+
+- ``dhcp4`` - the command is forwarded to the :iscman:`kea-dhcp4` server.
+
+- ``dhcp6`` - the command is forwarded to the :iscman:`kea-dhcp6` server.
+
+- ``d2`` - the command is forwarded to the :iscman:`kea-dhcp-ddns` server.
+
+The server processing the incoming command sends a response of the
+form:
+
+::
+
+ {
+ "result": 0, // 0|1|2|3|4
+ "text": "textual description",
+ "arguments": {
+ "argument1": "value1",
+ "argument2": "value2",
+ ...
+ }
+ }
+
+The ``result`` value is a status code indicating a result of the command. The
+following general status codes are currently supported:
+
+- ``0`` - the command has been processed successfully.
+- ``1`` - a general error or failure has occurred during the command processing.
+- ``2`` - the specified command is unsupported by the server receiving it.
+- ``3`` - the requested operation has been completed but the requested
+ resource was not found. This status code is returned when a command
+ returns no resources or affects no resources.
+- ``4`` - the well-formed command has been processed but the requested
+ changes could not be applied, because they were in conflict with the
+ server state or its notion of the configuration.
+
+For example, a well-formed command that requests a subnet that exists
+in a server's configuration returns the result 0. If the server encounters
+an error condition, it returns 1. If the command asks for an IPv6 subnet,
+but was sent to a DHCPv4 server, it returns 2. If the query asks for a
+subnet with ``subnet-id`` that has matches, the result is 3.
+If the command attempts to update a lease but the specified ``subnet-id``
+does not match the identifier in the server's configuration, the result
+is 4.
+
+Hook libraries can sometimes return additional status codes specific
+to their use cases.
+
+The ``text`` field typically appears when the result is non-zero and
+contains a description of the error encountered, but it often also
+appears for successful outcomes. The exact text is command-specific, but
+in general uses plain English to describe the outcome of the command.
+The ``arguments`` map contains additional data values returned by the server
+which are specific to the command issued. The map may be present, but that
+depends on the specific command.
+
+.. note::
+
+ Since Kea 1.9.7, it is possible to put comments in commands as
+ in the configuration file. For instance:
+
+::
+
+ {
+ "command": "foo",
+ // service is a list
+ "service": [ "dhcp4" ],
+ # command arguments are here.
+ "arguments": {
+ "param1": "value1",
+ ...
+ /*
+ "param2": "value2",
+ ...
+ */
+ }
+ }
+
+.. _ctrl-channel-control-agent-command-response-format:
+
+Control Agent Command Response Format
+=====================================
+
+When sending commands via the Control Agent, it is possible to specify
+multiple services at which the command is targeted. CA forwards this
+command to each service individually. Thus, the CA response to the
+controlling client is always wrapped in an array (JSON list) of
+individual responses. For example, the response for a command sent
+to one service would be structured as follows:
+
+::
+
+ [
+ {
+ "result": 0, // 0|1|2|3|4
+ "text": "textual description",
+ "arguments": {
+ "argument1": "value1",
+ "argument2": "value2",
+ ...
+ }
+ }
+ ]
+
+
+If the command is sent to more than one service, the array would
+contain responses from each service, in the order they were requested:
+
+::
+
+ [
+ {
+ "result": 0, // 0|1|2|3|4
+ "text": "textual description",
+ "arguments": {
+ "argument1": "value1",
+ "argument2": "value2",
+ ...
+ }
+ },
+ {
+ "result": 0, // 0|1|2|3|4
+ "text": "textual description",
+ "arguments": {
+ "argument1": "value1",
+ "argument2": "value2",
+ ...
+ }
+ },
+ ...
+ ]
+
+An exception to this are authentication or authorization errors which cause CA
+to reject the command entirely. The response to such an error is formatted
+as a single entry (JSON map) as follows:
+
+::
+
+ {
+ "result": 403,
+ "text": "Forbidden"
+ }
+
+
+These types of errors are possible on systems configured for either basic
+authentication or agents that load :ischooklib:`libca_rbac.so`.
+
+.. _ctrl-channel-client:
+
+Using the Control Channel
+=========================
+
+The easiest way to start interacting with the control API is to use
+common UNIX/Linux tools such as ``socat`` and ``curl``.
+
+In order to control the given Kea service via a UNIX domain socket, use
+``socat`` in interactive mode as follows:
+
+.. code-block:: console
+
+ $ socat UNIX:/path/to/the/kea/socket -
+
+or in batch mode, include the "ignoreeof" option as shown below to
+ensure ``socat`` waits long enough for the server to respond:
+
+.. code-block:: console
+
+ $ echo "{ some command...}" | socat UNIX:/path/to/the/kea/socket -,ignoreeof
+
+where ``/path/to/the/kea/socket`` is the path specified in the
+``Dhcp4/control-socket/socket-name`` parameter in the Kea configuration
+file. Text passed to ``socat`` is sent to Kea and the responses received
+from Kea are printed to standard output. This approach communicates with
+the specific server directly and bypasses the Control Agent.
+
+It is also easy to open a UNIX socket programmatically. An example of a
+simple client written in C is available in the Kea Developer's Guide, in
+the Control Channel Overview chapter, in the
+`Using Control Channel <https://reports.kea.isc.org/dev_guide/d2/d96/ctrlSocket.html#ctrlSocketClient>`__
+section.
+
+To use Kea's RESTful API with ``curl``, use the following:
+
+.. code-block:: console
+
+ $ curl -X POST -H "Content-Type: application/json" -d '{ "command": "config-get", "service": [ "dhcp4" ] }' http://ca.example.org:8000/
+
+This assumes that the Control Agent is running on host
+``ca.example.org`` and is running the RESTful service on port 8000.
+
+.. _commands-common:
+
+Commands Supported by Both the DHCPv4 and DHCPv6 Servers
+========================================================
+
+.. isccmd:: build-report
+.. _command-build-report:
+
+The ``build-report`` Command
+----------------------------
+
+The :isccmd:`build-report` command returns on the control channel what the
+command line ``-W`` argument displays, i.e. the embedded content of the
+``config.report`` file. This command does not take any parameters.
+
+::
+
+ {
+ "command": "build-report"
+ }
+
+.. isccmd:: config-get
+.. _command-config-get:
+
+The ``config-get`` Command
+--------------------------
+
+The :isccmd:`config-get` command retrieves the current configuration used by the
+server. This command does not take any parameters. The configuration
+returned is roughly equal to the configuration that was loaded using the
+``-c`` command-line option during server start-up, or was later set using the
+:isccmd:`config-set` command. However, there may be certain differences, as
+comments are not retained. If the original configuration used file
+inclusion, the returned configuration includes all parameters from
+all included files. Starting with 2.4.0, the successful response also
+contains a SHA-256 digest that can be used to easily determine if a
+configuration has changed or not.
+
+.. warning::
+
+ The returned configuration is not redacted, i.e. it
+ contains database passwords in plain text, if those were specified in the
+ original configuration. Care should be taken not to expose the command
+ channel to unprivileged users.
+
+An example command invocation looks like this:
+
+::
+
+ {
+ "command": "config-get"
+ }
+
+.. isccmd:: config-hash-get
+.. _command-config-hash-get:
+
+The ``config-hash-get`` Command
+-------------------------------
+
+The ``config-hash-get`` command retrieves the SHA-256 hash of the current
+configuration used by the server. This command does not take any parameters.
+The returned hash can be used to detect configuration changes.
+
+An example command invocation looks like this:
+
+::
+
+ {
+ "command": "config-hash-get"
+ }
+
+And the server's response:
+
+::
+
+ {
+ "result": 0,
+ "arguments": {
+ "hash": "5C3C90EF7035249E2FF74D003C19F34EE0B83A3D329E741B52B2EF95A2C9CC5C"
+ }
+ }
+
+Starting with 2.4.0, also ``config-set`` and ``config-get`` return the SHA-256 hash
+of the new or current configuration. This may be used to later determine if a configuration
+has changed or not.
+
+.. isccmd:: config-reload
+.. _command-config-reload:
+
+The ``config-reload`` Command
+-----------------------------
+
+The :isccmd:`config-reload` command instructs Kea to load again the
+configuration file that was used previously. This operation is useful if
+the configuration file has been changed by some external source; for
+example, a system administrator can tweak the configuration file and use this
+command to force Kea pick up the changes.
+
+Caution should be taken when mixing this with the :isccmd:`config-set` command. Kea
+remembers the location of the configuration file it was started with,
+and this configuration can be significantly changed using the :isccmd:`config-set`
+command. When :isccmd:`config-reload` is issued after :isccmd:`config-set`, Kea attempts
+to reload its original configuration from the file, possibly losing all
+changes introduced using :isccmd:`config-set` or other commands.
+
+The :isccmd:`config-reload` command does not take any parameters. An example command
+invocation looks like this:
+
+::
+
+ {
+ "command": "config-reload"
+ }
+
+If the configuration file is incorrect, reloading it can raise an error
+which leaves the server in an unusable state. See :ref:`command-config-set`
+to learn how to recover from a non-working server.
+
+.. isccmd:: config-test
+.. _command-config-test:
+
+The ``config-test`` Command
+---------------------------
+
+The :isccmd:`config-test` command instructs the server to check whether the new
+configuration supplied in the command's arguments can be loaded. The
+supplied configuration is expected to be the full configuration for the
+target server, along with an optional logger configuration. The configuration
+is sanity-checked to the extent possible without the server actually
+attempting to load it; it is possible for a configuration which successfully
+passes this command to still fail in the :isccmd:`config-set` command or at launch
+time. The structure of the command is as follows:
+
+::
+
+ {
+ "command": "config-test",
+ "arguments": {
+ "<server>": {
+ }
+ }
+ }
+
+where <server> is the configuration element name for a given server, such
+as "Dhcp4" or "Dhcp6". For example:
+
+::
+
+ {
+ "command": "config-test",
+ "arguments": {
+ "Dhcp6": {
+ ...
+ }
+ }
+ }
+
+The server's response contains a numeric code, ``result`` (0 for
+success, non-zero on failure), and a string, ``text``, describing the
+outcome:
+
+::
+
+ {"result": 0, "text": "Configuration seems sane..." }
+
+ or
+
+ {"result": 1, "text": "unsupported parameter: BOGUS (<string>:16:26)" }
+
+.. isccmd:: config-write
+.. _command-config-write:
+
+The ``config-write`` Command
+----------------------------
+
+The :isccmd:`config-write` command instructs the Kea server to write its current
+configuration to a file on disk. It takes one optional argument, called
+"filename", that specifies the name of the file to write the
+configuration to. If not specified, the name used when starting Kea
+(passed as a ``-c`` argument) is used. If a relative path is specified,
+Kea writes its files only in the directory where it is running.
+
+An example command invocation looks like this:
+
+::
+
+ {
+ "command": "config-write",
+ "arguments": {
+ "filename": "config-modified-2017-03-15.json"
+ }
+ }
+
+.. isccmd:: leases-reclaim
+.. _command-leases-reclaim:
+
+The ``leases-reclaim`` Command
+------------------------------
+
+The :isccmd:`leases-reclaim` command instructs the server to reclaim all expired
+leases immediately. The command has the following JSON syntax:
+
+::
+
+ {
+ "command": "leases-reclaim",
+ "arguments": {
+ "remove": true
+ }
+ }
+
+The ``remove`` boolean parameter is mandatory and indicates whether the
+reclaimed leases should be removed from the lease database (if ``true``), or
+left in the ``expired-reclaimed`` state (if ``false``). The latter facilitates
+lease affinity, i.e. the ability to re-assign an expired lease to a
+returning client that previously used that lease. See :ref:`lease-affinity`
+for details. Also, see :ref:`lease-reclamation` for general
+information about the processing of expired leases (lease reclamation).
+
+.. isccmd:: libreload
+.. _command-libreload:
+
+The ``libreload`` Command
+-------------------------
+
+This command is now deprecated and will be removed in future Kea versions.
+
+The :isccmd:`libreload` command first unloads and then loads all currently
+loaded hook libraries. This is primarily intended to allow one or more
+hook libraries to be replaced with newer versions, without requiring Kea
+servers to be reconfigured or restarted. The hook libraries
+are passed the same parameter values (if any) that were passed when they
+originally loaded.
+
+::
+
+ {
+ "command": "libreload",
+ "arguments": { }
+ }
+
+The server responds with a result of either 0, indicating success,
+or 1, indicating failure.
+
+.. isccmd:: list-commands
+.. _command-list-commands:
+
+The ``list-commands`` Command
+-----------------------------
+
+The :isccmd:`list-commands` command retrieves a list of all commands supported
+by the server. It does not take any arguments. An example command may
+look like this:
+
+::
+
+ {
+ "command": "list-commands",
+ "arguments": { }
+ }
+
+The server responds with a list of all supported commands. The arguments
+element is a list of strings, each of which conveys one supported
+command.
+
+.. isccmd:: config-set
+.. _command-config-set:
+
+The ``config-set`` Command
+--------------------------
+
+The :isccmd:`config-set` command instructs the server to replace its current
+configuration with the new configuration supplied in the command's
+arguments. The supplied configuration is expected to be the full
+configuration for the target server, along with an optional logger
+configuration. While optional, the logger configuration is highly
+recommended, as without it the server reverts to its default logging
+configuration. The structure of the command is as follows:
+
+::
+
+ {
+ "command": "config-set",
+ "arguments": {
+ "<server>": {
+ }
+ }
+ }
+
+where <server> is the configuration element name for a given server, such
+as "Dhcp4" or "Dhcp6". For example:
+
+::
+
+ {
+ "command": "config-set",
+ "arguments": {
+ "Dhcp6": {
+ ...
+ }
+ }
+ }
+
+If the new configuration proves to be invalid, the server retains its
+current configuration; however, in some cases a fatal error message is logged
+indicating that the server is no longer providing any service: a working
+configuration must be loaded as soon as possible. If the control channel
+is dead, the configuration file can still be reloaded using the ``SIGHUP``
+signal. If that is unsuccessful, restart the server.
+
+Please note that the new configuration is
+retained in memory only; if the server is restarted or a configuration
+reload is triggered via a signal, the server uses the configuration
+stored in its configuration file. The server's response contains a
+numeric code, ``result`` (0 for success, non-zero on failure), and a
+string, ``text``, describing the outcome:
+
+::
+
+ {"result": 0, "text": "Configuration successful." }
+
+ or
+
+ {"result": 1, "text": "unsupported parameter: BOGUS (<string>:16:26)" }
+
+Starting with 2.4.0, the successful response from a DHCPv4, DHCPv6, or DHCP-DDNS daemons
+also contain a SHA-256 digest of the newly set configuration. The digest can be used to easily
+determine if a configuration has changed or not.
+
+.. isccmd:: shutdown
+.. _command-shutdown:
+
+The ``shutdown`` Command
+------------------------
+
+The :isccmd:`shutdown` command instructs the server to initiate its shutdown
+procedure. It is the equivalent of sending a ``SIGTERM`` signal to the
+process. This command does not take any arguments. An example command
+may look like this:
+
+::
+
+ {
+ "command": "shutdown",
+ "arguments": {
+ "exit-value": 3
+ }
+ }
+
+The server responds with a confirmation that the shutdown procedure has
+been initiated. The optional parameter, ``exit-value``, specifies the
+numeric value with which the server process exits to the system.
+The default value is zero.
+
+The DDNS daemon supports an extra parameter, ``type``, which controls the way
+the process cleans up on exit. The supported shutdown types are:
+
+ - "normal" - stops the queue manager and finishes all current transactions
+ before exiting. This is the default.
+
+ - "drain_first" - stops the queue manager but continues processing requests
+ from the queue until it is empty.
+
+ - "now" - exits immediately.
+
+An example command may look like this:
+
+::
+
+ {
+ "command": "shutdown",
+ "arguments": {
+ "exit-value": 3,
+ "type": "drain_first"
+ }
+ }
+
+.. isccmd:: dhcp-disable
+.. _command-dhcp-disable:
+
+The ``dhcp-disable`` Command
+----------------------------
+
+The :isccmd:`dhcp-disable` command globally disables the DHCP service. The
+server continues to operate, but it drops all received DHCP messages.
+This command is useful when the server's maintenance requires that the
+server temporarily stop allocating new leases and renew existing leases.
+It is also useful in failover-like configurations during a
+synchronization of the lease databases at startup, or recovery after a
+failure. The optional parameter ``max-period`` specifies the time in
+seconds after which the DHCP service should be automatically re-enabled,
+if the :isccmd:`dhcp-enable` command is not sent before this time elapses.
+
+Since Kea 1.9.4, there is an additional ``origin`` parameter that specifies the
+command source. A server administrator should typically omit this parameter
+because the default value "user" indicates that the administrator sent the
+command. This command can also be sent by the partner server running HA hooks
+library. In that case, the partner server sets the parameter to a unique
+integer identifier of an HA service. The integer values are reserved for the
+communication between HA partners and should not be specified in the
+administrator's commands, as it may interfere with HA operation. The
+administrator should either omit this parameter or set it to "user".
+
+::
+
+ {
+ "command": "dhcp-disable",
+ "arguments": {
+ "max-period": 20,
+ "origin": "user"
+ }
+ }
+
+.. isccmd:: dhcp-enable
+.. _command-dhcp-enable:
+
+The ``dhcp-enable`` Command
+---------------------------
+
+The :isccmd:`dhcp-enable` command globally enables the DHCP service.
+
+Since Kea 1.9.4, there is an additional ``origin`` parameter that specifies the
+command source. A server administrator should typically omit this parameter
+because the default value "user" indicates that the administrator sent the
+command. This command can also be sent by the partner server running the HA hook
+library. In that case, the partner server sets the parameter to a unique
+integer identifier of an HA service. The integer values are reserved for the
+communication between HA partners and should not be specified in the
+administrator's commands, as it may interfere with HA operation. The
+administrator should either omit this parameter or set it to
+"user".
+
+::
+
+ {
+ "command": "dhcp-enable",
+ "arguments": {
+ "origin": "user"
+ }
+ }
+
+.. isccmd:: status-get
+.. _command-status-get:
+
+The ``status-get`` Command
+--------------------------
+
+The :isccmd:`status-get` command returns the server's runtime information:
+
+ - ``pid``: the process ID.
+
+ - ``uptime``: the number of seconds since the start of the server.
+
+ - ``reload``: the number of seconds since the last configuration (re)load.
+
+ - ``high-availability``: HA-specific status information about the DHCP servers
+ configured to use the HA hook library:
+
+ * ``local``: the state, the role (primary,
+ secondary, ...), and the scopes (i.e. what the server is actually
+ processing) of the local server.
+
+ * ``remote``: the remote server's last known state, its served
+ HA scopes, and the role of the remote server in the HA relationship.
+
+ - ``multi-threading-enabled``: a flag indicating whether multi-threading is enabled.
+
+ - ``thread-pool-size``: the number of DHCP service threads.
+
+ - ``packet-queue-size``: the maximum size of the packet queue. There is one queue,
+ regardless of the number of running threads.
+
+ - ``packet-queue-statistics``: the average queue size for the last 10, 100, and 1000
+ packets, using an approach similar to the UNIX ``top`` command.
+ The average queue size for the last 10 packets can be considered an
+ instantaneous value, while the average for the last 1000 packets shows
+ a longer-term trend.
+
+The ``high-availability`` information is returned only when the command is
+sent to the DHCP servers in an HA setup. This parameter is
+never returned when the :isccmd:`status-get` command is sent to the
+Control Agent or DDNS daemon.
+
+The ``thread-pool-size``, ``packet-queue-size`` and
+``packet-queue-statistics`` parameters are returned only when the
+command is sent to DHCP servers with multi-threading enabled. These
+three parameters and ``multi-threading-enabled`` are never returned when
+the :isccmd:`status-get` command is sent to the Control Agent or DDNS daemon.
+
+To learn more about the HA status information returned by the
+:isccmd:`status-get` command, please refer to the :ref:`command-ha-status-get`
+section.
+
+
+.. isccmd:: server-tag-get
+.. _command-server-tag-get:
+
+The ``server-tag-get`` Command:
+-------------------------------
+
+The :isccmd:`server-tag-get` command returns the configured server tag of
+the DHCPv4 or DHCPv6 server (:ref:`cb-sharing` explains the server tag concept).
+
+.. isccmd:: config-backend-pull
+.. _command-config-backend-pull:
+
+The ``config-backend-pull`` Command:
+------------------------------------
+
+The :isccmd:`config-backend-pull` command triggers the polling of configuration backends
+(which must be configured for this command to have an effect),
+explained in :ref:`dhcp4-cb-json`.
+
+.. isccmd:: version-get
+.. _command-version-get:
+
+The ``version-get`` Command
+---------------------------
+
+The :isccmd:`version-get` command returns extended information about the Kea
+version. It is the same information available via the ``-V``
+command-line argument. This command does not take any parameters.
+
+::
+
+ {
+ "command": "version-get"
+ }
+
+Commands Supported by the D2 Server
+===================================
+
+The D2 server supports only a subset of the DHCPv4/DHCPv6 server commands:
+
+- :isccmd:`build-report`
+
+- :isccmd:`config-get`
+
+- :isccmd:`config-hash-get`
+
+- :isccmd:`config-reload`
+
+- :isccmd:`config-set`
+
+- :isccmd:`config-test`
+
+- :isccmd:`config-write`
+
+- :isccmd:`list-commands`
+
+- :isccmd:`shutdown`
+
+- :isccmd:`status-get`
+
+- :isccmd:`version-get`
+
+.. _agent-commands:
+
+Commands Supported by the Control Agent
+=======================================
+
+The following commands, listed in :ref:`commands-common`, are also supported by the
+Control Agent; when the ``service`` parameter is blank, the
+commands are handled by the CA and they relate to the CA process itself:
+
+- :isccmd:`build-report`
+
+- :isccmd:`config-get`
+
+- :isccmd:`config-hash-get`
+
+- :isccmd:`config-reload`
+
+- :isccmd:`config-set`
+
+- :isccmd:`config-test`
+
+- :isccmd:`config-write`
+
+- :isccmd:`list-commands`
+
+- :isccmd:`shutdown`
+
+- :isccmd:`status-get`
+
+- :isccmd:`version-get`
diff --git a/doc/sphinx/arm/database-connectivity.rst b/doc/sphinx/arm/database-connectivity.rst
new file mode 100644
index 0000000..8d61d64
--- /dev/null
+++ b/doc/sphinx/arm/database-connectivity.rst
@@ -0,0 +1,104 @@
+.. _database-connectivity:
+
+*********************
+Database Connectivity
+*********************
+
+The Kea servers (:iscman:`kea-dhcp4` and :iscman:`kea-dhcp6`) can be configured to use a variety of
+database backends for leases, hosts, and configuration. They can be
+configured to support automatic recovery when connectivity is lost, via
+the ``on-fail`` and ``retry-on-startup`` parameters.
+(The ``reconnect-wait-time`` and ``max-reconnect-tries`` parameters are
+described in :ref:`database-configuration4` and :ref:`database-configuration6`.)
+
+It is important to understand how and when automatic recovery comes into play.
+Automatic recovery, when configured, only operates after a successful startup
+or reconfiguration during which connectivity to all backends has been
+successfully established.
+
+During server startup, the inability to connect to any of the configured
+backends is considered fatal only if ``retry-on-startup`` is set to ``false``
+(the default). A fatal error is logged and the server exits, based on the idea
+that the configuration should be valid at startup. Exiting to the operating
+system allows nanny scripts to detect the problem.
+If ``retry-on-startup`` is set to ``true``, the server will start reconnection
+attempts even at server startup or on reconfigure events, and will honor the
+action specified in the ``on-fail`` parameter.
+Database connection retries are not attempted on startup if the
+:ischooklib:`libdhcp_limits.so` is loaded because the hook library requires a
+valid connection to the database to check if JSON format is supported and to
+recount class limits.
+
+During dynamic reconfiguration, all backends are disconnected and then
+reconnected using the new configuration. If connectivity to any of the
+backends cannot be established, the server logs a fatal error but remains
+up. It is able to process commands but does not serve clients. This
+allows the configuration to be corrected via the :isccmd:`config-set` or
+``remote-*`` commands, if required.
+
+During normal operations, if connectivity to any of the backends is lost and
+automatic recovery for that backend is enabled, the server disconnects from the
+respective backend and then attempts to reconnect. During the recovery process,
+the server ceases to serve clients according to the ``on-fail`` configured
+option but continues to respond to commands.
+
+The ``on-fail`` parameter configures the actions the server should take when a
+connection is lost. It can have one of the following values:
+
+- ``stop-retry-exit`` - indicates that the server should stop the service
+ while it tries to recover the connection, and exit if recovery is not
+ successful after ``max-reconnect-tries``.
+
+- ``serve-retry-exit`` - indicates that the server should not stop the
+ service while it tries to recover the connection, and exit if recovery is not
+ successful after ``max-reconnect-tries``.
+
+- ``serve-retry-continue`` - indicates that the server should not stop the
+ service while it tries to recover the connection, and not exit if recovery is
+ not successful after ``max-reconnect-tries``.
+
+If connectivity to all backends is restored, the server returns to normal
+operations. If the connection cannot be restored and the server is configured
+to exit, it issues a fatal error before shutdown.
+
+For Kea DHCP servers to work with database backends, the schema has to be
+created and has to have the version specific to the version of the running Kea
+server. If the version check fails on a database backend that is not configured
+as readonly, Kea attempts to initialize the schema.
+
+.. note::
+
+ Schema upgrades are not attempted to not accidentally remove the
+ opportunity for prior administrative actions that users may be interested in,
+ like, for example, backing up the database or temporarily shutting off running
+ Kea servers that are currently operating on the database.
+
+The connection to the database server can optionally be protected by TLS.
+Corresponding database configuration parameters for Kea servers are:
+
+- The ``trust-anchor`` specifies the Certification Authority file name or
+ directory path.
+
+- The ``cert-file`` specifies the client certificate file name.
+
+- The ``key-file`` specifies the private key file name.
+
+- The ``cipher-list`` specifies the list of TLS ciphers (the syntax of
+ the content of this parameter is described in the OpenSSL ciphers
+ manual).
+
+These parameters are similar to the parameters of the secure connections
+with the agent but are interpreted by different backends using database
+configurations too.
+
+Currently the support for each database is:
+
+- MySQL supports the whole set, additional configuration must be done
+ in the MySQL local setup, for instance certificate revocation list,
+ choice of a specific TLS version, mutual authentication, etc.
+ When a TLS connection was required but the actual connection is in
+ clear text an error log is emitted.
+
+- PostgreSQL only uses the configuration to enable the SSL/TLS support
+ in the client library (libpq). Anything else must be done in the
+ PostgreSQL local configuration.
diff --git a/doc/sphinx/arm/ddns.rst b/doc/sphinx/arm/ddns.rst
new file mode 100644
index 0000000..9b3464e
--- /dev/null
+++ b/doc/sphinx/arm/ddns.rst
@@ -0,0 +1,1027 @@
+.. _dhcp-ddns-server:
+
+********************
+The DHCP-DDNS Server
+********************
+
+.. _dhcp-ddns-overview:
+
+Overview
+========
+
+The DHCP-DDNS Server (:iscman:`kea-dhcp-ddns`, known informally as D2) conducts
+the client side of the Dynamic DNS protocol (DDNS, defined in `RFC
+2136 <https://tools.ietf.org/html/rfc2136>`__) on behalf of the DHCPv4
+and DHCPv6 servers (:iscman:`kea-dhcp4` and :iscman:`kea-dhcp6` respectively).
+The DHCP servers construct DDNS update requests, known as NameChangeRequests
+(NCRs), based on DHCP lease change events and then post them to D2. D2
+attempts to match each request to the appropriate DNS server(s) and
+carries out the necessary conversation with those servers to update the
+DNS data.
+
+For the ability to generate host names procedurally, based on an expression, and
+for the ability to skip DDNS updates on a per-client basis, or fine-tuning
+various DNS update aspects, the :iscman:`kea-dhcp4` and :iscman:`kea-dhcp6` can
+load the premium hook library `libdhcp_ddns_tuning.so` which is available from
+ISC. Please refer to :ref:`hooks-ddns-tuning` documentation for the
+configuration options.
+
+.. _dhcp-ddns-dns-server-selection:
+
+DNS Server Selection
+--------------------
+
+To match a request to the appropriate DNS servers, D2 must have
+a catalog of servers from which to select. In fact, D2 has two such
+catalogs, one for forward DNS and one for reverse DNS; these catalogs
+are referred to as "DDNS domain lists." Each list consists of one or more
+named DDNS domains. Further, each DDNS domain has a list of one or more
+DNS servers that publish the DNS data for that domain.
+
+When conducting forward-domain matching, D2 compares the fully qualified
+domain name (FQDN) in the request against the name of each forward DDNS
+domain in its catalog. The domain whose name matches the longest portion
+of the FQDN is considered the best match. For example, if the FQDN is
+"myhost.sample.example.com.", and there are two forward domains in the
+catalog, "sample.example.com." and "example.com.", the former is
+regarded as the best match. In some cases, it may not be possible to
+find a suitable match. Given the same two forward domains there would be
+no match for the FQDN "bogus.net", so the request would be rejected.
+Finally, if there are no forward DDNS domains defined, D2 simply
+disregards the forward-update portion of requests.
+
+When conducting reverse-domain matching, D2 constructs a reverse FQDN
+from the lease address in the request and compares that against the name
+of each reverse DDNS domain. Again, the domain whose name matches the
+longest portion of the FQDN is considered the best match. For instance,
+if the lease address is "172.16.1.40" and there are two reverse domains
+in the catalog, "1.16.172.in-addr.arpa." and "16.172.in-addr.arpa", the
+former is the best match. As with forward matching, D2 may not find a
+suitable match. Given the same two domains, there would be no match for
+the lease address, "192.168.1.50", and the request would be rejected.
+As with forward-domain matching, if there are no reverse DDNS domains defined, D2 simply
+disregards the reverse-update portion of requests.
+
+.. _dhcp-ddns-conflict-resolution:
+
+Conflict Resolution
+-------------------
+
+D2 implements the conflict resolution strategy prescribed by `RFC
+4703 <https://tools.ietf.org/html/rfc4703>`__. Conflict resolution is
+intended to prevent different clients from mapping to the same FQDN at
+the same time. To make this possible, the RFC requires that forward DNS
+entries for a given FQDN must be accompanied by a DHCID resource record
+(RR). This record contains a client identifier that uniquely identifies
+the client to whom the name belongs. Furthermore, any DNS updater that
+wishes to update or remove existing forward entries for an FQDN may only
+do so if their client matches that of the DHCID RR.
+
+In other words, the DHCID RR maps an FQDN to the client to whom it
+belongs, and thereafter changes to that mapping can only be done by
+or at the behest of that client.
+
+Conflict resolution can be indirectly enabled or disabled via
+the configuration parameter ``ddns-use-conflict-resolution``, supported
+by both :iscman:`kea-dhcp4` and :iscman:`kea-dhcp6`. These servers use this parameter to
+set a flag within each NameChangeRequest they send that tells D2
+whether conflict resolution should be employed for that request.
+By default, conflict resolution is enabled. For more details, please refer
+to discussions of ``ddns-use-conflict-resolution`` in :ref:`dhcp4-ddns-config` and :ref:`dhcp6-ddns-config`.
+
+When conflict resolution is disabled, D2 still adds DHCID RRs but does
+not use them to enforce client ownership of DNS entries. Disabling it should
+only be used after careful consideration.
+
+.. _dhcp-ddns-dual-stack:
+
+Dual-Stack Environments
+-----------------------
+
+`RFC 4703, section
+5.2, <https://tools.ietf.org/html/rfc4703#section-5.2>`__ describes
+issues that may arise with dual-stack clients. These are clients that
+wish to have both IPv4 and IPv6 mappings for the same FQDN.
+To work properly, clients must embed their IPv6 DUID
+within their IPv4 client identifier option, as described in `RFC
+4361 <https://tools.ietf.org/html/rfc4361>`__. In this way, DNS updates
+for both IPv4 and IPv6 can be managed under the same DHCID RR. This feature
+is supported by Kea beginning with release 2.1.2.
+
+.. _dhcp-ddns-server-start-stop:
+
+Starting and Stopping the DHCP-DDNS Server
+==========================================
+
+:iscman:`kea-dhcp-ddns` is the Kea DHCP-DDNS server and, due to the nature of
+DDNS, it runs alongside either the DHCPv4 or DHCPv6 component (or both).
+Like other parts of Kea, it is a separate binary that can be run on its
+own or through :iscman:`keactrl` (see :ref:`keactrl`). In normal
+operation, controlling :iscman:`kea-dhcp-ddns` with :iscman:`keactrl` is
+recommended; however, it is also possible to run the DHCP-DDNS server
+directly. It accepts the following command-line switches:
+
+- ``-c file`` - specifies the configuration file. This is the only
+ mandatory switch.
+
+- ``-d`` - specifies whether server logging should be switched to
+ debug/verbose mode. In verbose mode, the logging severity and
+ debuglevel specified in the configuration file are ignored and
+ "debug" severity and the maximum debuglevel (99) are assumed. The
+ flag is convenient for temporarily switching the server into maximum
+ verbosity, e.g. when debugging.
+
+- ``-v`` - displays the Kea version and exits.
+
+- ``-W`` - displays the Kea configuration report and exits. The report
+ is a copy of the ``config.report`` file produced by ``./configure``;
+ it is embedded in the executable binary.
+
+- ``-t file`` - specifies the configuration file to be tested.
+ :iscman:`kea-dhcp-ddns` attempts to load it and conducts sanity checks.
+ Certain checks are possible only while running the actual
+ server. The actual status is reported with an exit code (0 =
+ configuration looks okay, 1 = error encountered). Kea prints out log
+ messages to standard output and errors to standard error when testing
+ the configuration.
+
+ The contents of the ``config.report`` file may also be accessed by examining
+ certain libraries in the installation tree or in the source tree.
+
+ .. code-block:: shell
+
+ # from installation using libkea-process.so
+ $ strings ${prefix}/lib/libkea-process.so | sed -n 's/;;;; //p'
+
+ # from sources using libkea-process.so
+ $ strings src/lib/process/.libs/libkea-process.so | sed -n 's/;;;; //p'
+
+ # from sources using libkea-process.a
+ $ strings src/lib/process/.libs/libkea-process.a | sed -n 's/;;;; //p'
+
+ # from sources using libcfgrpt.a
+ $ strings src/lib/process/cfgrpt/.libs/libcfgrpt.a | sed -n 's/;;;; //p'
+
+Upon startup, the module loads its configuration and begins listening
+for NCRs based on that configuration.
+
+During startup, the server attempts to create a PID file of the form:
+``[runstatedir]/[conf name].kea-dhcp-ddns.pid`` where:
+
+- ``runstatedir`` - is the value as passed into the build configure
+ script; it defaults to "/usr/local/var/run". Note that this value may be
+ overridden at runtime by setting the environment variable
+ ``KEA_PIDFILE_DIR``. This is intended primarily for testing purposes.
+
+- ``conf name`` - is the configuration file name used to start the server,
+ minus all preceding paths and the file extension. For example, given
+ a pathname of "/usr/local/etc/kea/myconf.txt", the portion used would
+ be "myconf".
+
+If the file already exists and contains the PID of a live process, the
+server issues a ``DHCP_DDNS_ALREADY_RUNNING`` log message and exits. It
+is possible, though unlikely, that the file is a remnant of a system
+crash and the process to which the PID belongs is unrelated to Kea. In
+such a case it is necessary to manually delete the PID file.
+
+.. _d2-configuration:
+
+Configuring the DHCP-DDNS Server
+================================
+
+Before starting the :iscman:`kea-dhcp-ddns` module for the first time, a
+configuration file must be created. The following default configuration
+is a template that can be customized to individual requirements.
+
+::
+
+ "DhcpDdns": {
+ "ip-address": "127.0.0.1",
+ "port": 53001,
+ "dns-server-timeout": 500,
+ "ncr-protocol": "UDP",
+ "ncr-format": "JSON",
+ "tsig-keys": [ ],
+ "forward-ddns": {
+ "ddns-domains": [ ]
+ },
+ "reverse-ddns": {
+ "ddns-domains": [ ]
+ }
+ }
+
+The configuration can be divided into the following sections, each of
+which is described below:
+
+- *Global Server Parameters* - define values which control connectivity and
+ global server behavior.
+
+- *Control Socket* - defines the Control Socket type and name.
+
+- *TSIG Key Info* - defines the TSIG keys used for secure traffic with
+ DNS servers.
+
+- *Forward DDNS* - defines the catalog of forward DDNS domains.
+
+- *Reverse DDNS* - defines the catalog of reverse DDNS domains.
+
+.. _d2-server-parameter-config:
+
+Global Server Parameters
+------------------------
+
+- ``ip-address`` - the IP address on which D2 listens for requests. The
+ default is the local loopback interface at address 127.0.0.1.
+ Either an IPv4 or IPv6 address may be specified.
+
+- ``port`` - the port on which D2 listens for requests. The default value
+ is 53001.
+
+- ``dns-server-timeout`` - the maximum amount of time, in milliseconds,
+ that D2 will wait for a response from a DNS server to a single DNS
+ update message. The default is 500 ms.
+
+- ``ncr-protocol`` - the socket protocol to use when sending requests to
+ D2. Currently only UDP is supported.
+
+- ``ncr-format`` - the packet format to use when sending requests to D2.
+ Currently only JSON format is supported.
+
+D2 must listen for change requests on a known address and port. By
+default it listens at 127.0.0.1 on port 53001. The following example
+illustrates how to change D2's global parameters so it will listen at
+192.168.1.10 port 900:
+
+::
+
+ "DhcpDdns": {
+ "ip-address": "192.168.1.10",
+ "port": 900,
+ ...
+ }
+
+.. warning::
+
+ It is possible for a malicious attacker to send bogus
+ NameChangeRequests to the DHCP-DDNS server. Addresses other than the
+ IPv4 or IPv6 loopback addresses (127.0.0.1 or ::1) should only be
+ used for testing purposes; note that local users may still
+ communicate with the DHCP-DDNS server.
+
+.. note::
+
+ If the ``ip-address`` and ``port`` are changed, the corresponding values in
+ the DHCP servers' ``dhcp-ddns`` configuration section must be changed.
+
+.. _d2-ctrl-channel:
+
+Management API for the D2 Server
+--------------------------------
+
+The management API allows the issuing of specific management commands,
+such as configuration retrieval or shutdown. For more details, see
+:ref:`ctrl-channel`. Currently, the only supported communication
+channel type is the UNIX stream socket. By default there are no sockets
+open; to instruct Kea to open a socket, the following entry in the
+configuration file can be used:
+
+::
+
+ "DhcpDdns": {
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/path/to/the/unix/socket"
+ },
+ ...
+ }
+
+The length of the path specified by the ``socket-name`` parameter is
+restricted by the maximum length for the UNIX socket name on the
+operating system, i.e. the size of the ``sun_path`` field in the
+``sockaddr_un`` structure, decreased by 1. This value varies on
+different operating systems, between 91 and 107 characters. Typical
+values are 107 on Linux and 103 on FreeBSD.
+
+Communication over the control channel is conducted using JSON structures.
+See the `Control Channel section in the Kea Developer's
+Guide <https://reports.kea.isc.org/dev_guide/d2/d96/ctrlSocket.html>`__
+for more details.
+
+The D2 server supports the following operational commands:
+
+- :isccmd:`build-report`
+- :isccmd:`config-get`
+- :isccmd:`config-hash-get`
+- :isccmd:`config-reload`
+- :isccmd:`config-set`
+- :isccmd:`config-test`
+- :isccmd:`config-write`
+- :isccmd:`list-commands`
+- :isccmd:`shutdown`
+- :isccmd:`status-get`
+- :isccmd:`version-get`
+
+Since Kea version 2.0.0, the D2 server also supports the following
+operational commands for statistics:
+
+- :isccmd:`statistic-get`
+- :isccmd:`statistic-get`-all
+- :isccmd:`statistic-reset`
+- :isccmd:`statistic-reset`-all
+
+The :isccmd:`shutdown` command supports the extra ``type`` argument, which controls the
+way the D2 server cleans up on exit.
+The supported shutdown types are:
+
+- ``normal`` - stops the queue manager and finishes all current transactions
+ before exiting. This is the default.
+
+- ``drain_first`` - stops the queue manager but continues processing requests
+ from the queue until it is empty.
+
+- ``now`` - exits immediately.
+
+An example command may look like this:
+
+::
+
+ {
+ "command": "shutdown",
+ "arguments": {
+ "exit-value": 3,
+ "type": "drain_first"
+ }
+ }
+
+.. _d2-tsig-key-list-config:
+
+TSIG Key List
+-------------
+
+A DDNS protocol exchange can be conducted with or without a transaction
+signature, or TSIG (defined
+in `RFC 2845 <https://tools.ietf.org/html/rfc2845>`__). This
+configuration section allows the administrator to define the set of TSIG
+keys that may be used in such exchanges.
+
+To use TSIG when updating entries in a DNS domain, a key must be defined
+in the TSIG key list and referenced by name in that domain's
+configuration entry. When D2 matches a change request to a domain, it
+checks whether the domain has a TSIG key associated with it. If so, D2
+uses that key to sign DNS update messages sent to and verify
+responses received from the domain's DNS server(s). For each TSIG key
+required by the DNS servers that D2 is working with, there must be
+a corresponding TSIG key in the TSIG key list.
+
+As one might gather from the name, the ``tsig-key`` section of the D2
+configuration lists the TSIG keys. Each entry describes a TSIG key used
+by one or more DNS servers to authenticate requests and sign responses.
+Every entry in the list has three parameters:
+
+- ``name`` - is a unique text label used to identify this key within the
+ list. This value is used to specify which key (if any) should be used
+ when updating a specific domain. As long as the name is unique its
+ content is arbitrary, although for clarity and ease of maintenance it
+ is recommended that it match the name used on the DNS server(s). This
+ field cannot be blank.
+
+- ``algorithm`` - specifies which hashing algorithm should be used with
+ this key. This value must specify the same algorithm used for the key
+ on the DNS server(s). The supported algorithms are listed below:
+
+ - HMAC-MD5
+ - HMAC-SHA1
+ - HMAC-SHA224
+ - HMAC-SHA256
+ - HMAC-SHA384
+ - HMAC-SHA512
+
+ This value is not case-sensitive.
+
+- ``digest-bits`` - is used to specify the minimum truncated length in
+ bits. The default value 0 means truncation is forbidden; non-zero
+ values must be an integral number of octets, and be greater than both
+ 80 and half of the full length. (Note that in BIND 9 this parameter
+ is appended to the algorithm name, after a dash.)
+
+- ``secret`` - is used to specify the shared secret key code for this
+ key. This value is case-sensitive and must exactly match the value
+ specified on the DNS server(s). It is a base64-encoded text value.
+
+As an example, suppose that a domain D2 will be updating is maintained
+by a BIND 9 DNS server, which requires dynamic updates to be secured
+with TSIG. Suppose further that the entry for the TSIG key in BIND 9's
+named.conf file looks like this:
+
+::
+
+ :
+ key "key.four.example.com." {
+ algorithm hmac-sha224;
+ secret "bZEG7Ow8OgAUPfLWV3aAUQ==";
+ };
+ :
+
+By default, the TSIG key list is empty:
+
+::
+
+ "DhcpDdns": {
+ "tsig-keys": [ ],
+ ...
+ }
+
+A new key must be added to the list:
+
+::
+
+ "DhcpDdns": {
+ "tsig-keys": [
+ {
+ "name": "key.four.example.com.",
+ "algorithm": "HMAC-SHA224",
+ "secret": "bZEG7Ow8OgAUPfLWV3aAUQ=="
+ }
+ ],
+ ...
+ }
+
+These steps must be repeated for each TSIG key needed, although the
+same TSIG key can be used with more than one domain.
+
+.. _d2-forward-ddns-config:
+
+Forward DDNS
+------------
+
+The forward DDNS section is used to configure D2's forward-update
+behavior. Currently it contains a single parameter, the catalog of
+forward DDNS domains, which is a list of structures.
+
+::
+
+ "DhcpDdns": {
+ "forward-ddns": {
+ "ddns-domains": [ ]
+ },
+ ...
+ }
+
+By default, this list is empty, which causes the server to ignore
+the forward-update portions of requests.
+
+.. _add-forward-ddns-domain:
+
+Adding Forward DDNS Domains
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A forward DDNS domain maps a forward DNS zone to a set of DNS servers
+which maintain the forward DNS data (i.e. name-to-address mapping) for
+that zone. Each zone served needs one forward DDNS domain.
+Some or all of the zones may be maintained by the same
+servers, but one DDNS domain is still needed for each zone. Remember that
+matching a request to the appropriate server(s) is done by zone and a
+DDNS domain only defines a single zone.
+
+This section describes how to add forward DDNS domains; repeat these
+steps for each forward DDNS domain desired. Each forward DDNS domain has
+the following parameters:
+
+- ``name`` - this is the fully qualified domain name (or zone) that this DDNS
+ domain can update. This value is compared against the request FQDN
+ during forward matching. It must be unique within the catalog.
+
+- ``key-name`` - if TSIG is used with this domain's servers, this value
+ should be the name of the key from the TSIG key list. If the
+ value is blank (the default), TSIG will not be used in DDNS
+ conversations with this domain's servers.
+
+- ``dns-servers`` - this is a list of one or more DNS servers which can conduct
+ the server side of the DDNS protocol for this domain. The servers are
+ used in a first-to-last preference; in other words, when D2 begins to
+ process a request for this domain, it will pick the first server in
+ this list and attempt to communicate with it. If that attempt fails,
+ D2 will move to the next one in the list and so on, until either it
+ is successful or the list is exhausted.
+
+To create a new forward DDNS domain, add a new domain element and set
+its parameters:
+
+::
+
+ "DhcpDdns": {
+ "forward-ddns": {
+ "ddns-domains": [
+ {
+ "name": "other.example.com.",
+ "key-name": "",
+ "dns-servers": [
+ ]
+ }
+ ]
+ }
+ }
+
+It is possible to add a domain without any servers; however, if that
+domain matches a request, the request will fail. To make the domain
+useful, at least one DNS server must be added to it.
+
+.. _add-forward-dns-servers:
+
+Adding Forward DNS Servers
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This section describes how to add DNS servers to a forward DDNS domain.
+Repeat these instructions as needed for all the servers in each domain.
+
+Forward DNS server entries represent actual DNS servers which support
+the server side of the DDNS protocol. Each forward DNS server has the
+following parameters:
+
+- ``hostname`` - the resolvable host name of the DNS server; this
+ parameter is not yet implemented.
+
+- ``ip-address`` - the IP address at which the server listens for DDNS
+ requests. This may be either an IPv4 or an IPv6 address.
+
+- ``port`` - the port on which the server listens for DDNS requests. It
+ defaults to the standard DNS service port of 53.
+
+To create a new forward DNS server, a new server element must be added to
+the domain and its parameters filled in. If, for example, the service is
+running at "172.88.99.10", set the forward DNS server as follows:
+
+::
+
+ "DhcpDdns": {
+ "forward-ddns": {
+ "ddns-domains": [
+ {
+ "name": "other.example.com.",
+ "key-name": "",
+ "dns-servers": [
+ {
+ "ip-address": "172.88.99.10",
+ "port": 53
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+.. note::
+
+ Since ``hostname`` is not yet supported, the parameter ``ip-address``
+ must be set to the address of the DNS server.
+
+.. _d2-reverse-ddns-config:
+
+Reverse DDNS
+------------
+
+The reverse DDNS section is used to configure D2's reverse update
+behavior, and the concepts are the same as for the forward DDNS section.
+Currently it contains a single parameter, the catalog of reverse DDNS
+domains, which is a list of structures.
+
+::
+
+ "DhcpDdns": {
+ "reverse-ddns": {
+ "ddns-domains": [ ]
+ },
+ ...
+ }
+
+By default, this list is empty, which causes the server to ignore
+the reverse-update portions of requests.
+
+.. _add-reverse-ddns-domain:
+
+Adding Reverse DDNS Domains
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A reverse DDNS domain maps a reverse DNS zone to a set of DNS servers
+which maintain the reverse DNS data (address-to-name mapping) for that
+zone. Each zone served needs one reverse DDNS domain.
+Some or all of the zones may be maintained by the same servers, but
+one DDNS domain entry is needed for each zone. Remember that
+matching a request to the appropriate server(s) is done by zone and a
+DDNS domain only defines a single zone.
+
+This section describes how to add reverse DDNS domains; repeat these
+steps for each reverse DDNS domain desired. Each reverse DDNS domain has
+the following parameters:
+
+- ``name`` - this is the fully qualified reverse zone that this DDNS domain can
+ update. This is the value used during reverse matching, which
+ compares it with a reversed version of the request's lease address.
+ The zone name should follow the appropriate standards; for example,
+ to support the IPv4 subnet 172.16.1, the name should be
+ "1.16.172.in-addr.arpa.". Similarly, to support an IPv6 subnet of
+ 2001:db8:1, the name should be "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa."
+ The name must be unique within the catalog.
+
+- ``key-name`` - if TSIG is used with this domain's servers,
+ this value should be the name of the key from the TSIG key list. If
+ the value is blank (the default), TSIG will not be used in DDNS
+ conversations with this domain's servers.
+
+- ``dns-servers`` - this is a list of one or more DNS servers which can conduct
+ the server side of the DDNS protocol for this domain. Currently, the
+ servers are used in a first-to-last preference; in other words, when
+ D2 begins to process a request for this domain, it will pick the
+ first server in this list and attempt to communicate with it. If that
+ attempt fails, D2 will move to the next one in the list and so on,
+ until either it is successful or the list is exhausted.
+
+To create a new reverse DDNS domain, a new domain element must be added
+and its parameters set. For example, to support subnet 2001:db8:1::, the
+following configuration could be used:
+
+::
+
+ "DhcpDdns": {
+ "reverse-ddns": {
+ "ddns-domains": [
+ {
+ "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.",
+ "key-name": "",
+ "dns-servers": [
+ ]
+ }
+ ]
+ }
+ }
+
+It is possible to add a domain without any servers; however, if that
+domain matches a request, the request will fail. To make the domain
+useful, at least one DNS server must be added to it.
+
+.. _add-reverse-dns-servers:
+
+Adding Reverse DNS Servers
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This section describes how to add DNS servers to a reverse DDNS domain.
+Repeat these instructions as needed for all the servers in each domain.
+
+Reverse DNS server entries represent actual DNS servers which support
+the server side of the DDNS protocol. Each reverse DNS server has the
+following parameters:
+
+- ``hostname`` - the resolvable host name of the DNS server; this value
+ is currently ignored.
+
+- ``ip-address`` - the IP address at which the server listens for DDNS
+ requests.
+
+- ``port`` - the port on which the server listens for DDNS requests. It
+ defaults to the standard DNS service port of 53.
+
+To create a new reverse DNS server, a new server
+element must be added to the domain and its parameters specified. If, for example, the
+service is running at "172.88.99.10", then set it as follows:
+
+::
+
+ "DhcpDdns": {
+ "reverse-ddns": {
+ "ddns-domains": [
+ {
+ "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.",
+ "key-name": "",
+ "dns-servers": [
+ {
+ "ip-address": "172.88.99.10",
+ "port": 53
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+.. note::
+
+ Since ``hostname`` is not yet supported, the parameter ``ip-address``
+ must be set to the address of the DNS server.
+
+.. _per-server-keys:
+
+Per-DNS-Server TSIG Keys
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Since Kea version 2.0.0, a TSIG key can be specified in a DNS server
+configuration. The priority rule is:
+
+- if a not-empty key name is specified in a DNS server entry, this TSIG
+ key protects DNS updates sent to this server.
+
+- if the DNS server entry is empty, but a
+ not-empty key name is specified in the parent's domain entry, the parent domain's
+ TSIG key protects DNS updates sent to this server.
+
+- if the DNS server entry is empty, and no key name is specified in its parent
+ domain entry, no TSIG protects DNS updates sent to this server.
+
+For instance, in this configuration:
+
+::
+
+ "DhcpDdns": {
+ "forward-ddns": {
+ "ddns-domains": [
+ {
+ "name": "other.example.com.",
+ "key-name": "foo",
+ "dns-servers": [
+ {
+ "ip-address": "172.88.99.10",
+ "port": 53
+ },
+ {
+ "ip-address": "172.88.99.11",
+ "port": 53,
+ "key-name": "bar"
+ }
+ ]
+ }
+ ]
+ },
+ "reverse-ddns": {
+ "ddns-domains": [
+ {
+ "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.",
+ "dns-servers": [
+ {
+ "ip-address": "172.88.99.12",
+ "port": 53
+ },
+ {
+ "ip-address": "172.88.99.13",
+ "port": 53,
+ "key-name": "bar"
+ }
+ ]
+ }
+ ]
+ },
+ "tsig-keys": [
+ {
+ "name": "foo",
+ "algorithm": "HMAC-MD5",
+ "secret": "LSWXnfkKZjdPJI5QxlpnfQ=="
+ },
+ {
+ "name": "bar",
+ "algorithm": "HMAC-SHA224",
+ "secret": "bZEG7Ow8OgAUPfLWV3aAUQ=="
+ }
+ ]
+ }
+
+
+The 172.88.99.10 server will use the "foo" TSIG key, the 172.88.99.11 and
+172.88.99.13 servers will use the "bar" key. and 172.88.99.12 will not use TSIG.
+
+.. _d2-user-contexts:
+
+User Contexts in DDNS
+---------------------
+
+See :ref:`user-context` for additional background regarding the user
+context idea.
+
+User contexts can be specified on a global scope, a DDNS domain, a DNS server,
+a TSIG key, and loggers. One other useful usage is the ability to store
+comments or descriptions; the parser translates a "comment" entry into a
+user context with the entry, which allows a comment to be attached
+inside the configuration itself.
+
+.. _d2-example-config:
+
+Example DHCP-DDNS Server Configuration
+--------------------------------------
+
+This section provides a sample DHCP-DDNS server configuration, based on
+a small example network. Let's suppose our example network has three
+domains, each with their own subnet.
+
+.. table:: Our example network
+
+ +------------------+-----------------+-----------------+-----------------+
+ | Domain | Subnet | Forward DNS | Reverse DNS |
+ | | | Servers | Servers |
+ +==================+=================+=================+=================+
+ | four.example.com | 192.0.2.0/24 | 172.16.1.5, | 172.16.1.5, |
+ | | | 172.16.2.5 | 172.16.2.5 |
+ +------------------+-----------------+-----------------+-----------------+
+ | six.example.com | 2001:db8:1::/64 | 3001:1::50 | 3001:1::51 |
+ +------------------+-----------------+-----------------+-----------------+
+ | example.com | 192.0.0.0/16 | 172.16.2.5 | 172.16.2.5 |
+ +------------------+-----------------+-----------------+-----------------+
+
+We need to construct three forward DDNS domains:
+
+.. table:: Forward DDNS domains needed
+
+ +----+-------------------+------------------------+
+ | # | DDNS Domain Name | DNS Servers |
+ +====+===================+========================+
+ | 1. | four.example.com. | 172.16.1.5, 172.16.2.5 |
+ +----+-------------------+------------------------+
+ | 2. | six.example.com. | 3001:1::50 |
+ +----+-------------------+------------------------+
+ | 3. | example.com. | 172.16.2.5 |
+ +----+-------------------+------------------------+
+
+As discussed earlier, FQDN-to-domain matching is based on the longest
+match. The FQDN "myhost.four.example.com." matches the first domain
+("four.example.com"), while "admin.example.com." matches the third
+domain ("example.com"). The FQDN "other.example.net." fails to
+match any domain and is rejected.
+
+The following example configuration specifies the forward DDNS domains.
+
+::
+
+ "DhcpDdns": {
+ "comment": "example configuration: forward part",
+ "forward-ddns": {
+ "ddns-domains": [
+ {
+ "name": "four.example.com.",
+ "key-name": "",
+ "dns-servers": [
+ { "ip-address": "172.16.1.5" },
+ { "ip-address": "172.16.2.5" }
+ ]
+ },
+ {
+ "name": "six.example.com.",
+ "key-name": "",
+ "dns-servers": [
+ { "ip-address": "2001:db8::1" }
+ ]
+ },
+ {
+ "name": "example.com.",
+ "key-name": "",
+ "dns-servers": [
+ { "ip-address": "172.16.2.5" }
+ ],
+ "user-context": { "backup": false }
+ },
+ ...
+ ]
+ }
+ }
+
+Similarly, we need to construct the three reverse DDNS domains:
+
+.. table:: Reverse DDNS domains needed
+
+ +----+-----------------------------------+------------------------+
+ | # | DDNS Domain Name | DNS Servers |
+ +====+===================================+========================+
+ | 1. | 2.0.192.in-addr.arpa. | 172.16.1.5, 172.16.2.5 |
+ +----+-----------------------------------+------------------------+
+ | 2. | 1.0.0.0.8.d.b.0.1.0.0.2.ip6.arpa. | 3001:1::50 |
+ +----+-----------------------------------+------------------------+
+ | 3. | 0.182.in-addr.arpa. | 172.16.2.5 |
+ +----+-----------------------------------+------------------------+
+
+An address of "192.0.2.150" matches the first domain,
+"2001:db8:1::10" matches the second domain, and "192.0.50.77" matches the
+third domain.
+
+These reverse DDNS domains are specified as follows:
+
+::
+
+ "DhcpDdns": {
+ "comment": "example configuration: reverse part",
+ "reverse-ddns": {
+ "ddns-domains": [
+ {
+ "name": "2.0.192.in-addr.arpa.",
+ "key-name": "",
+ "dns-servers": [
+ { "ip-address": "172.16.1.5" },
+ { "ip-address": "172.16.2.5" }
+ ]
+ },
+ {
+ "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.",
+ "key-name": "",
+ "dns-servers": [
+ { "ip-address": "2001:db8::1" }
+ ]
+ },
+ {
+ "name": "0.192.in-addr.arpa.",
+ "key-name": "",
+ "dns-servers": [
+ { "ip-address": "172.16.2.5" }
+ ]
+ },
+ ...
+ ]
+ }
+ }
+
+DHCP-DDNS Server Statistics
+===========================
+
+Kea version 2.0.0 introduced statistics support for DHCP-DDNS.
+
+Statistics are divided into three groups: NameChangeRequests, DNS updates,
+and per-TSIG-key DNS updates. While the statistics of the first two groups
+are cumulative, i.e. not affected by configuration change or reload,
+per-key statistics are reset to 0 when the underlying object is
+(re)created.
+
+Currently Kea's statistics management has the following limitations:
+
+- only integer samples (i.e. a counter and a timestamp) are used;
+- the maximum sample count is 1;
+- there is no API to remove one or all statistics;
+- there is no API to set the maximum sample count or age.
+
+.. note::
+
+ Hook libraries, such as the ISC subscriber-only GSS-TSIG library,
+ make new statistics available in Kea.
+
+More information about Kea statistics can be found at :ref:`stats`.
+
+NCR Statistics
+--------------
+
+The NameChangeRequest statistics are:
+
+- ``ncr-received`` - the number of received valid NCRs
+- ``ncr-invalid`` - the number of received invalid NCRs
+- ``ncr-error`` - the number of errors in NCR receptions other than an I/O cancel on shutdown
+
+DNS Update Statistics
+---------------------
+
+The global DNS update statistics are:
+
+- ``update-sent`` - the number of DNS updates sent
+- ``update-signed`` - the number of DNS updates sent and protected by TSIG
+- ``update-unsigned`` - the number of DNS updates sent and not protected by TSIG
+- ``update-success`` - the number of DNS updates which successfully completed
+- ``update-timeout`` - the number of DNS updates which completed on timeout
+- ``update-error`` - the number of DNS updates which completed with an error other than
+ timeout
+
+Per-TSIG-Key DNS Update Statistics
+----------------------------------
+
+The per TSIG key DNS update statistics are:
+
+- ``update-sent`` - the number of DNS updates sent
+- ``update-success`` - the number of DNS updates which successfully completed
+- ``update-timeout`` - the number of DNS updates which completed on timeout
+- ``update-error`` - the number of DNS updates which completed with an error other than
+ timeout
+
+The name format for per-key statistics is ``key[<key-DNS-name>].<stat-name>``:
+for instance, the name of the ``update-sent`` statistics for the
+``key.example.com.`` TSIG key is ``key[key.example.com.].update-sent``.
+
+DHCP-DDNS Server Limitations
+============================
+
+The following are the current limitations of the DHCP-DDNS server.
+
+- Requests received from the DHCP servers are placed in a queue until
+ they are processed. Currently, all queued requests are lost if the
+ server shuts down.
+
+Supported Standards
+===================
+
+The following RFCs are supported by the DHCP-DDNS server:
+
+- *Secret Key Transaction Authentication for DNS (TSIG)*, `RFC 2845
+ <https://tools.ietf.org/html/rfc2845>`__: All DNS update packets sent and
+ received by the DHCP-DDNS server can be protected by TSIG signatures.
+
+- *Dynamic Updates in the Domain Name System (DNS UPDATE)*, `RFC 2136
+ <https://tools.ietf.org/html/rfc2136>`__: The complete DNS update mechanism is
+ supported.
+
+- *Resolution of Fully Qualified Domain Name (FQDN) Conflicts among Dynamic Host
+ Configuration Protocol (DHCP) Clients*, `RFC 4703
+ <https://tools.ietf.org/html/rfc4703>`__: DHCP-DDNS takes care of
+ conflict resolution, for both DHCPv4 and DHCPv6 servers.
+
+- *A DNS Resource Record (RR) for Encoding Dynamic Host Configuration Protocol
+ (DHCP) Information (DHCID RR)*, `RFC 4701
+ <https://tools.ietf.org/html/rfc4701>`__: The DHCP-DDNS server uses DHCID
+ records.
diff --git a/doc/sphinx/arm/dhcp4-srv.rst b/doc/sphinx/arm/dhcp4-srv.rst
new file mode 100644
index 0000000..eea2648
--- /dev/null
+++ b/doc/sphinx/arm/dhcp4-srv.rst
@@ -0,0 +1,8455 @@
+.. _dhcp4:
+
+*****************
+The DHCPv4 Server
+*****************
+
+.. _dhcp4-start-stop:
+
+Starting and Stopping the DHCPv4 Server
+=======================================
+
+It is recommended that the Kea DHCPv4 server be started and stopped
+using :iscman:`keactrl` (described in :ref:`keactrl`); however, it is also
+possible to run the server directly via the :iscman:`kea-dhcp4` command, which accepts
+the following command-line switches:
+
+- ``-c file`` - specifies the configuration file. This is the only
+ mandatory switch.
+
+- ``-d`` - specifies whether the server logging should be switched to
+ debug/verbose mode. In verbose mode, the logging severity and debuglevel
+ specified in the configuration file are ignored; "debug" severity
+ and the maximum debuglevel (99) are assumed. The flag is convenient
+ for temporarily switching the server into maximum verbosity, e.g.
+ when debugging.
+
+- ``-p server-port`` - specifies the local UDP port on which the server
+ listens. This is only useful during testing, as a DHCPv4 server
+ listening on ports other than the standard ones is not able to
+ handle regular DHCPv4 queries.
+
+- ``-P client-port`` - specifies the remote UDP port to which the
+ server sends all responses. This is only useful during testing,
+ as a DHCPv4 server sending responses to ports other than the standard
+ ones is not able to handle regular DHCPv4 queries.
+
+- ``-t file`` - specifies a configuration file to be tested. :iscman:`kea-dhcp4`
+ loads it, checks it, and exits. During the test, log messages are
+ printed to standard output and error messages to standard error. The
+ result of the test is reported through the exit code (0 =
+ configuration looks OK, 1 = error encountered). The check is not
+ comprehensive; certain checks are possible only when running the
+ server.
+
+- ``-T file`` - specifies a configuration file to be tested. :iscman:`kea-dhcp4`
+ loads it, checks it, and exits. It performs extra checks beyond what ``-t``
+ offers, such as establishing database connections (for the lease backend,
+ host reservations backend, configuration backend, and forensic logging
+ backend), loading hook libraries, parsing hook-library configurations, etc.
+ It does not open UNIX or TCP/UDP sockets, nor does it open or rotate
+ files, as any of these actions could interfere with a running process on the
+ same machine.
+
+- ``-v`` - displays the Kea version and exits.
+
+- ``-V`` - displays the Kea extended version with additional parameters
+ and exits. The listing includes the versions of the libraries
+ dynamically linked to Kea.
+
+- ``-W`` - displays the Kea configuration report and exits. The report
+ is a copy of the ``config.report`` file produced by ``./configure``;
+ it is embedded in the executable binary.
+
+ The contents of the ``config.report`` file may also be accessed by examining
+ certain libraries in the installation tree or in the source tree.
+
+ .. code-block:: shell
+
+ # from installation using libkea-process.so
+ $ strings ${prefix}/lib/libkea-process.so | sed -n 's/;;;; //p'
+
+ # from sources using libkea-process.so
+ $ strings src/lib/process/.libs/libkea-process.so | sed -n 's/;;;; //p'
+
+ # from sources using libkea-process.a
+ $ strings src/lib/process/.libs/libkea-process.a | sed -n 's/;;;; //p'
+
+ # from sources using libcfgrpt.a
+ $ strings src/lib/process/cfgrpt/.libs/libcfgrpt.a | sed -n 's/;;;; //p'
+
+On startup, the server detects available network interfaces and
+attempts to open UDP sockets on all interfaces listed in the
+configuration file. Since the DHCPv4 server opens privileged ports, it
+requires root access; this daemon must be run as root.
+
+During startup, the server attempts to create a PID file of the
+form: ``[runstatedir]/kea/[conf name].kea-dhcp4.pid``, where:
+
+- ``runstatedir``: The value as passed into the build configure
+ script; it defaults to ``/usr/local/var/run``. Note that this value may be
+ overridden at runtime by setting the environment variable
+ ``KEA_PIDFILE_DIR``, although this is intended primarily for testing
+ purposes.
+
+- ``conf name``: The configuration file name used to start the server,
+ minus all preceding paths and the file extension. For example, given
+ a pathname of ``/usr/local/etc/kea/myconf.txt``, the portion used would
+ be ``myconf``.
+
+If the file already exists and contains the PID of a live process, the
+server issues a ``DHCP4_ALREADY_RUNNING`` log message and exits. It is
+possible, though unlikely, that the file is a remnant of a system crash
+and the process to which the PID belongs is unrelated to Kea. In such a
+case, it would be necessary to manually delete the PID file.
+
+The server can be stopped using the ``kill`` command. When running in a
+console, the server can also be shut down by pressing Ctrl-c. Kea detects
+the key combination and shuts down gracefully.
+
+The reconfiguration of each Kea server is triggered by the SIGHUP signal.
+When a server receives the SIGHUP signal it rereads its configuration file and,
+if the new configuration is valid, uses the new configuration.
+If the new configuration proves to be invalid, the server retains its
+current configuration; however, in some cases a fatal error message is logged
+indicating that the server is no longer providing any service: a working
+configuration must be loaded as soon as possible.
+
+.. _dhcp4-configuration:
+
+DHCPv4 Server Configuration
+===========================
+
+Introduction
+------------
+
+This section explains how to configure the Kea DHCPv4 server using a
+configuration file.
+
+Before DHCPv4 is started, its configuration file must
+be created. The basic configuration is as follows:
+
+::
+
+ {
+ # DHCPv4 configuration starts on the next line
+ "Dhcp4": {
+
+ # First we set up global values
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+ # Next we set up the interfaces to be used by the server.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+ # And we specify the type of lease database
+ "lease-database": {
+ "type": "memfile",
+ "persist": true,
+ "name": "/var/lib/kea/dhcp4.leases"
+ },
+
+ # Finally, we list the subnets from which we will be leasing addresses.
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "pools": [
+ {
+ "pool": "192.0.2.1 - 192.0.2.200"
+ }
+ ]
+ }
+ ]
+ # DHCPv4 configuration ends with the next line
+ }
+
+ }
+
+The following paragraphs provide a brief overview of the parameters in
+the above example, along with their format. Subsequent sections of this
+chapter go into much greater detail for these and other parameters.
+
+The lines starting with a hash (#) are comments and are ignored by the
+server; they do not impact its operation in any way.
+
+The configuration starts in the first line with the initial opening
+curly bracket (or brace). Each configuration must contain an object
+specifying the configuration of the Kea module using it. In the example
+above, this object is called ``Dhcp4``.
+
+The ``Dhcp4`` configuration starts with the ``"Dhcp4": {`` line and ends
+with the corresponding closing brace (in the above example, the brace
+after the last comment). Everything defined between those lines is
+considered to be the ``Dhcp4`` configuration.
+
+In general, the order in which those parameters appear does not
+matter, but there are two caveats. The first one is that the
+configuration file must be well-formed JSON, meaning that the
+parameters for any given scope must be separated by a comma, and there
+must not be a comma after the last parameter. When reordering a
+configuration file, moving a parameter to or from the
+last position in a given scope may also require moving the comma. The
+second caveat is that it is uncommon — although legal JSON — to repeat
+the same parameter multiple times. If that happens, the last occurrence
+of a given parameter in a given scope is used, while all previous
+instances are ignored. This is unlikely to cause any confusion as there
+are no real-life reasons to keep multiple copies of the same parameter
+in the configuration file.
+
+The first few DHCPv4 configuration elements
+define some global parameters. ``valid-lifetime`` defines how long the
+addresses (leases) given out by the server are valid; the default
+is for a client to be allowed to use a given address for 4000
+seconds. (Note that integer numbers are specified as is, without any
+quotes around them.) ``renew-timer`` and ``rebind-timer`` are values
+(also in seconds) that define the T1 and T2 timers that govern when the
+client begins the renewal and rebind processes.
+
+.. note::
+
+ The lease valid lifetime is expressed as a triplet with minimum, default, and
+ maximum values using configuration entries
+ ``min-valid-lifetime``, ``valid-lifetime``, and ``max-valid-lifetime``.
+ Since Kea 1.9.5, these values may be specified in client classes. The procedure
+ the server uses to select which lifetime value to use is as follows:
+
+ If the client query is a BOOTP query, the server always uses the
+ infinite lease time (e.g. 0xffffffff). Otherwise, the server must
+ determine which configured triplet to use by first searching all
+ classes assigned to the query, and then the subnet selected for
+ the query.
+
+ Classes are searched in the order they were assigned to the query; the
+ server uses the triplet from the first class that specifies it.
+ If no classes specify the triplet, the server uses the triplet
+ specified by the subnet selected for the client. If the subnet does not
+ explicitly specify it, the server next looks at the subnet's
+ shared-network (if one exists), then for a global specification, and
+ finally the global default.
+
+ If the client requested a lifetime value via DHCP option 51, then the
+ lifetime value used is the requested value bounded by the configured
+ triplet. In other words, if the requested lifetime is less than the
+ configured minimum, the configured minimum is used; if it is more
+ than the configured maximum, the configured maximum is used. If
+ the client did not provide a requested value, the lifetime value used
+ is the triplet default value.
+
+.. note::
+
+ Both ``renew-timer`` and ``rebind-timer``
+ are optional. The server only sends ``rebind-timer`` to the client,
+ via DHCPv4 option code 59, if it is less than ``valid-lifetime``; and it
+ only sends ``renew-timer``, via DHCPv4 option code 58, if it is less
+ than ``rebind-timer`` (or ``valid-lifetime`` if ``rebind-timer`` was not
+ specified). In their absence, the client should select values for T1
+ and T2 timers according to `RFC 2131 <https://tools.ietf.org/html/rfc2131>`_.
+ See section :ref:`dhcp4-t1-t2-times`
+ for more details on generating T1 and T2.
+
+The ``interfaces-config`` map specifies the network interfaces on which the
+server should listen to DHCP messages. The ``interfaces`` parameter specifies
+a list of network interfaces on which the server should listen. Lists are
+opened and closed with square brackets, with elements separated by commas. To
+listen on two interfaces, the ``interfaces-config`` element should look like
+this:
+
+::
+
+ {
+ "interfaces-config": {
+ "interfaces": [ "eth0", "eth1" ]
+ },
+ ...
+ }
+
+The next lines define the lease database, the place where the
+server stores its lease information. This particular example tells the
+server to use memfile, which is the simplest and fastest database
+backend. It uses an in-memory database and stores leases on disk in a
+CSV (comma-separated values) file. This is a very simple configuration example;
+usually the lease database configuration is more extensive and contains
+additional parameters. Note that ``lease-database`` is an object and opens up a
+new scope, using an opening brace. Its parameters (just one in this example:
+``type``) follow. If there were more than one, they would be separated
+by commas. This scope is closed with a closing brace. As more parameters
+for the ``Dhcp4`` definition follow, a trailing comma is present.
+
+Finally, we need to define a list of IPv4 subnets. This is the most
+important DHCPv4 configuration structure, as the server uses that
+information to process clients' requests. It defines all subnets from
+which the server is expected to receive DHCP requests. The subnets are
+specified with the ``subnet4`` parameter. It is a list, so it starts and
+ends with square brackets. Each subnet definition in the list has
+several attributes associated with it, so it is a structure and is
+opened and closed with braces. At a minimum, a subnet definition must
+have at least two parameters: ``subnet``, which defines the whole
+subnet; and ``pools``, which is a list of dynamically allocated pools
+that are governed by the DHCP server.
+
+The example contains a single subnet. If more than one were defined,
+additional elements in the ``subnet4`` parameter would be specified and
+separated by commas. For example, to define three subnets, the following
+syntax would be used:
+
+::
+
+ {
+ "subnet4": [
+ {
+ "id": 1,
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "subnet": "192.0.2.0/24"
+ },
+ {
+ "id": 2,
+ "pools": [ { "pool": "192.0.3.100 - 192.0.3.200" } ],
+ "subnet": "192.0.3.0/24"
+ },
+ {
+ "id": 3,
+ "pools": [ { "pool": "192.0.4.1 - 192.0.4.254" } ],
+ "subnet": "192.0.4.0/24"
+ }
+ ],
+ ...
+ }
+
+Note that indentation is optional and is used for aesthetic purposes
+only. In some cases it may be preferable to use more compact notation.
+
+After all the parameters have been specified, there are two contexts open:
+``global`` and ``Dhcp4``; thus, two closing curly brackets must be used to close
+them.
+
+Lease Storage
+-------------
+
+All leases issued by the server are stored in the lease database.
+There are three database backends available: memfile
+(the default), MySQL, PostgreSQL.
+
+Memfile - Basic Storage for Leases
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The server is able to store lease data in different repositories. Larger
+deployments may elect to store leases in a database;
+:ref:`database-configuration4` describes this option. In
+typical smaller deployments, though, the server stores lease
+information in a CSV file rather than a database. As well as requiring
+less administration, an advantage of using a file for storage is that it
+eliminates a dependency on third-party database software.
+
+The configuration of the memfile backend is controlled through
+the ``Dhcp4``/``lease-database`` parameters. The ``type`` parameter is mandatory
+and specifies which storage for leases the server should use, through
+the ``"memfile"`` value. The following list gives additional optional parameters
+that can be used to configure the memfile backend.
+
+- ``persist``: controls whether the new leases and updates to existing
+ leases are written to the file. It is strongly recommended that the
+ value of this parameter be set to ``true`` at all times during the
+ server's normal operation. Not writing leases to disk means that if a
+ server is restarted (e.g. after a power failure), it will not know
+ which addresses have been assigned. As a result, it may assign new clients
+ addresses that are already in use. The value of
+ ``false`` is mostly useful for performance-testing purposes. The
+ default value of the ``persist`` parameter is ``true``, which enables
+ writing lease updates to the lease file.
+
+- ``name``: specifies an absolute location of the lease file in which
+ new leases and lease updates are recorded. The default value for
+ this parameter is ``"[kea-install-dir]/var/lib/kea/kea-leases4.csv"``.
+
+- ``lfc-interval``: specifies the interval, in seconds, at which the
+ server will perform a lease file cleanup (LFC). This removes
+ redundant (historical) information from the lease file and
+ effectively reduces the lease file size. The cleanup process is
+ described in more detail later in this section. The default
+ value of the ``lfc-interval`` is ``3600``. A value of ``0`` disables the LFC.
+
+- ``max-row-errors``: specifies the number of row errors before the server
+ stops attempting to load a lease file. When the server loads a lease file, it is processed
+ row by row, each row containing a single lease. If a row is flawed and
+ cannot be processed correctly the server logs it, discards the row,
+ and goes on to the next row. This parameter can be used to set a limit on
+ the number of such discards that can occur, after which the server
+ abandons the effort and exits. The default value of ``0`` disables the limit
+ and allows the server to process the entire file, regardless of how many
+ rows are discarded.
+
+An example configuration of the memfile backend is presented below:
+
+::
+
+ "Dhcp4": {
+ "lease-database": {
+ "type": "memfile",
+ "persist": true,
+ "name": "/tmp/kea-leases4.csv",
+ "lfc-interval": 1800,
+ "max-row-errors": 100
+ }
+ }
+
+This configuration selects ``/tmp/kea-leases4.csv`` as the storage
+for lease information and enables persistence (writing lease updates to
+this file). It also configures the backend to perform a periodic cleanup
+of the lease file every 1800 seconds (30 minutes) and sets the maximum number of
+row errors to 100.
+
+Why Is Lease File Cleanup Necessary?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is important to know how the lease file contents are organized to
+understand why the periodic lease file cleanup is needed. Every time the
+server updates a lease or creates a new lease for a client, the new
+lease information must be recorded in the lease file. For performance
+reasons, the server does not update the existing client's lease in the
+file, as this would potentially require rewriting the entire file.
+Instead, it simply appends the new lease information to the end of the
+file; the previous lease entries for the client are not removed. When
+the server loads leases from the lease file, e.g. at server startup,
+it assumes that the latest lease entry for the client is the valid one.
+Previous entries are discarded, meaning that the server can
+reconstruct accurate information about the leases even though there
+may be many lease entries for each client. However, storing many entries
+for each client results in a bloated lease file and impairs the
+performance of the server's startup and reconfiguration, as it needs to
+process a larger number of lease entries.
+
+Lease file cleanup (LFC) removes all previous entries for each client
+and leaves only the latest ones. The interval at which the cleanup is
+performed is configurable, and it should be selected according to the
+frequency of lease renewals initiated by the clients. The more frequent
+the renewals, the smaller the value of ``lfc-interval`` should be. Note,
+however, that the LFC takes time and thus it is possible (although
+unlikely) that, if the ``lfc-interval`` is too short, a new cleanup may
+be started while the previous one is still running. The server would
+recover from this by skipping the new cleanup when it detected that the
+previous cleanup was still in progress, but it implies that the actual
+cleanups will be triggered more rarely than the configured interval. Moreover,
+triggering a new cleanup adds overhead to the server, which is not
+able to respond to new requests for a short period of time when the new
+cleanup process is spawned. Therefore, it is recommended that the
+``lfc-interval`` value be selected in a way that allows the LFC
+to complete the cleanup before a new cleanup is triggered.
+
+Lease file cleanup is performed by a separate process (in the
+background) to avoid a performance impact on the server process. To
+avoid conflicts between two processes using the same lease
+files, the LFC process starts with Kea opening a new lease file; the
+actual LFC process operates on the lease file that is no longer used by
+the server. There are also other files created as a side effect of the
+lease file cleanup. The detailed description of the LFC process is located later
+in this Kea Administrator's Reference Manual: :ref:`kea-lfc`.
+
+.. _database-configuration4:
+
+Lease Database Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. note::
+
+ Lease database access information must be configured for the DHCPv4
+ server, even if it has already been configured for the DHCPv6 server.
+ The servers store their information independently, so each server can
+ use a separate database or both servers can use the same database.
+
+.. note::
+
+ Kea requires the database timezone to match the system timezone.
+ For more details, see :ref:`mysql-database-create` and
+ :ref:`pgsql-database-create`.
+
+Lease database configuration is controlled through the
+``Dhcp4``/``lease-database`` parameters. The database type must be set to
+``memfile``, ``mysql`` or ``postgresql``, e.g.:
+
+::
+
+ "Dhcp4": { "lease-database": { "type": "mysql", ... }, ... }
+
+Next, the name of the database to hold the leases must be set; this is
+the name used when the database was created (see
+:ref:`mysql-database-create` or :ref:`pgsql-database-create`).
+
+For MySQL or PostgreSQL:
+
+::
+
+ "Dhcp4": { "lease-database": { "name": "database-name" , ... }, ... }
+
+If the database is located on a different system from the DHCPv4 server,
+the database host name must also be specified:
+
+::
+
+ "Dhcp4": { "lease-database": { "host": "remote-host-name", ... }, ... }
+
+Normally, the database is on the same machine as the DHCPv4 server.
+In this case, set the value to the empty string:
+
+::
+
+ "Dhcp4": { "lease-database": { "host" : "", ... }, ... }
+
+Should the database use a port other than the default, it may be
+specified as well:
+
+::
+
+ "Dhcp4": { "lease-database": { "port" : 12345, ... }, ... }
+
+Should the database be located on a different system, the administrator may need to
+specify a longer interval for the connection timeout:
+
+::
+
+ "Dhcp4": { "lease-database": { "connect-timeout" : timeout-in-seconds, ... }, ... }
+
+The default value of five seconds should be more than adequate for local
+connections. If a timeout is given, though, it should be an integer
+greater than zero.
+
+The maximum number of times the server automatically attempts to
+reconnect to the lease database after connectivity has been lost may be
+specified:
+
+::
+
+ "Dhcp4": { "lease-database": { "max-reconnect-tries" : number-of-tries, ... }, ... }
+
+If the server is unable to reconnect to the database after making the
+maximum number of attempts, the server will exit. A value of 0 (the
+default) disables automatic recovery and the server will exit
+immediately upon detecting a loss of connectivity (MySQL and PostgreSQL
+only).
+
+The number of milliseconds the server waits between attempts to
+reconnect to the lease database after connectivity has been lost may
+also be specified:
+
+::
+
+ "Dhcp4": { "lease-database": { "reconnect-wait-time" : number-of-milliseconds, ... }, ... }
+
+The default value for MySQL and PostgreSQL is 0, which disables automatic
+recovery and causes the server to exit immediately upon detecting the
+loss of connectivity.
+
+::
+
+ "Dhcp4": { "lease-database": { "on-fail" : "stop-retry-exit", ... }, ... }
+
+The possible values are:
+
+- ``stop-retry-exit`` - disables the DHCP service while trying to automatically
+ recover lost connections. Shuts down the server on failure after exhausting
+ ``max-reconnect-tries``. This is the default value for the lease backend,
+ the host backend, and the configuration backend.
+
+- ``serve-retry-exit`` - continues the DHCP service while trying to
+ automatically recover lost connections. Shuts down the server on failure
+ after exhausting ``max-reconnect-tries``.
+
+- ``serve-retry-continue`` - continues the DHCP service and does not shut down
+ the server even if the recovery fails. This is the default value for forensic
+ logging.
+
+.. note::
+
+ Automatic reconnection to database backends is configured individually per
+ backend; this allows users to tailor the recovery parameters to each backend
+ they use. We suggest that users enable it either for all backends or none,
+ so behavior is consistent.
+
+ Losing connectivity to a backend for which reconnection is disabled results
+ (if configured) in the server shutting itself down. This includes cases when
+ the lease database backend and the hosts database backend are connected to
+ the same database instance.
+
+ It is highly recommended not to change the ``stop-retry-exit`` default
+ setting for the lease manager, as it is critical for the connection to be
+ active while processing DHCP traffic. Change this only if the server is used
+ exclusively as a configuration tool.
+
+::
+
+ "Dhcp4": { "lease-database": { "retry-on-startup" : true, ... }, ... }
+
+During server startup, the inability to connect to any of the configured
+backends is considered fatal only if ``retry-on-startup`` is set to ``false``
+(the default). A fatal error is logged and the server exits, based on the idea
+that the configuration should be valid at startup. Exiting to the operating
+system allows nanny scripts to detect the problem.
+If ``retry-on-startup`` is set to ``true``, the server will start reconnection
+attempts even at server startup or on reconfigure events, and will honor the
+action specified in the ``on-fail`` parameter.
+
+The host parameter is used by the MySQL and PostgreSQL backends.
+
+Finally, the credentials of the account under which the server will
+access the database should be set:
+
+::
+
+ "Dhcp4": {
+ "lease-database": {
+ "user": "user-name",
+ "password": "password",
+ ...
+ },
+ ...
+ }
+
+If there is no password to the account, set the password to the empty
+string ``""``. (This is the default.)
+
+.. _tuning-database-timeouts4:
+
+Tuning Database Timeouts
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+In rare cases, reading or writing to the database may hang. This can be
+caused by a temporary network issue, or by misconfiguration of the proxy
+server switching the connection between different database instances.
+These situations are rare, but users have reported
+that Kea sometimes hangs while performing database IO operations.
+Setting appropriate timeout values can mitigate such issues.
+
+MySQL exposes two distinct connection options to configure the read and
+write timeouts. Kea's corresponding ``read-timeout`` and ``write-timeout``
+configuration parameters specify the timeouts in seconds. For example:
+
+::
+
+ "Dhcp4": { "lease-database": { "read-timeout" : 10, "write-timeout": 20, ... }, ... }
+
+
+Setting these parameters to 0 is equivalent to not specifying them, and
+causes the Kea server to establish a connection to the database with the
+MySQL defaults. In this case, Kea waits indefinitely for the completion of
+the read and write operations.
+
+MySQL versions earlier than 5.6 do not support setting timeouts for
+read and write operations. Moreover, the ``read-timeout`` and ``write-timeout``
+parameters can only be specified for the MySQL backend; setting them for
+any other backend database type causes a configuration error.
+
+To set a timeout in seconds for PostgreSQL, use the ``tcp-user-timeout``
+parameter. For example:
+
+::
+
+ "Dhcp4": { "lease-database": { "tcp-user-timeout" : 10, ... }, ... }
+
+
+Specifying this parameter for other backend types causes a configuration
+error.
+
+.. note::
+
+ The timeouts described here are only effective for TCP connections.
+ Please note that the MySQL client library used by the Kea servers
+ typically connects to the database via a UNIX domain socket when the
+ ``host`` parameter is ``localhost``, but establishes a TCP connection
+ for ``127.0.0.1``.
+
+
+.. _hosts4-storage:
+
+Hosts Storage
+-------------
+
+Kea is also able to store information about host reservations in the
+database. The hosts database configuration uses the same syntax as the
+lease database. In fact, the Kea server opens independent connections for
+each purpose, be it lease or hosts information, which gives
+the most flexibility. Kea can keep leases and host reservations
+separately, but can also point to the same database. Currently the
+supported hosts database types are MySQL and PostgreSQL.
+
+The following configuration can be used to configure a
+connection to MySQL:
+
+::
+
+ "Dhcp4": {
+ "hosts-database": {
+ "type": "mysql",
+ "name": "kea",
+ "user": "kea",
+ "password": "secret123",
+ "host": "localhost",
+ "port": 3306
+ }
+ }
+
+Depending on the database configuration, many of the
+parameters may be optional.
+
+Please note that usage of hosts storage is optional. A user can define
+all host reservations in the configuration file, and that is the
+recommended way if the number of reservations is small. However, when
+the number of reservations grows, it is more convenient to use host
+storage. Please note that both storage methods (the configuration file and
+one of the supported databases) can be used together. If hosts are
+defined in both places, the definitions from the configuration file are
+checked first and external storage is checked later, if necessary.
+
+Host information can be placed in multiple stores. Operations
+are performed on the stores in the order they are defined in the
+configuration file, although this leads to a restriction in ordering
+in the case of a host reservation addition; read-only stores must be
+configured after a (required) read-write store, or the addition will
+fail.
+
+.. note::
+
+ Kea requires the database timezone to match the system timezone.
+ For more details, see :ref:`mysql-database-create` and
+ :ref:`pgsql-database-create`.
+
+.. _hosts-databases-configuration4:
+
+DHCPv4 Hosts Database Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Hosts database configuration is controlled through the
+``Dhcp4``/``hosts-database`` parameters. If enabled, the type of database must
+be set to ``mysql`` or ``postgresql``.
+
+::
+
+ "Dhcp4": { "hosts-database": { "type": "mysql", ... }, ... }
+
+Next, the name of the database to hold the reservations must be set;
+this is the name used when the lease database was created (see
+:ref:`supported-databases` for instructions on how to set up the
+desired database type):
+
+::
+
+ "Dhcp4": { "hosts-database": { "name": "database-name" , ... }, ... }
+
+If the database is located on a different system than the DHCPv4 server,
+the database host name must also be specified:
+
+::
+
+ "Dhcp4": { "hosts-database": { "host": remote-host-name, ... }, ... }
+
+Normally, the database is on the same machine as the DHCPv4 server.
+In this case, set the value to the empty string:
+
+::
+
+ "Dhcp4": { "hosts-database": { "host" : "", ... }, ... }
+
+Should the database use a port different than the default, it may be
+specified as well:
+
+::
+
+ "Dhcp4": { "hosts-database": { "port" : 12345, ... }, ... }
+
+The maximum number of times the server automatically attempts to
+reconnect to the host database after connectivity has been lost may be
+specified:
+
+::
+
+ "Dhcp4": { "hosts-database": { "max-reconnect-tries" : number-of-tries, ... }, ... }
+
+If the server is unable to reconnect to the database after making the
+maximum number of attempts, the server will exit. A value of 0 (the
+default) disables automatic recovery and the server will exit
+immediately upon detecting a loss of connectivity (MySQL and PostgreSQL
+only).
+
+The number of milliseconds the server waits between attempts to
+reconnect to the host database after connectivity has been lost may also
+be specified:
+
+::
+
+ "Dhcp4": { "hosts-database": { "reconnect-wait-time" : number-of-milliseconds, ... }, ... }
+
+The default value for MySQL and PostgreSQL is 0, which disables automatic
+recovery and causes the server to exit immediately upon detecting the
+loss of connectivity.
+
+::
+
+ "Dhcp4": { "hosts-database": { "on-fail" : "stop-retry-exit", ... }, ... }
+
+The possible values are:
+
+- ``stop-retry-exit`` - disables the DHCP service while trying to automatically
+ recover lost connections. Shuts down the server on failure after exhausting
+ ``max-reconnect-tries``. This is the default value for MySQL and PostgreSQL.
+
+- ``serve-retry-exit`` - continues the DHCP service while trying to automatically
+ recover lost connections. Shuts down the server on failure after exhausting
+ ``max-reconnect-tries``.
+
+- ``serve-retry-continue`` - continues the DHCP service and does not shut down the
+ server even if the recovery fails.
+
+.. note::
+
+ Automatic reconnection to database backends is configured individually per
+ backend. This allows users to tailor the recovery parameters to each backend
+ they use. We suggest that users enable it either for all backends or none,
+ so behavior is consistent.
+
+ Losing connectivity to a backend for which reconnection is disabled results
+ (if configured) in the server shutting itself down. This includes cases when
+ the lease database backend and the hosts database backend are connected to
+ the same database instance.
+
+::
+
+ "Dhcp4": { "hosts-database": { "retry-on-startup" : true, ... }, ... }
+
+During server startup, the inability to connect to any of the configured
+backends is considered fatal only if ``retry-on-startup`` is set to ``false``
+(the default). A fatal error is logged and the server exits, based on the idea
+that the configuration should be valid at startup. Exiting to the operating
+system allows nanny scripts to detect the problem.
+If ``retry-on-startup`` is set to ``true``, the server will start reconnection
+attempts even at server startup or on reconfigure events, and will honor the
+action specified in the ``on-fail`` parameter.
+Database connection retries are not attempted on startup if the
+:ischooklib:`libdhcp_limits.so` is loaded because the hook library requires a
+valid connection to the database to check if JSON format is supported and to
+recount class limits.
+
+Finally, the credentials of the account under which the server will
+access the database should be set:
+
+::
+
+ "Dhcp4": {
+ "hosts-database": {
+ "user": "user-name",
+ "password": "password",
+ ...
+ },
+ ...
+ }
+
+If there is no password to the account, set the password to the empty
+string ``""``. (This is the default.)
+
+The multiple-storage extension uses a similar syntax; a configuration is
+placed into a ``hosts-databases`` list instead of into a ``hosts-database``
+entry, as in:
+
+::
+
+ "Dhcp4": { "hosts-databases": [ { "type": "mysql", ... }, ... ], ... }
+
+If the same host is configured both in-file and in-database, Kea does not issue a warning,
+as it would if both were specified in the same data source.
+Instead, the host configured in-file has priority over the one configured
+in-database.
+
+.. _read-only-database-configuration4:
+
+Using Read-Only Databases for Host Reservations With DHCPv4
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In some deployments, the user whose name is specified in the
+database backend configuration may not have write privileges to the
+database. This is often required by the policy within a given network to
+secure the data from being unintentionally modified. In many cases
+administrators have deployed inventory databases, which contain
+substantially more information about the hosts than just the static
+reservations assigned to them. The inventory database can be used to
+create a view of a Kea hosts database and such a view is often
+read-only.
+
+Kea host-database backends operate with an implicit configuration to
+both read from and write to the database. If the user does not
+have write access to the host database, the backend will fail to start
+and the server will refuse to start (or reconfigure). However, if access
+to a read-only host database is required for retrieving reservations
+for clients and/or assigning specific addresses and options, it is
+possible to explicitly configure Kea to start in "read-only" mode. This
+is controlled by the ``readonly`` boolean parameter as follows:
+
+::
+
+ "Dhcp4": { "hosts-database": { "readonly": true, ... }, ... }
+
+Setting this parameter to ``false`` configures the database backend to
+operate in "read-write" mode, which is also the default configuration if
+the parameter is not specified.
+
+.. note::
+
+ The ``readonly`` parameter is only supported for MySQL and
+ PostgreSQL databases.
+
+
+Tuning Database Timeouts for Hosts Storage
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+See :ref:`tuning-database-timeouts4`.
+
+.. _dhcp4-interface-configuration:
+
+Interface Configuration
+-----------------------
+
+The DHCPv4 server must be configured to listen on specific network
+interfaces. The simplest network interface configuration tells the
+server to listen on all available interfaces:
+
+::
+
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [ "*" ]
+ },
+ ...
+ }
+
+The asterisk plays the role of a wildcard and means "listen on all
+interfaces." However, it is usually a good idea to explicitly specify
+interface names:
+
+::
+
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [ "eth1", "eth3" ]
+ },
+ ...
+ }
+
+
+It is possible to use an interface wildcard (*) concurrently
+with explicit interface names:
+
+::
+
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [ "eth1", "eth3", "*" ]
+ },
+ ...
+ }
+
+This format should only be used when it is
+desired to temporarily override a list of interface names and listen on
+all interfaces.
+
+Some deployments of DHCP servers require that the servers listen on
+interfaces with multiple IPv4 addresses configured. In these situations,
+the address to use can be selected by appending an IPv4 address to the
+interface name in the following manner:
+
+::
+
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [ "eth1/10.0.0.1", "eth3/192.0.2.3" ]
+ },
+ ...
+ }
+
+
+Should the server be required to listen on multiple IPv4 addresses
+assigned to the same interface, multiple addresses can be specified for
+an interface as in the example below:
+
+::
+
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [ "eth1/10.0.0.1", "eth1/10.0.0.2" ]
+ },
+ ...
+ }
+
+
+Alternatively, if the server should listen on all addresses for the
+particular interface, an interface name without any address should be
+specified.
+
+Kea supports responding to directly connected clients which do not have
+an address configured. This requires the server to inject the hardware
+address of the destination into the data-link layer of the packet
+being sent to the client. The DHCPv4 server uses raw sockets to
+achieve this, and builds the entire IP/UDP stack for the outgoing
+packets. The downside of raw socket use, however, is that incoming and
+outgoing packets bypass the firewalls (e.g. iptables).
+
+Handling traffic on multiple IPv4 addresses assigned to the same
+interface can be a challenge, as raw sockets are bound to the
+interface. When the DHCP server is configured to use the raw socket on
+an interface to receive DHCP traffic, advanced packet filtering
+techniques (e.g. the BPF) must be used to receive unicast traffic on
+the desired addresses assigned to the interface. Whether clients use
+the raw socket or the UDP socket depends on whether they are directly
+connected (raw socket) or relayed (either raw or UDP socket).
+
+Therefore, in deployments where the server does not need to provision
+the directly connected clients and only receives the unicast packets
+from the relay agents, the Kea server should be configured to use UDP
+sockets instead of raw sockets. The following configuration
+demonstrates how this can be achieved:
+
+::
+
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [ "eth1", "eth3" ],
+ "dhcp-socket-type": "udp"
+ },
+ ...
+ }
+
+
+The ``dhcp-socket-type`` parameter specifies that the IP/UDP sockets will be
+opened on all interfaces on which the server listens, i.e. "eth1" and
+"eth3" in this example. If ``dhcp-socket-type`` is set to ``raw``, it
+configures the server to use raw sockets instead. If the
+``dhcp-socket-type`` value is not specified, the default value ``raw``
+is used.
+
+Using UDP sockets automatically disables the reception of broadcast
+packets from directly connected clients. This effectively means that UDP
+sockets can be used for relayed traffic only. When using raw sockets,
+both the traffic from the directly connected clients and the relayed
+traffic are handled.
+
+Caution should be taken when configuring the server
+to open multiple raw sockets on the interface with several IPv4
+addresses assigned. If the directly connected client sends the message
+to the broadcast address, all sockets on this link will receive this
+message and multiple responses will be sent to the client. Therefore,
+the configuration with multiple IPv4 addresses assigned to the interface
+should not be used when the directly connected clients are operating on
+that link. To use a single address on such an interface, the
+"interface-name/address" notation should be used.
+
+.. note::
+
+ Specifying the value ``raw`` as the socket type does not guarantee
+ that raw sockets will be used! The use of raw sockets to handle
+ traffic from the directly connected clients is currently
+ supported on Linux and BSD systems only. If raw sockets are not
+ supported on the particular OS in use, the server issues a warning and
+ fall back to using IP/UDP sockets.
+
+In a typical environment, the DHCP server is expected to send back a
+response on the same network interface on which the query was received.
+This is the default behavior. However, in some deployments it is desired
+that the outbound (response) packets be sent as regular traffic and
+the outbound interface be determined by the routing tables. This
+kind of asymmetric traffic is uncommon, but valid. Kea supports a
+parameter called ``outbound-interface`` that controls this behavior. It
+supports two values: the first one, ``same-as-inbound``, tells Kea to
+send back the response on the same interface where the query packet was
+received. This is the default behavior. The second parameter, ``use-routing``,
+tells Kea to send regular UDP packets and let the kernel's routing table
+determine the most appropriate interface. This only works when
+``dhcp-socket-type`` is set to ``udp``. An example configuration looks
+as follows:
+
+::
+
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [ "eth1", "eth3" ],
+ "dhcp-socket-type": "udp",
+ "outbound-interface": "use-routing"
+ },
+ ...
+ }
+
+Interfaces are re-detected at each reconfiguration. This behavior can be
+disabled by setting the ``re-detect`` value to ``false``, for instance:
+
+::
+
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [ "eth1", "eth3" ],
+ "re-detect": false
+ },
+ ...
+ }
+
+
+Note that interfaces are not re-detected during :isccmd:`config-test`.
+
+Usually loopback interfaces (e.g. the ``lo`` or ``lo0`` interface) are not
+configured, but if a loopback interface is explicitly configured and
+IP/UDP sockets are specified, the loopback interface is accepted.
+
+For example, this setup can be used to run Kea in a FreeBSD jail having only a
+loopback interface, to service a relayed DHCP request:
+
+::
+
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [ "lo0" ],
+ "dhcp-socket-type": "udp"
+ },
+ ...
+ }
+
+Kea binds the service sockets for each interface on startup. If another
+process is already using a port, then Kea logs the message and suppresses an
+error. DHCP service runs, but it is unavailable on some interfaces.
+
+The "service-sockets-require-all" option makes Kea require all sockets to
+be successfully bound. If any opening fails, Kea interrupts the
+initialization and exits with a non-zero status. (Default is false).
+
+::
+
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [ "eth1", "eth3" ],
+ "service-sockets-require-all": true
+ },
+ ...
+ }
+
+Sometimes, immediate interruption isn't a good choice. The port can be
+unavailable only temporary. In this case, retrying the opening may resolve
+the problem. Kea provides two options to specify the retrying:
+``service-sockets-max-retries`` and ``service-sockets-retry-wait-time``.
+
+The first defines a maximal number of retries that Kea makes to open a socket.
+The zero value (default) means that the Kea doesn't retry the process.
+
+The second defines a wait time (in milliseconds) between attempts. The default
+value is 5000 (5 seconds).
+
+::
+
+ "Dhcp4": {
+ "interfaces-config": {
+ "interfaces": [ "eth1", "eth3" ],
+ "service-sockets-max-retries": 5,
+ "service-sockets-retry-wait-time": 5000
+ },
+ ...
+ }
+
+If "service-sockets-max-retries" is non-zero and "service-sockets-require-all"
+is false, then Kea retries the opening (if needed) but does not fail if any
+socket is still not opened.
+
+.. _dhcpinform-unicast-issues:
+
+Issues With Unicast Responses to DHCPINFORM
+-------------------------------------------
+
+The use of UDP sockets has certain benefits in deployments where the
+server receives only relayed traffic; these benefits are mentioned in
+:ref:`dhcp4-interface-configuration`. From the
+administrator's perspective it is often desirable to configure the
+system's firewall to filter out unwanted traffic, and the use of UDP
+sockets facilitates this. However, the administrator must also be aware
+of the implications related to filtering certain types of traffic, as it
+may impair the DHCP server's operation.
+
+In this section we focus on the case when the server receives the
+DHCPINFORM message from the client via a relay. According to `RFC
+2131 <https://tools.ietf.org/html/rfc2131>`__, the server should unicast
+the DHCPACK response to the address carried in the ``ciaddr`` field. When
+the UDP socket is in use, the DHCP server relies on the low-level
+functions of an operating system to build the data link, IP, and UDP
+layers of the outgoing message. Typically, the OS first uses ARP to
+obtain the client's link-layer address to be inserted into the frame's
+header, if the address is not cached from a previous transaction that
+the client had with the server. When the ARP exchange is successful, the
+DHCP message can be unicast to the client, using the obtained address.
+
+Some system administrators block ARP messages in their network, which
+causes issues for the server when it responds to the DHCPINFORM
+messages because the server is unable to send the DHCPACK if the
+preceding ARP communication fails. Since the OS is entirely responsible
+for the ARP communication and then sending the DHCP packet over the
+wire, the DHCP server has no means to determine that the ARP exchange
+failed and the DHCP response message was dropped. Thus, the server does
+not log any error messages when the outgoing DHCP response is dropped.
+At the same time, all hooks pertaining to the packet-sending operation
+will be called, even though the message never reaches its destination.
+
+Note that the issue described in this section is not observed when
+raw sockets are in use, because, in this case, the DHCP server builds
+all the layers of the outgoing message on its own and does not use ARP.
+Instead, it inserts the value carried in the ``chaddr`` field of the
+DHCPINFORM message into the link layer.
+
+Server administrators willing to support DHCPINFORM messages via relays
+should not block ARP traffic in their networks, or should use raw sockets
+instead of UDP sockets.
+
+.. _ipv4-subnet-id:
+
+IPv4 Subnet Identifier
+----------------------
+
+The subnet identifier (subnet ID) is a unique number associated with a particular
+subnet. In principle, it is used to associate clients' leases with their
+respective subnets. The server configuration should contain unique and stable
+identifiers for all subnets. When a subnet identifier is not specified for a
+subnet, it is automatically assigned by the configuration mechanism. The identifiers
+are assigned starting at 1 and are monotonically increased for each subsequent
+subnet: 1, 2, 3, ....
+
+If there are multiple subnets configured with auto-generated identifiers
+and one of them is removed, the subnet identifiers may be renumbered.
+For example: if there are four subnets and the third is removed, the
+last subnet will be assigned the identifier that the third subnet had
+before removal. As a result, the leases stored in the lease database for
+subnet 3 are now associated with subnet 4, something that may have
+unexpected consequences. It is one of the reasons why auto-generated subnet
+identifiers are deprecated starting from Kea version 2.4.0.
+
+.. note::
+
+ The auto-generation of the subnet identifiers will be removed in a future
+ release. Starting from Kea 2.4.0, a subnet without an ``id`` entry
+ or with the zero value raises a warning at the configuration time.
+
+.. note::
+
+ Subnet IDs must be greater than zero and less than 4294967295.
+
+The following configuration assigns the specified subnet identifier
+to a newly configured subnet:
+
+::
+
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "subnet": "192.0.2.0/24",
+ "id": 1024,
+ ...
+ }
+ ]
+ }
+
+This identifier will not change for this subnet unless the ``id``
+parameter is removed or set to 0. The value of 0 forces auto-generation
+of the subnet identifier.
+
+.. _ipv4-subnet-prefix:
+
+IPv4 Subnet Prefix
+------------------
+
+The subnet prefix is the second way to identify a subnet. Kea can
+accept non-canonical subnet addresses; for instance,
+this configuration is accepted:
+
+::
+
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "subnet": "192.0.2.1/24",
+ ...
+ }
+ ]
+ }
+
+This works even if there is another subnet with the "192.0.2.0/24" prefix;
+only the textual form of subnets are compared to avoid duplicates.
+
+.. note::
+
+ Abuse of this feature can lead to incorrect subnet selection
+ (see :ref:`dhcp4-subnet-selection`).
+
+.. _dhcp4-address-config:
+
+Configuration of IPv4 Address Pools
+-----------------------------------
+
+The main role of a DHCPv4 server is address assignment. For this, the
+server must be configured with at least one subnet and one pool of
+dynamic addresses to be managed. For example, assume that the server is
+connected to a network segment that uses the 192.0.2.0/24 prefix. The
+administrator of that network decides that addresses from the range
+192.0.2.10 to 192.0.2.20 are going to be managed by the DHCPv4 server.
+Such a configuration can be achieved in the following way:
+
+::
+
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "subnet": "192.0.2.0/24",
+ "pools": [
+ { "pool": "192.0.2.10 - 192.0.2.20" }
+ ],
+ ...
+ }
+ ]
+ }
+
+Note that ``subnet`` is defined as a simple string, but the ``pools``
+parameter is actually a list of pools; for this reason, the pool
+definition is enclosed in square brackets, even though only one range of
+addresses is specified.
+
+Each ``pool`` is a structure that contains the parameters that describe
+a single pool. Currently there is only one parameter, ``pool``, which
+gives the range of addresses in the pool.
+
+It is possible to define more than one pool in a subnet; continuing the
+previous example, further assume that 192.0.2.64/26 should also be
+managed by the server. It could be written as 192.0.2.64 to 192.0.2.127,
+or it can be expressed more simply as 192.0.2.64/26. Both
+formats are supported by ``Dhcp4`` and can be mixed in the pool list. For
+example, the following pools could be defined:
+
+::
+
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "subnet": "192.0.2.0/24",
+ "pools": [
+ { "pool": "192.0.2.10-192.0.2.20" },
+ { "pool": "192.0.2.64/26" }
+ ],
+ ...
+ }
+ ],
+ ...
+ }
+
+White space in pool definitions is ignored, so spaces before and after
+the hyphen are optional. They can be used to improve readability.
+
+The number of pools is not limited, but for performance reasons it is
+recommended to use as few as possible.
+
+The server may be configured to serve more than one subnet. To add a
+second subnet, use a command similar to the following:
+
+::
+
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "subnet": "192.0.2.0/24",
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ ...
+ },
+ {
+ "subnet": "192.0.3.0/24",
+ "pools": [ { "pool": "192.0.3.100 - 192.0.3.200" } ],
+ ...
+ },
+ {
+ "subnet": "192.0.4.0/24",
+ "pools": [ { "pool": "192.0.4.1 - 192.0.4.254" } ],
+ ...
+ }
+ ]
+ }
+
+When configuring a DHCPv4 server using prefix/length notation, please
+pay attention to the boundary values. When specifying that the server
+can use a given pool, it is also able to allocate the first
+(typically a network address) and the last (typically a broadcast
+address) address from that pool. In the aforementioned example of pool
+192.0.3.0/24, both the 192.0.3.0 and 192.0.3.255 addresses may be
+assigned as well. This may be invalid in some network configurations. To
+avoid this, use the ``min-max`` notation.
+
+In a subnet whose prefix length is less than 24, users may wish to exclude all
+addresses ending in .0 and .255 from being dynamically allocated. For
+instance, in the subnet 10.0.0.0/8, an administrator may wish to exclude 10.x.y.0
+and 10.x.y.255 for all
+values of x and y, even though only 10.0.0.0 and 10.255.255.255 must be
+excluded according to RFC standards. The ``exclude-first-last-24`` configuration
+compatibility flag (:ref:`dhcp4-compatibility`) does this
+automatically, rather than requiring explicit configuration of many pools or
+reservations for fake hosts. When ``true``, it applies only to subnets of
+24 prefix length or smaller i.e. larger address space; the default is ``false``.
+
+In this case, "exclude" means to skip these addresses in the free address pickup
+routine of the allocation engine; if a client explicitly requests or
+has a host reservation for an address in .0 or .255, it will get it.
+
+.. note::
+
+ Here are some liberties and limits to the values that subnets and pools can
+ take in unusual Kea configurations:
+
+ +-------------------------------------------------------------+---------+--------------------------------------------------------------------------------------+
+ | Kea configuration case | Allowed | Comment |
+ +=============================================================+=========+======================================================================================+
+ | Overlapping subnets | Yes | Administrator should consider how clients are matched to these subnets. |
+ +-------------------------------------------------------------+---------+--------------------------------------------------------------------------------------+
+ | Overlapping pools in one subnet | No | Startup error: DHCP4_PARSER_FAIL |
+ +-------------------------------------------------------------+---------+--------------------------------------------------------------------------------------+
+ | Overlapping address pools in different subnets | Yes | Specifying the same address pool in different subnets can be used as an equivalent |
+ | | | of the global address pool. In that case, the server can assign addresses from the |
+ | | | same range regardless of the client's subnet. If an address from such a pool is |
+ | | | assigned to a client in one subnet, the same address will be renewed for this |
+ | | | client if it moves to another subnet. Another client in a different subnet will |
+ | | | not be assigned an address already assigned to the client in any of the subnets. |
+ +-------------------------------------------------------------+---------+--------------------------------------------------------------------------------------+
+ | Pools not matching the subnet prefix | No | Startup error: DHCP4_PARSER_FAIL |
+ +-------------------------------------------------------------+---------+--------------------------------------------------------------------------------------+
+
+.. _dhcp4-t1-t2-times:
+
+Sending T1 (Option 58) and T2 (Option 59)
+-----------------------------------------
+
+According to `RFC 2131 <https://tools.ietf.org/html/rfc2131>`__,
+servers should send values for T1 and T2 that are 50% and 87.5% of the
+lease lifetime, respectively. By default, :iscman:`kea-dhcp4` does not send
+either value; it can be configured to send values that are either specified
+explicitly or that are calculated as percentages of the lease time. The
+server's behavior is governed by a combination of configuration
+parameters, two of which have already been mentioned.
+To send specific, fixed values use the following two parameters:
+
+- ``renew-timer`` - specifies the value of T1 in seconds.
+
+- ``rebind-timer`` - specifies the value of T2 in seconds.
+
+The server only sends T2 if it is less than the valid lease time. T1
+is only sent if T2 is being sent and T1 is less than T2; or T2
+is not being sent and T1 is less than the valid lease time.
+
+Calculating the values is controlled by the following three parameters.
+
+- ``calculate-tee-times`` - when true, T1 and T2 are calculated as
+ percentages of the valid lease time. It defaults to false.
+
+- ``t1-percent`` - the percentage of the valid lease time to use for
+ T1. It is expressed as a real number between 0.0 and 1.0 and must be
+ less than ``t2-percent``. The default value is 0.50, per RFC 2131.
+
+- ``t2-percent`` - the percentage of the valid lease time to use for
+ T2. It is expressed as a real number between 0.0 and 1.0 and must be
+ greater than ``t1-percent``. The default value is .875, per RFC 2131.
+
+.. note::
+
+ In the event that both explicit values are specified and
+ ``calculate-tee-times`` is true, the server will use the explicit values.
+ Administrators with a setup where some subnets or shared-networks
+ use explicit values and some use calculated values must
+ not define the explicit values at any level higher than where they
+ will be used. Inheriting them from too high a scope, such as
+ global, will cause them to have explicit values at every level underneath
+ (shared-networks and subnets), effectively disabling calculated
+ values.
+
+.. _dhcp4-std-options:
+
+Standard DHCPv4 Options
+-----------------------
+
+One of the major features of the DHCPv4 server is the ability to provide
+configuration options to clients. Most of the options are sent by the
+server only if the client explicitly requests them using the Parameter
+Request List option. Those that do not require inclusion in the
+Parameter Request List option are commonly used options, e.g. "Domain
+Server", and options which require special behavior, e.g. "Client FQDN",
+which is returned to the client if the client has included this option
+in its message to the server.
+
+:ref:`dhcp4-std-options-list` comprises the list of the
+standard DHCPv4 options whose values can be configured using the
+configuration structures described in this section. This table excludes
+the options which require special processing and thus cannot be
+configured with fixed values. The last column of the table
+indicates which options can be sent by the server even when they are not
+requested in the Parameter Request List option, and those which are sent
+only when explicitly requested.
+
+The following example shows how to configure the addresses of DNS
+servers, which is one of the most frequently used options. Options
+specified in this way are considered global and apply to all configured
+subnets.
+
+::
+
+ "Dhcp4": {
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "code": 6,
+ "space": "dhcp4",
+ "csv-format": true,
+ "data": "192.0.2.1, 192.0.2.2"
+ },
+ ...
+ ]
+ }
+
+
+Note that either ``name`` or ``code`` is required; there is no need to
+specify both. ``space`` has a default value of ``dhcp4``, so this can be skipped
+as well if a regular (not encapsulated) DHCPv4 option is defined.
+Finally, ``csv-format`` defaults to ``true``, so it too can be skipped, unless
+the option value is specified as a hexadecimal string. Therefore,
+the above example can be simplified to:
+
+::
+
+ "Dhcp4": {
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "data": "192.0.2.1, 192.0.2.2"
+ },
+ ...
+ ]
+ }
+
+
+Defined options are added to the response when the client requests them,
+with a few exceptions which are always added. To enforce the addition of
+a particular option, set the ``always-send`` flag to ``true`` as in:
+
+::
+
+ "Dhcp4": {
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "data": "192.0.2.1, 192.0.2.2",
+ "always-send": true
+ },
+ ...
+ ]
+ }
+
+
+The effect is the same as if the client added the option code in the
+Parameter Request List option (or its equivalent for vendor options):
+
+::
+
+ "Dhcp4": {
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "data": "192.0.2.1, 192.0.2.2",
+ "always-send": true
+ },
+ ...
+ ],
+ "subnet4": [
+ {
+ "subnet": "192.0.3.0/24",
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "data": "192.0.3.1, 192.0.3.2"
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+
+In the example above, the ``domain-name-servers`` option respects the global
+``always-send`` flag and is always added to responses, but for subnet
+``192.0.3.0/24``, the value is taken from the subnet-level option data
+specification.
+
+Contrary to ``always-send``, if the ``never-send`` flag is set to
+``true`` for a particular option, the server does not add it to the response.
+The effect is the same as if the client removed the option code in the
+Parameter Request List option (or its equivalent for vendor options):
+
+::
+
+ "Dhcp4": {
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "data": "192.0.2.1, 192.0.2.2"
+ },
+ ...
+ ],
+ "subnet4": [
+ {
+ "subnet": "192.0.3.0/24",
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "never-send": true
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+In the example above, the ``domain-name-servers`` option is never added to
+responses on subnet ``192.0.3.0/24``. ``never-send`` has precedence over
+``always-send``, so if both are ``true`` the option is not added.
+
+.. note::
+
+ The ``always-send`` and ``never-send`` flags are sticky, meaning
+ they do not follow the usual configuration inheritance rules.
+ Instead, if they are enabled at least once along the configuration
+ inheritance chain, they are applied - even if they are
+ disabled in other places which would normally receive a higher priority.
+ For instance, if one of the flags is enabled in the global scope,
+ but disabled at the subnet level, it is enabled,
+ disregarding the subnet-level setting.
+
+.. note::
+
+ The ``never-send`` flag is less powerful than :ischooklib:`libdhcp_flex_option.so`;
+ for instance, it has no effect on options managed by the server itself.
+ Both ``always-send`` and ``never-send`` have no effect on options
+ which cannot be requested, for instance from a custom space.
+
+The ``name`` parameter specifies the option name. For a list of
+currently supported names, see :ref:`dhcp4-std-options-list`
+below. The ``code`` parameter specifies the option code, which must
+match one of the values from that list. The next line specifies the
+option space, which must always be set to ``dhcp4`` as these are standard
+DHCPv4 options. For other option spaces, including custom option spaces,
+see :ref:`dhcp4-option-spaces`. The next line specifies the format in
+which the data will be entered; use of CSV (comma-separated values) is
+recommended. The sixth line gives the actual value to be sent to
+clients. The data parameter is specified as normal text, with values separated by
+commas if more than one value is allowed.
+
+Options can also be configured as hexadecimal values. If ``csv-format``
+is set to ``false``, option data must be specified as a hexadecimal string.
+The following commands configure the ``domain-name-servers`` option for all
+subnets with the following addresses: 192.0.3.1 and 192.0.3.2. Note that
+``csv-format`` is set to ``false``.
+
+::
+
+ "Dhcp4": {
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "code": 6,
+ "space": "dhcp4",
+ "csv-format": false,
+ "data": "C0 00 03 01 C0 00 03 02"
+ },
+ ...
+ ],
+ ...
+ }
+
+Kea supports the following formats when specifying hexadecimal data:
+
+- ``Delimited octets`` - one or more octets separated by either colons or
+ spaces (":" or " "). While each octet may contain one or two digits,
+ we strongly recommend always using two digits. Valid examples are
+ "ab:cd:ef" and "ab cd ef".
+
+- ``String of digits`` - a continuous string of hexadecimal digits with
+ or without a "0x" prefix. Valid examples are "0xabcdef" and "abcdef".
+
+Care should be taken to use proper encoding when using hexadecimal
+format; Kea's ability to validate data correctness in hexadecimal is
+limited.
+
+It is also possible to specify data for binary options as
+a single-quoted text string within double quotes as shown (note that
+``csv-format`` must be set to ``false``):
+
+::
+
+ "Dhcp4": {
+ "option-data": [
+ {
+ "name": "user-class",
+ "code": 77,
+ "space": "dhcp4",
+ "csv-format": false,
+ "data": "'convert this text to binary'"
+ },
+ ...
+ ],
+ ...
+ }
+
+Most of the parameters in the ``option-data`` structure are optional and
+can be omitted in some circumstances, as discussed in :ref:`dhcp4-option-data-defaults`.
+
+It is possible to specify or override options on a per-subnet basis. If
+clients connected to most subnets are expected to get the same
+values of a given option, administrators should use global options. On the other
+hand, if different values are used in each subnet, it does not make sense
+to specify global option values; rather, only
+subnet-specific ones should be set.
+
+The following commands override the global DNS servers option for a
+particular subnet, setting a single DNS server with address 192.0.2.3:
+
+::
+
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "code": 6,
+ "space": "dhcp4",
+ "csv-format": true,
+ "data": "192.0.2.3"
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+In some cases it is useful to associate some options with an address
+pool from which a client is assigned a lease. Pool-specific option
+values override subnet-specific and global option values; it
+is not possible to prioritize assignment of pool-specific
+options via the order of pool declarations in the server
+configuration.
+
+The following configuration snippet demonstrates how to specify the DNS
+servers option, which is assigned to a client only if the client
+obtains an address from the given pool:
+
+::
+
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "pools": [
+ {
+ "pool": "192.0.2.1 - 192.0.2.200",
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "data": "192.0.2.3"
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+Options can also be specified in class or host-reservation scope. The
+current Kea options precedence order is (from most important to least): host
+reservation, pool, subnet, shared network, class, global.
+
+When a data field is a string and that string contains the comma (``,``;
+U+002C) character, the comma must be escaped with two backslashes (``\\,``;
+U+005C). This double escape is required because both the routine
+splitting of CSV data into fields and JSON use the same escape character; a
+single escape (``\,``) would make the JSON invalid. For example, the string
+"foo,bar" must be represented as:
+
+::
+
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "pools": [
+ {
+ "option-data": [
+ {
+ "name": "boot-file-name",
+ "data": "foo\\,bar"
+ }
+ ]
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+Some options are designated as arrays, which means that more than one
+value is allowed. For example, the option ``time-servers``
+allows the specification of more than one IPv4 address, enabling clients
+to obtain the addresses of multiple NTP servers.
+
+:ref:`dhcp4-custom-options` describes the
+configuration syntax to create custom option definitions (formats).
+Creation of custom definitions for standard options is generally not
+permitted, even if the definition being created matches the actual
+option format defined in the RFCs. However, there is an exception to this rule
+for standard options for which Kea currently does not provide a
+definition. To use such options, a server administrator must
+create a definition as described in
+:ref:`dhcp4-custom-options` in the ``dhcp4`` option space. This
+definition should match the option format described in the relevant RFC,
+but the configuration mechanism allows any option format as there is
+currently no way to validate it.
+
+The currently supported standard DHCPv4 options are listed in
+the table below. "Name" and "Code" are the
+values that should be used as a name/code in the option-data structures.
+"Type" designates the format of the data; the meanings of the various
+types are given in :ref:`dhcp-types`.
+
+.. _dhcp4-std-options-list:
+
+.. table:: List of standard DHCPv4 options configurable by an administrator
+
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | Name | Code | Type | Array? | Returned if |
+ | | | | | not |
+ | | | | | requested? |
+ +========================================+======+===========================+=============+=============+
+ | time-offset | 2 | int32 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | routers | 3 | ipv4-address | true | true |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | time-servers | 4 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | name-servers | 5 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | domain-name-servers | 6 | ipv4-address | true | true |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | log-servers | 7 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | cookie-servers | 8 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | lpr-servers | 9 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | impress-servers | 10 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | resource-location-servers | 11 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | boot-size | 13 | uint16 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | merit-dump | 14 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | domain-name | 15 | fqdn | false | true |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | swap-server | 16 | ipv4-address | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | root-path | 17 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | extensions-path | 18 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | ip-forwarding | 19 | boolean | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | non-local-source-routing | 20 | boolean | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | policy-filter | 21 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | max-dgram-reassembly | 22 | uint16 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | default-ip-ttl | 23 | uint8 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | path-mtu-aging-timeout | 24 | uint32 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | path-mtu-plateau-table | 25 | uint16 | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | interface-mtu | 26 | uint16 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | all-subnets-local | 27 | boolean | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | broadcast-address | 28 | ipv4-address | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | perform-mask-discovery | 29 | boolean | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | mask-supplier | 30 | boolean | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | router-discovery | 31 | boolean | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | router-solicitation-address | 32 | ipv4-address | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | static-routes | 33 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | trailer-encapsulation | 34 | boolean | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | arp-cache-timeout | 35 | uint32 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | ieee802-3-encapsulation | 36 | boolean | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | default-tcp-ttl | 37 | uint8 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | tcp-keepalive-interval | 38 | uint32 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | tcp-keepalive-garbage | 39 | boolean | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | nis-domain | 40 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | nis-servers | 41 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | ntp-servers | 42 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | vendor-encapsulated-options | 43 | empty | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | netbios-name-servers | 44 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | netbios-dd-server | 45 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | netbios-node-type | 46 | uint8 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | netbios-scope | 47 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | font-servers | 48 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | x-display-manager | 49 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | dhcp-option-overload | 52 | uint8 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | dhcp-server-identifier | 54 | ipv4-address | false | true |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | dhcp-message | 56 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | dhcp-max-message-size | 57 | uint16 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | vendor-class-identifier | 60 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | nwip-domain-name | 62 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | nwip-suboptions | 63 | binary | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | nisplus-domain-name | 64 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | nisplus-servers | 65 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | tftp-server-name | 66 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | boot-file-name | 67 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | mobile-ip-home-agent | 68 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | smtp-server | 69 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | pop-server | 70 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | nntp-server | 71 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | www-server | 72 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | finger-server | 73 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | irc-server | 74 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | streettalk-server | 75 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | streettalk-directory-assistance-server | 76 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | user-class | 77 | binary | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | slp-directory-agent | 78 | record (boolean, | true | false |
+ | | | ipv4-address) | | |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | slp-service-scope | 79 | record (boolean, string) | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | nds-server | 85 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | nds-tree-name | 86 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | nds-context | 87 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | bcms-controller-names | 88 | fqdn | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | bcms-controller-address | 89 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | client-system | 93 | uint16 | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | client-ndi | 94 | record (uint8, uint8, | false | false |
+ | | | uint8) | | |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | uuid-guid | 97 | record (uint8, binary) | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | uap-servers | 98 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | geoconf-civic | 99 | binary | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | pcode | 100 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | tcode | 101 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | v6-only-preferred | 108 | uint32 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | netinfo-server-address | 112 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | netinfo-server-tag | 113 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | v4-captive-portal | 114 | string | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | auto-config | 116 | uint8 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | name-service-search | 117 | uint16 | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | domain-search | 119 | fqdn | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | classless-static-route | 121 | internal | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | vivco-suboptions | 124 | record (uint32, binary) | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | vivso-suboptions | 125 | uint32 | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | pana-agent | 136 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | v4-lost | 137 | fqdn | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | capwap-ac-v4 | 138 | ipv4-address | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | sip-ua-cs-domains | 141 | fqdn | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | v4-sztp-redirect | 143 | tuple | true | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | rdnss-selection | 146 | record (uint8, | true | false |
+ | | | ipv4-address, | | |
+ | | | ipv4-address, fqdn) | | |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | v4-portparams | 159 | record (uint8, psid) | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | v4-dnr | 162 | record (uint16, uint16, | false | false |
+ | | | uint8, fqdn, binary) | | |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | option-6rd | 212 | record (uint8, uint8, | true | false |
+ | | | ipv6-address, | | |
+ | | | ipv4-address) | | |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+ | v4-access-domain | 213 | fqdn | false | false |
+ +----------------------------------------+------+---------------------------+-------------+-------------+
+
+.. note::
+
+ The ``default-url`` option was replaced with ``v4-captive-portal`` in Kea 2.1.2, as introduced by
+ `RFC 8910 <https://tools.ietf.org/html/rfc8910>`_. The new option has exactly the same format as the
+ old one. The general perception is that ``default-url`` was seldom used. If you used it and migrating,
+ please replace ``default-url`` with ``v4-captive-portal`` and your configuration will continue to work
+ as before.
+
+Kea also supports other options than those listed above; the following options
+are returned by the Kea engine itself and in general should not be configured
+manually.
+
+.. table:: List of standard DHCPv4 options managed by Kea on its own and not directly configurable by an administrator
+
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | Name | Code | Type | Description |
+ +================================+=======+=======================================+===================================================================+
+ | subnet-mask | 1 | ipv4-address | calculated automatically, based on subnet definition. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | host-name | 12 | string | sent by client, generally governed by the DNS configuration. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | dhcp-requested-address | 50 | ipv4-address | may be sent by the client and the server should not set it. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | dhcp-lease-time | 51 | uint32 | set automatically based on the ``valid-lifetime`` parameter. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | dhcp-message-type | 53 | string | sent by clients and servers. Set by the Kea engine depending on |
+ | | | | the situation and should never be configured explicitly. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | dhcp-parameter-request-list | 55 | uint8 array | sent by clients and should never be sent by the server. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | dhcp-renewal-time | 58 | uint32 | governed by ``renew-timer`` parameter. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | dhcp-rebinding-time | 59 | uint32 | governed by ``rebind-timer`` parameter. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | dhcp-client-identifier | 61 | binary | sent by client, echoed back with the value sent by the client. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | fqdn | 81 | record (uint8, uint8, uint8, fqdn) | part of the DDNS and D2 configuration. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | dhcp-agent-options | 82 | empty | sent by the relay agent. This is an empty container option; see |
+ | | | | RAI option detail later in this section. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | authenticate | 90 | binary | sent by client, Kea does not yet validate it. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | client-last-transaction-time | 91 | uint32 | sent by client, server does not set it. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | associated-ip | 92 | ipv4-address array | sent by client, server responds with list of addresses. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+ | subnet-selection | 118 | ipv4-address | if present in client's messages, will be used in the subnet |
+ | | | | selection process. |
+ +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+
+
+The following table lists all option types used in the previous two tables with a description of
+what values are accepted for them.
+
+.. _dhcp-types:
+
+.. table:: List of standard DHCP option types
+
+ +-----------------+-------------------------------------------------------+
+ | Name | Meaning |
+ +=================+=======================================================+
+ | binary | An arbitrary string of bytes, specified as a set |
+ | | of hexadecimal digits. |
+ +-----------------+-------------------------------------------------------+
+ | boolean | A boolean value with allowed |
+ | | values true or false. |
+ +-----------------+-------------------------------------------------------+
+ | empty | No value; data is carried in |
+ | | sub-options. |
+ +-----------------+-------------------------------------------------------+
+ | fqdn | Fully qualified domain name (e.g. |
+ | | www.example.com). |
+ +-----------------+-------------------------------------------------------+
+ | ipv4-address | IPv4 address in the usual |
+ | | dotted-decimal notation (e.g. |
+ | | 192.0.2.1). |
+ +-----------------+-------------------------------------------------------+
+ | ipv6-address | IPv6 address in the usual colon |
+ | | notation (e.g. 2001:db8::1). |
+ +-----------------+-------------------------------------------------------+
+ | ipv6-prefix | IPv6 prefix and prefix length |
+ | | specified using CIDR notation, |
+ | | e.g. 2001:db8:1::/64. This data |
+ | | type is used to represent an |
+ | | 8-bit field conveying a prefix |
+ | | length and the variable length |
+ | | prefix value. |
+ +-----------------+-------------------------------------------------------+
+ | psid | PSID and PSID length separated by |
+ | | a slash, e.g. 3/4 specifies |
+ | | PSID=3 and PSID length=4. In the |
+ | | wire format it is represented by |
+ | | an 8-bit field carrying PSID |
+ | | length (in this case equal to 4) |
+ | | and the 16-bits-long PSID value |
+ | | field (in this case equal to |
+ | | "0011000000000000b" using binary |
+ | | notation). Allowed values for a |
+ | | PSID length are 0 to 16. See `RFC |
+ | | 7597 <https://tools.ietf.org/html/rfc7597>`__ |
+ | | for details about the PSID wire |
+ | | representation. |
+ +-----------------+-------------------------------------------------------+
+ | record | Structured data that may be |
+ | | comprised of any types (except |
+ | | "record" and "empty"). The array |
+ | | flag applies to the last field |
+ | | only. |
+ +-----------------+-------------------------------------------------------+
+ | string | Any text. Please note that Kea |
+ | | silently discards any |
+ | | terminating/trailing nulls from |
+ | | the end of "string" options when |
+ | | unpacking received packets. This |
+ | | is in keeping with `RFC 2132, |
+ | | Section |
+ | | 2 <https://tools.ietf.org/html/rfc2132#section-2>`__. |
+ +-----------------+-------------------------------------------------------+
+ | tuple | A length encoded as an 8-bit (16-bit |
+ | | for DHCPv6) unsigned integer |
+ | | followed by a string of this |
+ | | length. |
+ +-----------------+-------------------------------------------------------+
+ | uint8 | An 8-bit unsigned integer with |
+ | | allowed values 0 to 255. |
+ +-----------------+-------------------------------------------------------+
+ | uint16 | A 16-bit unsigned integer with |
+ | | allowed values 0 to 65535. |
+ +-----------------+-------------------------------------------------------+
+ | uint32 | A 32-bit unsigned integer with |
+ | | allowed values 0 to 4294967295. |
+ +-----------------+-------------------------------------------------------+
+ | int8 | An 8-bit signed integer with allowed |
+ | | values -128 to 127. |
+ +-----------------+-------------------------------------------------------+
+ | int16 | A 16-bit signed integer with |
+ | | allowed values -32768 to 32767. |
+ +-----------------+-------------------------------------------------------+
+ | int32 | A 32-bit signed integer with |
+ | | allowed values -2147483648 to |
+ | | 2147483647. |
+ +-----------------+-------------------------------------------------------+
+
+Kea also supports the Relay Agent Information (RAI, defined in
+`RFC 3046 <https://tools.ietf.org/html/rfc3046>`_) option, sometimes referred to as the relay option, agent
+option, or simply option 82. The option itself is just a container and does not convey any information
+on its own. The following table contains a list of RAI sub-options that Kea can understand. The RAI
+and its sub-options are inserted by the relay agent and received by Kea; there is no need for Kea
+to be configured with those options. Kea's classification and flex-id in host reservations can be
+used to process those and other options no listed in the table below.
+
+.. table:: List of RAI sub-options that Kea can understand
+
+ +--------------------+------+----------------------------------------------------------------------+
+ | Name | Code | Comment |
+ +====================+======+======================================================================+
+ | circuit-id | 1 | Used when host-reservation-identifiers is set to `circuit-id`. |
+ +--------------------+------+----------------------------------------------------------------------+
+ | remote-id | 2 | Can be used with flex-id to identify hosts. |
+ +--------------------+------+----------------------------------------------------------------------+
+ | link-selection | 5 | If present, used to select the appropriate subnet. |
+ +--------------------+------+----------------------------------------------------------------------+
+ | subscriber-id | 6 | Can be used with flex-id to identify hosts. |
+ +--------------------+------+----------------------------------------------------------------------+
+ | server-id-override | 11 | If sent by the relay, Kea accepts it as the `server-id`. |
+ +--------------------+------+----------------------------------------------------------------------+
+ | relay-id | 12 | Identifies the relay |
+ +--------------------+------+----------------------------------------------------------------------+
+ | relay-port | 19 | If sent by the relay, Kea sends back its responses to this port. |
+ +--------------------+------+----------------------------------------------------------------------+
+
+All other RAI sub-options (including those not listed here) can be used in client classification to
+classify incoming packets to specific classes and/or by :ischooklib:`libdhcp_flex_id.so` to
+construct a unique device identifier. For more information about expressions used in client
+classification, and flex-id, see :ref:`classify`. The RAI sub-options can be
+referenced using ``relay4[option-code].hex``. For example, to classify packets based on the
+``remote-id`` (sub-option code 2), one would use ``relay4[2].hex``. An example client class that
+would include all packets with a specific ``remote-id`` value would looks as follows:
+
+::
+
+ "Dhcp4": {
+ "client-classes": [
+ {
+ "name": "remote-id-1020304",
+ "test": "relay4[2].hex == 0x01020304",
+ ...
+ }
+ ],
+ ...
+ }
+
+Classes may be used to segregate traffic into a relatively small number of groups, which then
+can be used to select specific subnets, pools and extra options and more. If per host behavior
+is necessary, using host reservations with flex-id is strongly recommended.
+
+.. _dhcp4-custom-options:
+
+Custom DHCPv4 Options
+---------------------
+
+Kea supports custom (non-standard) DHCPv4 options. Let's say that we want
+to define a new DHCPv4 option called ``foo``, which will have code 222
+and will convey a single, unsigned, 32-bit integer value.
+Such an option can be defined by putting the following entry in the configuration file:
+
+::
+
+ "Dhcp4": {
+ "option-def": [
+ {
+ "name": "foo",
+ "code": 222,
+ "type": "uint32",
+ "array": false,
+ "record-types": "",
+ "space": "dhcp4",
+ "encapsulate": ""
+ },
+ ...
+ ],
+ ...
+ }
+
+The ``false`` value of the ``array`` parameter determines that the
+option does NOT comprise an array of ``uint32`` values but is, instead, a
+single value. Two other parameters have been left blank:
+``record-types`` and ``encapsulate``. The former specifies the
+comma-separated list of option data fields, if the option comprises a
+record of data fields. The ``record-types`` value should be non-empty if
+``type`` is set to "record"; otherwise it must be left blank. The latter
+parameter specifies the name of the option space being encapsulated by
+the particular option. If the particular option does not encapsulate any
+option space, the parameter should be left blank. Note that the ``option-def``
+configuration statement only defines the format of an option and does
+not set its value(s).
+
+The ``name``, ``code``, and ``type`` parameters are required; all others
+are optional. The ``array`` parameter default value is ``false``. The
+``record-types`` and ``encapsulate`` parameters default values are blank
+(``""``). The default ``space`` is ``dhcp4``.
+
+Once the new option format is defined, its value is set in the same way
+as for a standard option. For example, the following commands set a
+global value that applies to all subnets.
+
+::
+
+ "Dhcp4": {
+ "option-data": [
+ {
+ "name": "foo",
+ "code": 222,
+ "space": "dhcp4",
+ "csv-format": true,
+ "data": "12345"
+ },
+ ...
+ ],
+ ...
+ }
+
+New options can take more complex forms than the simple use of primitives
+(uint8, string, ipv4-address, etc.); it is possible to define an option
+comprising a number of existing primitives.
+
+For example, say we want to define a new option that will consist of
+an IPv4 address, followed by an unsigned 16-bit integer, followed by a
+boolean value, followed by a text string. Such an option could be
+defined in the following way:
+
+::
+
+ "Dhcp4": {
+ "option-def": [
+ {
+ "name": "bar",
+ "code": 223,
+ "space": "dhcp4",
+ "type": "record",
+ "array": false,
+ "record-types": "ipv4-address, uint16, boolean, string",
+ "encapsulate": ""
+ },
+ ...
+ ],
+ ...
+ }
+
+The ``type`` parameter is set to ``"record"`` to indicate that the option
+contains multiple values of different types. These types are given as a
+comma-separated list in the ``record-types`` field and should be ones
+from those listed in :ref:`dhcp-types`.
+
+The option's values are set in an ``option-data`` statement as follows:
+
+::
+
+ "Dhcp4": {
+ "option-data": [
+ {
+ "name": "bar",
+ "space": "dhcp4",
+ "code": 223,
+ "csv-format": true,
+ "data": "192.0.2.100, 123, true, Hello World"
+ }
+ ],
+ ...
+ }
+
+The ``csv-format`` parameter is set to ``true`` to indicate that the ``data``
+field comprises a comma-separated list of values. The values in ``data`` must
+correspond to the types set in the ``record-types`` field of the option
+definition.
+
+When ``array`` is set to ``true`` and ``type`` is set to ``"record"``, the
+last field is an array, i.e. it can contain more than one value, as in:
+
+::
+
+ "Dhcp4": {
+ "option-def": [
+ {
+ "name": "bar",
+ "code": 223,
+ "space": "dhcp4",
+ "type": "record",
+ "array": true,
+ "record-types": "ipv4-address, uint16",
+ "encapsulate": ""
+ },
+ ...
+ ],
+ ...
+ }
+
+The new option content is one IPv4 address followed by one or more 16-bit
+unsigned integers.
+
+.. note::
+
+ In general, boolean values are specified as ``true`` or ``false``,
+ without quotes. Some specific boolean parameters may also accept
+ ``"true"``, ``"false"``, ``0``, ``1``, ``"0"``, and ``"1"``.
+
+.. note::
+
+ Numbers can be specified in decimal or hexadecimal format. The
+ hexadecimal format can be either plain (e.g. abcd) or prefixed with
+ 0x (e.g. 0xabcd).
+
+.. _dhcp4-private-opts:
+
+DHCPv4 Private Options
+----------------------
+
+Options with a code between 224 and 254 are reserved for private use.
+They can be defined at the global scope or at the client-class local
+scope; this allows option definitions to be used depending on context,
+and option data to be set accordingly. For instance, to configure an old
+PXEClient vendor:
+
+::
+
+ "Dhcp4": {
+ "client-classes": [
+ {
+ "name": "pxeclient",
+ "test": "option[vendor-class-identifier].text == 'PXEClient'",
+ "option-def": [
+ {
+ "name": "configfile",
+ "code": 209,
+ "type": "string"
+ }
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+As the Vendor-Specific Information (VSI) option (code 43) has a vendor-specific
+format, i.e. can carry either raw binary value or sub-options, this
+mechanism is also available for this option.
+
+In the following example taken from a real configuration, two vendor
+classes use option 43 for different and incompatible purposes:
+
+::
+
+ "Dhcp4": {
+ "option-def": [
+ {
+ "name": "cookie",
+ "code": 1,
+ "type": "string",
+ "space": "APC"
+ },
+ {
+ "name": "mtftp-ip",
+ "code": 1,
+ "type": "ipv4-address",
+ "space": "PXE"
+ },
+ ...
+ ],
+ "client-classes": [
+ {
+ "name": "APC",
+ "test": "option[vendor-class-identifier].text == 'APC'",
+ "option-def": [
+ {
+ "name": "vendor-encapsulated-options",
+ "type": "empty",
+ "encapsulate": "APC"
+ }
+ ],
+ "option-data": [
+ {
+ "name": "cookie",
+ "space": "APC",
+ "data": "1APC"
+ },
+ {
+ "name": "vendor-encapsulated-options"
+ },
+ ...
+ ],
+ ...
+ },
+ {
+ "name": "PXE",
+ "test": "option[vendor-class-identifier].text == 'PXE'",
+ "option-def": [
+ {
+ "name": "vendor-encapsulated-options",
+ "type": "empty",
+ "encapsulate": "PXE"
+ }
+ ],
+ "option-data": [
+ {
+ "name": "mtftp-ip",
+ "space": "PXE",
+ "data": "0.0.0.0"
+ },
+ {
+ "name": "vendor-encapsulated-options"
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+The definition used to decode a VSI option is:
+
+1. The local definition of a client class the incoming packet belongs
+ to;
+
+2. If none, the global definition;
+
+3. If none, the last-resort definition described in the next section,
+ :ref:`dhcp4-vendor-opts` (backward-compatible with previous Kea versions).
+
+.. note::
+
+ This last-resort definition for the Vendor-Specific Information
+ option (code 43) is not compatible with a raw binary value. When
+ there are known cases where a raw binary value will be used, a
+ client class must be defined with both a classification expression
+ matching these cases and an option definition for the VSI option with
+ a binary type and no encapsulation.
+
+.. note::
+
+ By default, in the Vendor-Specific Information option (code 43),
+ sub-option code 0 and 255 mean PAD and END respectively, according to
+ `RFC 2132 <https://tools.ietf.org/html/rfc2132>`_. In other words, the
+ sub-option code values of 0 and 255 are reserved. Kea does, however,
+ allow users to define sub-option codes from 0 to 255. If
+ sub-options with codes 0 and/or 255 are defined, bytes with that value are
+ no longer treated as a PAD or an END, but as the sub-option code
+ when parsing a VSI option in an incoming query.
+
+ Option 43 input processing (also called unpacking) is deferred so that it
+ happens after classification. This means clients cannot be classified
+ using option 43 sub-options. The definition used to unpack option 43
+ is determined as follows:
+
+ - If defined at the global scope, this definition is used.
+ - If defined at client class scope and the packet belongs to this
+ class, the client class definition is used.
+ - If not defined at global scope nor in a client class to which the
+ packet belongs, the built-in last resort definition is used. This
+ definition only says the sub-option space is
+ ``"vendor-encapsulated-options-space"``.
+
+ The output definition selection is a bit simpler:
+
+ - If the packet belongs to a client class which defines the option
+ 43, use this definition.
+ - If defined at the global scope, use this definition.
+ - Otherwise, use the built-in last-resort definition.
+
+ Since they use a specific/per vendor option space, sub-options
+ are defined at the global scope.
+
+.. note::
+
+ Option definitions in client classes are allowed only for this
+ limited option set (codes 43 and from 224 to 254), and only for
+ DHCPv4.
+
+.. _dhcp4-vendor-opts:
+
+DHCPv4 Vendor-Specific Options
+------------------------------
+
+Currently there are two option spaces defined for :iscman:`kea-dhcp4`:
+``dhcp4`` (for the top-level DHCPv4 options) and
+``"vendor-encapsulated-options-space"``, which is empty by default but in
+which options can be defined. Those options are carried in the
+Vendor-Specific Information option (code 43). The following examples
+show how to define an option ``foo`` with code 1 that
+comprises an IPv4 address, an unsigned 16-bit integer, and a string. The
+``foo`` option is conveyed in a Vendor-Specific Information option.
+
+The first step is to define the format of the option:
+
+::
+
+ "Dhcp4": {
+ "option-def": [
+ {
+ "name": "foo",
+ "code": 1,
+ "space": "vendor-encapsulated-options-space",
+ "type": "record",
+ "array": false,
+ "record-types": "ipv4-address, uint16, string",
+ "encapsulate": ""
+ }
+ ],
+ ...
+ }
+
+Note that the option space is set to ``"vendor-encapsulated-options-space"``.
+Once the option format is defined, the next step is to define actual values
+for that option:
+
+::
+
+ "Dhcp4": {
+ "option-data": [
+ {
+ "name": "foo",
+ "space": "vendor-encapsulated-options-space",
+ "code": 1,
+ "csv-format": true,
+ "data": "192.0.2.3, 123, Hello World"
+ }
+ ],
+ ...
+ }
+
+In this example, we also include the Vendor-Specific Information option, which
+conveys our sub-option ``foo``. This is required; otherwise, the option
+will not be included in messages sent to the client.
+
+::
+
+ "Dhcp4": {
+ "option-data": [
+ {
+ "name": "vendor-encapsulated-options"
+ }
+ ],
+ ...
+ }
+
+Alternatively, the option can be specified using its code.
+
+::
+
+ "Dhcp4": {
+ "option-data": [
+ {
+ "code": 43
+ }
+ ],
+ ...
+ }
+
+Another popular option that is often somewhat imprecisely called the "vendor
+option" is option 125. Its proper name is the "vendor-independent
+vendor-specific information option" or "vivso". The idea behind vivso options
+is that each vendor has its own unique set of options with their own custom
+formats. The vendor is identified by a 32-bit unsigned integer called
+``enterprise-number`` or ``vendor-id``.
+
+
+The standard spaces defined in Kea and their options are:
+
+- ``vendor-4491``: Cable Television Laboratories, Inc. for DOCSIS3 options:
+
++-------------+--------------+------------------------------------------------------------------------+
+| option code | option name | option description |
++=============+==============+========================================================================+
+| 1 | oro | ORO (or Option Request Option), used by clients to request a list of |
+| | | options they are interested in. |
++-------------+--------------+------------------------------------------------------------------------+
+| 2 | tftp-servers | a list of IPv4 addresses of TFTP servers to be used by the cable modem |
++-------------+--------------+------------------------------------------------------------------------+
+
+In Kea, each vendor is represented by its own vendor space. Since there are
+hundreds of vendors and they sometimes use different option definitions for
+different hardware, it is impossible for Kea to support them all natively.
+Fortunately, it is easy to define support for new vendor options. As an
+example, the Genexis home gateway device requires the vivso 125 option to be
+sent with a sub-option 2 that contains a string with the TFTP server URL. To
+support such a device, three steps are needed: first, establish option
+definitions that explain how the option is supposed to be formed; second,
+define option values; and third, tell Kea when to send those
+specific options, via client classification.
+
+An example snippet of a configuration could look similar to the
+following:
+
+::
+
+ "Dhcp4": {
+ // First, we need to define that the sub-option 2 in vivso option for
+ // vendor-id 25167 has a specific format (it's a plain string in this example).
+ // After this definition, we can specify values for option tftp.
+ "option-def": [
+ {
+ // We define a short name, so the option can be referenced by name.
+ // The option has code 2 and resides within vendor space 25167.
+ // Its data is a plain string.
+ "name": "tftp",
+ "code": 2,
+ "space": "vendor-25167",
+ "type": "string"
+ }
+ ],
+
+ "client-classes": [
+ {
+ // We now need to tell Kea how to recognize when to use vendor space 25167.
+ // Usually we can use a simple expression, such as checking if the device
+ // sent a vivso option with specific vendor-id, e.g. "vendor[4491].exists".
+ // Unfortunately, Genexis is a bit unusual in this aspect, because it
+ // doesn't send vivso. In this case we need to look into the vendor class
+ // (option code 60) and see if there's a specific string that identifies
+ // the device. Alternatively, one can make use of the automated `VENDOR_CLASS_`
+ // client class and replace "name" and "test" with `"name": "VENDOR_CLASS_HMC1000"`
+ // and no test expression.
+ "name": "cpe_genexis",
+ "test": "substring(option[60].hex,0,7) == 'HMC1000'",
+
+ // Once the device is recognized, we want to send two options:
+ // the vivso option with vendor-id set to 25167, and a sub-option 2.
+ "option-data": [
+ {
+ "name": "vivso-suboptions",
+ "data": "25167"
+ },
+
+ // The sub-option 2 value is defined as any other option. However,
+ // we want to send this sub-option 2, even when the client didn't
+ // explicitly request it (often there is no way to do that for
+ // vendor options). Therefore we use always-send to force Kea
+ // to always send this option when 25167 vendor space is involved.
+ {
+ "name": "tftp",
+ "space": "vendor-25167",
+ "data": "tftp://192.0.2.1/genexis/HMC1000.v1.3.0-R.img",
+ "always-send": true
+ }
+ ]
+ }
+ ]
+ }
+
+By default, Kea sends back only those options that are requested by a client,
+unless there are protocol rules that tell the DHCP server to always send an
+option. This approach works nicely in most cases and avoids problems with
+clients refusing responses with options they do not understand. However, the
+situation with vendor options is more complex, as they are not requested the
+same way as other options, are not well-documented in official RFCs, or vary by
+vendor.
+
+Some vendors (such as DOCSIS, identified by vendor option 4491) have a mechanism
+to request specific vendor options and Kea is able to honor those (sub-option 1).
+Unfortunately, for many other vendors, such as Genexis (25167, discussed above),
+Kea does not have such a mechanism, so it cannot send any sub-options on its own.
+To solve this issue, we devised the concept of persistent options. Kea can be
+told to always send options, even if the client did not request them. This can
+be achieved by adding ``"always-send": true`` to the option data entry. Note
+that in this particular case an option is defined in vendor space 25167. With
+``always-send`` enabled, the option is sent every time there is a need to deal
+with vendor space 25167.
+
+This is also how :iscman:`kea-dhcp4` can be configured to send multiple vendor options
+from different vendors, along with each of their specific vendor ID.
+If these options need to be sent by the server regardless of whether the client
+specified any enterprise number, ``"always-send": true`` must be configured
+for the suboptions that will be included in the ``vivso-suboptions`` option (code 125).
+
+::
+
+ "Dhcp4": {
+ "option-data": [
+ # Typically DHCPv4 clients will send a Parameter Request List option (code 55) for
+ # vivso-suboptions (code 125), and that is enough for Kea to understand that it needs to
+ # send the option. These options still need to be defined in the configuration, one per
+ # each vendor, but they don't need "always-send" enabled in that case. For misbehaving
+ # clients that to do not explicitly request it, one may alternatively set "always-send"
+ # to true for them as well. This is referring to the following two entries in option-data.
+ {
+ "name": "vivso-suboptions",
+ "space": "dhcp4",
+ "data": "2234"
+ },
+ {
+ "name": "vivso-suboptions",
+ "space": "dhcp4",
+ "data": "3561"
+ },
+ {
+ "always-send": true,
+ "data": "tagged",
+ "name": "tag",
+ "space": "vendor-2234"
+ },
+ {
+ "always-send": true,
+ "data": "https://example.com:1234/path",
+ "name": "url",
+ "space": "vendor-3561"
+ }
+ ],
+ "option-def": [
+ {
+ "code": 22,
+ "name": "tag",
+ "space": "vendor-2234",
+ "type": "string"
+ },
+ {
+ "code": 11,
+ "name": "url",
+ "space": "vendor-3561",
+ "type": "string"
+ }
+ ]
+ }
+
+Another possibility is to redefine the option; see :ref:`dhcp4-private-opts`.
+
+Kea comes with several example configuration files. Some of them showcase
+how to configure options 60 and 43. See ``doc/examples/kea4/vendor-specific.json``
+and ``doc/examples/kea4/vivso.json`` in the Kea sources.
+
+.. note::
+
+ :iscman:`kea-dhcp4` is able to recognize multiple Vendor Class Identifier
+ options (code 60) with different vendor IDs in the client requests and to
+ send multiple vivso options (code 125) in the responses, one for each vendor.
+
+ :iscman:`kea-dhcp4` honors DOCSIS sub-option 1 (ORO) and adds only requested options
+ if this sub-option is present in the client request.
+
+ Currently only one vendor is supported for the ``vivco-suboptions``
+ (code 124) option. Specifying multiple enterprise numbers within a single
+ option instance or multiple options with different enterprise numbers is not
+ supported.
+
+.. _dhcp4-option-spaces:
+
+Nested DHCPv4 Options (Custom Option Spaces)
+--------------------------------------------
+
+It is sometimes useful to define a completely new option space, such as
+when a user creates a new option in the standard option space
+(``dhcp4``) and wants this option to convey sub-options. Since they are in
+a separate space, sub-option codes have a separate numbering scheme
+and may overlap with the codes of standard options.
+
+Note that the creation of a new option space is not required when
+defining sub-options for a standard option, because one is created by
+default if the standard option is meant to convey any sub-options (see
+:ref:`dhcp4-vendor-opts`).
+
+If we want a DHCPv4 option called ``container`` with code 222,
+that conveys two sub-options with codes 1 and 2, we first need to
+define the new sub-options:
+
+::
+
+ "Dhcp4": {
+ "option-def": [
+ {
+ "name": "subopt1",
+ "code": 1,
+ "space": "isc",
+ "type": "ipv4-address",
+ "record-types": "",
+ "array": false,
+ "encapsulate": ""
+ },
+ {
+ "name": "subopt2",
+ "code": 2,
+ "space": "isc",
+ "type": "string",
+ "record-types": "",
+ "array": false,
+ "encapsulate": ""
+ }
+ ],
+ ...
+ }
+
+Note that we have defined the options to belong to a new option space
+(in this case, ``"isc"``).
+
+The next step is to define a regular DHCPv4 option with the desired code
+and specify that it should include options from the new option space:
+
+::
+
+ "Dhcp4": {
+ "option-def": [
+ {
+ "name": "container",
+ "code": 222,
+ "space": "dhcp4",
+ "type": "empty",
+ "array": false,
+ "record-types": "",
+ "encapsulate": "isc"
+ },
+ ...
+ ],
+ ...
+ }
+
+The name of the option space in which the sub-options are defined is set
+in the ``encapsulate`` field. The ``type`` field is set to ``"empty"``, to
+indicate that this option does not carry any data other than
+sub-options.
+
+Finally, we can set values for the new options:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "option-data": [
+ {
+ "name": "subopt1",
+ "code": 1,
+ "space": "isc",
+ "data": "192.0.2.3"
+ },
+ {
+ "name": "subopt2",
+ "code": 2,
+ "space": "isc",
+ "data": "Hello world"
+ },
+ {
+ "name": "container",
+ "code": 222,
+ "space": "dhcp4"
+ }
+ ]
+ }
+ }
+
+It is possible to create an option which carries some data in
+addition to the sub-options defined in the encapsulated option space.
+For example, if the ``container`` option from the previous example were
+required to carry a uint16 value as well as the sub-options, the
+``type`` value would have to be set to ``"uint16"`` in the option
+definition. (Such an option would then have the following data
+structure: DHCP header, uint16 value, sub-options.) The value specified
+with the ``data`` parameter — which should be a valid integer enclosed
+in quotes, e.g. ``"123"`` — would then be assigned to the ``uint16`` field in
+the ``container`` option.
+
+.. _dhcp4-option-data-defaults:
+
+Unspecified Parameters for DHCPv4 Option Configuration
+------------------------------------------------------
+
+In many cases it is not required to specify all parameters for an option
+configuration, and the default values can be used. However, it is
+important to understand the implications of not specifying some of them,
+as it may result in configuration errors. The list below explains the
+behavior of the server when a particular parameter is not explicitly
+specified:
+
+- ``name`` - the server requires either an option name or an option code to
+ identify an option. If this parameter is unspecified, the option code
+ must be specified.
+
+- ``code`` - the server requires either an option name or an option code to
+ identify an option; this parameter may be left unspecified if the
+ ``name`` parameter is specified. However, this also requires that the
+ particular option have a definition (either as a standard option or
+ an administrator-created definition for the option using an
+ ``option-def`` structure), as the option definition associates an
+ option with a particular name. It is possible to configure an option
+ for which there is no definition (unspecified option format).
+ Configuration of such options requires the use of the option code.
+
+- ``space`` - if the option space is unspecified it defaults to
+ ``dhcp4``, which is an option space holding standard DHCPv4 options.
+
+- ``data`` - if the option data is unspecified it defaults to an empty
+ value. The empty value is mostly used for the options which have no
+ payload (boolean options), but it is legal to specify empty values
+ for some options which carry variable-length data and for which the
+ specification allows a length of 0. For such options, the data
+ parameter may be omitted in the configuration.
+
+- ``csv-format`` - if this value is not specified, the server
+ assumes that the option data is specified as a list of comma-separated
+ values to be assigned to individual fields of the DHCP option.
+
+.. _dhcp4-support-for-long-options:
+
+Support for Long Options
+------------------------
+
+The :iscman:`kea-dhcp4` server partially supports long options (RFC3396).
+Since Kea 2.1.6, the server accepts configuring long options and sub-options
+(longer than 255 bytes). The options and sub-options are stored internally
+in their unwrapped form and they can be processed as usual using the parser
+language. On send, the server splits long options and sub-options into multiple
+options and sub-options, using the respective option code.
+
+::
+
+ {
+ "option-def": [
+ {
+ "array": false,
+ "code": 240,
+ "encapsulate": "",
+ "name": "my-option",
+ "space": "dhcp4",
+ "type": "string"
+ }
+ ],
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:ff",
+ "option-data": [
+ {
+ "always-send": false,
+ "code": 240,
+ "name": "my-option",
+ "csv-format": true,
+ "data": "data \
+ -00010203040506070809-00010203040506070809-00010203040506070809-00010203040506070809 \
+ -00010203040506070809-00010203040506070809-00010203040506070809-00010203040506070809 \
+ -00010203040506070809-00010203040506070809-00010203040506070809-00010203040506070809 \
+ -data",
+ "space": "dhcp4"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+.. note::
+
+ In the example above, the data has been wrapped into several lines for clarity,
+ but Kea does not support wrapping in the configuration file.
+
+This example illustrates configuring a custom long option (exceeding 255 octets)
+in a reservation. When sending a response, the server splits this option
+into two options, each with the code 240.
+
+.. note::
+
+ Currently the server does not support storing long options in databases,
+ either host reservations or the configuration backend.
+
+The server is also able to receive packets with split options (options using
+the same option code) and to fuse the data chunks into one option. This is
+also supported for sub-options if each sub-option data chunk also contains the
+sub-option code and sub-option length.
+
+.. _dhcp4-stateless-configuration:
+
+Stateless Configuration of DHCPv4 Clients
+-----------------------------------------
+
+The DHCPv4 server supports stateless client configuration, whereby
+the client has an IP address configured (e.g. using manual
+configuration) and only contacts the server to obtain other
+configuration parameters, such as addresses of DNS servers. To
+obtain the stateless configuration parameters, the client sends the
+DHCPINFORM message to the server with the ``ciaddr`` set to the address
+that the client is currently using. The server unicasts the DHCPACK
+message to the client that includes the stateless configuration
+("yiaddr" not set).
+
+The server responds to the DHCPINFORM when the client is associated
+with a subnet defined in the server's configuration. An example subnet
+configuration looks like this:
+
+::
+
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "code": 6,
+ "data": "192.0.2.200,192.0.2.201",
+ "csv-format": true,
+ "space": "dhcp4"
+ }
+ ]
+ }
+ ]
+ }
+
+This subnet specifies the single option which will be included in the
+DHCPACK message to the client in response to DHCPINFORM. The
+subnet definition does not require the address pool configuration if it
+will be used solely for stateless configuration.
+
+This server will associate the subnet with the client if one of the
+following conditions is met:
+
+- The DHCPINFORM is relayed and the ``giaddr`` matches the configured
+ subnet.
+
+- The DHCPINFORM is unicast from the client and the ``ciaddr`` matches the
+ configured subnet.
+
+- The DHCPINFORM is unicast from the client and the ``ciaddr`` is not set,
+ but the source address of the IP packet matches the configured
+ subnet.
+
+- The DHCPINFORM is not relayed and the IP address on the interface on
+ which the message is received matches the configured subnet.
+
+.. _dhcp4-client-classifier:
+
+Client Classification in DHCPv4
+-------------------------------
+
+The DHCPv4 server includes support for client classification. For a
+deeper discussion of the classification process, see :ref:`classify`.
+
+In certain cases it is useful to configure the server to differentiate
+between DHCP client types and treat them accordingly. Client
+classification can be used to modify the behavior of almost any part of
+DHCP message processing. Kea currently offers client classification
+via private options and option 43 deferred unpacking; subnet selection;
+pool selection; assignment of different options; and, for cable modems,
+specific options for use with the TFTP server address and the boot file
+field.
+
+Kea can be instructed to limit access to given subnets based on class
+information. This is particularly useful for cases where two types of
+devices share the same link and are expected to be served from two
+different subnets. The primary use case for such a scenario is cable
+networks, where there are two classes of devices: the cable modem
+itself, which should be handed a lease from subnet A; and all other
+devices behind the modem, which should get leases from subnet B. That
+segregation is essential to prevent overly curious end-users from playing
+with their cable modems. For details on how to set up class restrictions
+on subnets, see :ref:`classification-subnets`.
+
+When subnets belong to a shared network, the classification applies to
+subnet selection but not to pools; that is, a pool in a subnet limited to a
+particular class can still be used by clients which do not belong to the
+class, if the pool they are expected to use is exhausted. The limit
+on access based on class information is also available at the pool
+level within a subnet: see :ref:`classification-pools`. This is
+useful when segregating clients belonging to the same subnet into
+different address ranges.
+
+In a similar way, a pool can be constrained to serve only known clients,
+i.e. clients which have a reservation, using the built-in ``KNOWN`` or
+``UNKNOWN`` classes. Addresses can be assigned to registered clients
+without giving a different address per reservation: for instance, when
+there are not enough available addresses. The determination whether
+there is a reservation for a given client is made after a subnet is
+selected, so it is not possible to use ``KNOWN``/``UNKNOWN`` classes to select a
+shared network or a subnet.
+
+The process of classification is conducted in five steps. The first step
+is to assess an incoming packet and assign it to zero or more classes.
+The second step is to choose a subnet, possibly based on the class
+information. When the incoming packet is in the special class ``DROP``,
+it is dropped and a debug message logged.
+The next step is to evaluate class expressions depending on
+the built-in ``KNOWN``/``UNKNOWN`` classes after host reservation lookup,
+using them for pool selection and assigning classes from host
+reservations. The list of required classes is then built and each class
+of the list has its expression evaluated; when it returns ``true``, the
+packet is added as a member of the class. The last step is to assign
+options, again possibly based on the class information. More complete
+and detailed information is available in :ref:`classify`.
+
+There are two main methods of classification. The first is automatic and
+relies on examining the values in the vendor class options or the
+existence of a host reservation. Information from these options is
+extracted, and a class name is constructed from it and added to the
+class list for the packet. The second method specifies an expression that is
+evaluated for each packet. If the result is ``true``, the packet is a
+member of the class.
+
+.. note::
+
+ The new ``early-global-reservations-lookup`` global parameter flag
+ enables a lookup for global reservations before the subnet selection
+ phase. This lookup is similar to the general lookup described above
+ with two differences:
+
+ - the lookup is limited to global host reservations
+
+ - the ``UNKNOWN`` class is never set
+
+.. note::
+
+ Care should be taken with client classification, as it is easy for
+ clients that do not meet class criteria to be denied all service.
+
+Setting Fixed Fields in Classification
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is possible to specify that clients belonging to a particular class
+should receive packets with specific values in certain fixed fields. In
+particular, three fixed fields are supported: ``next-server`` (conveys
+an IPv4 address, which is set in the ``siaddr`` field), ``server-hostname``
+(conveys a server hostname, can be up to 64 bytes long, and is sent in
+the ``sname`` field) and ``boot-file-name`` (conveys the configuration file,
+can be up to 128 bytes long, and is sent using the ``file`` field).
+
+Obviously, there are many ways to assign clients to specific classes,
+but for PXE clients the client architecture type option (code 93)
+seems to be particularly suited to make the distinction. The following
+example checks whether the client identifies itself as a PXE device with
+architecture EFI x86-64, and sets several fields if it does. See
+`Section 2.1 of RFC
+4578 <https://tools.ietf.org/html/rfc4578#section-2.1>`__) or the
+client documentation for specific values.
+
+::
+
+ "Dhcp4": {
+ "client-classes": [
+ {
+ "name": "ipxe_efi_x64",
+ "test": "option[93].hex == 0x0009",
+ "next-server": "192.0.2.254",
+ "server-hostname": "hal9000",
+ "boot-file-name": "/dev/null"
+ },
+ ...
+ ],
+ ...
+ }
+
+If an incoming packet is matched to multiple classes, then the
+value used for each field will come from the first class that
+specifies the field, in the order the classes are assigned to the
+packet.
+
+.. note::
+
+ The classes are ordered as specified in the configuration.
+
+Using Vendor Class Information in Classification
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The server checks whether an incoming packet includes the vendor class
+identifier option (60). If it does, the content of that option is
+prepended with ``VENDOR_CLASS_``, and it is interpreted as a class. For
+example, modern cable modems send this option with value
+``docsis3.0``, so the packet belongs to the class
+``VENDOR_CLASS_docsis3.0``.
+
+.. note::
+
+ Certain special actions for clients in ``VENDOR_CLASS_docsis3.0`` can be
+ achieved by defining ``VENDOR_CLASS_docsis3.0`` and setting its
+ ``next-server`` and ``boot-file-name`` values appropriately.
+
+This example shows a configuration using an automatically generated
+``VENDOR_CLASS_`` class. The administrator of the 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 and only clients belonging to the DOCSIS 3.0 client
+class are allowed to use that pool.
+
+::
+
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ],
+ "client-class": "VENDOR_CLASS_docsis3.0"
+ }
+ ],
+ ...
+ }
+
+Defining and Using Custom Classes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The following example shows how to configure a class using an expression
+and a subnet using that class. This configuration defines the class
+named ``Client_foo``. It is comprised of all clients whose client IDs
+(option 61) start with the string ``foo``. Members of this class will be
+given addresses from 192.0.2.10 to 192.0.2.20 and the addresses of their
+DNS servers set to 192.0.2.1 and 192.0.2.2.
+
+::
+
+ "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": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ],
+ "client-class": "Client_foo"
+ },
+ ...
+ ],
+ ...
+ }
+
+.. _dhcp4-required-class:
+
+Required Classification
+~~~~~~~~~~~~~~~~~~~~~~~
+
+In some cases it is useful to limit the scope of a class to a
+shared network, subnet, or pool. There are two parameters which are used
+to limit the scope of the class by instructing the server to evaluate test
+expressions when required.
+
+The first one is the per-class ``only-if-required`` flag, which is ``false``
+by default. When it is set to ``true``, the test expression of the class
+is not evaluated at the reception of the incoming packet but later, and
+only if the class evaluation is required.
+
+The second is ``require-client-classes``, which takes a list of class
+names and is valid in shared-network, subnet, and pool scope. Classes in
+these lists are marked as required and evaluated after selection of this
+specific shared network/subnet/pool and before output-option processing.
+
+In this example, a class is assigned to the incoming packet when the
+specified subnet is used:
+
+::
+
+ "Dhcp4": {
+ "client-classes": [
+ {
+ "name": "Client_foo",
+ "test": "member('ALL')",
+ "only-if-required": true
+ },
+ ...
+ ],
+ "subnet4": [
+ {
+ "subnet": "192.0.2.0/24",
+ "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ],
+ "require-client-classes": [ "Client_foo" ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+Required evaluation can be used to express complex dependencies like
+subnet membership. It can also be used to reverse the
+precedence; if ``option-data`` is set in a subnet, it takes precedence
+over ``option-data`` in a class. If ``option-data`` is moved to a
+required class and required in the subnet, a class evaluated earlier
+may take precedence.
+
+Required evaluation is also available at the shared-network and pool levels.
+The order in which required classes are considered is: shared-network,
+subnet, and pool, i.e. in the reverse order from the way in which
+``option-data`` is processed.
+
+.. note::
+
+ Vendor-Identifying Vendor Options are a special case: for all other
+ options an option is identified by its code point, but ``vivco-suboptions``
+ (124) and ``vivso-suboptions`` (125) are identified by the pair of
+ code point and vendor identifier. This has no visible effect for
+ ``vivso-suboptions``, whose value is the vendor identifier, but it
+ is different for ``vivco-suboptions``, where the value is a record
+ with the vendor identifier and a binary value. For instance, in:
+
+::
+
+ "Dhcp4": {
+ "option-data": [
+ {
+ "name": "vivco-suboptions",
+ "always-send": true,
+ "data": "1234, 03666f6f"
+ },
+ {
+ "name": "vivco-suboptions",
+ "always-send": true,
+ "data": "5678, 03626172"
+ },
+ ...
+ ],
+ ...
+ }
+
+The first ``option-data`` entry does not hide the second one, because
+vendor identifiers (1234 and 5678) are different: the responses will carry
+two instances of the ``vivco-suboptions`` option, each for a different vendor.
+
+.. _dhcp4-ddns-config:
+
+DDNS for DHCPv4
+---------------
+
+As mentioned earlier, :iscman:`kea-dhcp4` can be configured to generate requests
+to the DHCP-DDNS server, :iscman:`kea-dhcp-ddns`, (referred to herein as "D2") to
+update DNS entries. These requests are known as NameChangeRequests or
+NCRs. Each NCR contains the following information:
+
+1. Whether it is a request to add (update) or remove DNS entries.
+
+2. Whether the change requests forward DNS updates (A records), reverse
+ DNS updates (PTR records), or both.
+
+3. The Fully Qualified Domain Name (FQDN), lease address, and DHCID
+ (information identifying the client associated with the FQDN).
+
+DDNS-related parameters are split into two groups:
+
+1. Connectivity Parameters
+
+ These are parameters which specify where and how :iscman:`kea-dhcp4` connects to
+ and communicates with D2. These parameters can only be specified
+ within the top-level ``dhcp-ddns`` section in the :iscman:`kea-dhcp4`
+ configuration. The connectivity parameters are listed below:
+
+ - ``enable-updates``
+ - ``server-ip``
+ - ``server-port``
+ - ``sender-ip``
+ - ``sender-port``
+ - ``max-queue-size``
+ - ``ncr-protocol``
+ - ``ncr-format"``
+
+2. Behavioral Parameters
+
+ These parameters influence behavior such as how client host names and
+ FQDN options are handled. They have been moved out of the ``dhcp-ddns``
+ section so that they may be specified at the global, shared-network,
+ and/or subnet levels. Furthermore, they are inherited downward from global to
+ shared-network to subnet. In other words, if a parameter is not specified at
+ a given level, the value for that level comes from the level above it.
+ The behavioral parameters are as follows:
+
+ - ``ddns-send-updates``
+ - ``ddns-override-no-update``
+ - ``ddns-override-client-update``
+ - ``ddns-replace-client-name"``
+ - ``ddns-generated-prefix``
+ - ``ddns-qualifying-suffix``
+ - ``ddns-update-on-renew``
+ - ``ddns-conflict-resolution-mode``
+ - ``ddns-ttl-percent``
+ - ``hostname-char-set``
+ - ``hostname-char-replacement``
+
+.. note::
+
+ For backward compatibility, configuration parsing still recognizes
+ the original behavioral parameters specified in ``dhcp-ddns``,
+ by translating the parameter into its global equivalent. If a
+ parameter is specified both globally and in ``dhcp-ddns``, the latter
+ value is ignored. In either case, a log is emitted explaining
+ what has occurred. Specifying these values within ``dhcp-ddns`` is
+ deprecated and support for it will be removed.
+
+The default configuration and values would appear as follows:
+
+::
+
+ "Dhcp4": {
+ "dhcp-ddns": {
+ // Connectivity parameters
+ "enable-updates": false,
+ "server-ip": "127.0.0.1",
+ "server-port":53001,
+ "sender-ip":"",
+ "sender-port":0,
+ "max-queue-size":1024,
+ "ncr-protocol":"UDP",
+ "ncr-format":"JSON"
+ },
+
+ // Behavioral parameters (global)
+ "ddns-send-updates": true,
+ "ddns-override-no-update": false,
+ "ddns-override-client-update": false,
+ "ddns-replace-client-name": "never",
+ "ddns-generated-prefix": "myhost",
+ "ddns-qualifying-suffix": "",
+ "ddns-update-on-renew": false,
+ "ddns-conflict-resolution-mode": "check-with-dhcid",
+ "hostname-char-set": "",
+ "hostname-char-replacement": "",
+ ...
+ }
+
+There are two parameters which determine if :iscman:`kea-dhcp4`
+can generate DDNS requests to D2: the existing ``dhcp-ddns:enable-updates``
+parameter, which now only controls whether :iscman:`kea-dhcp4` connects to D2;
+and the new behavioral parameter, ``ddns-send-updates``, which determines
+whether DDNS updates are enabled at a given level (i.e. global, shared-network,
+or subnet). The following table shows how the two parameters function
+together:
+
+.. table:: Enabling and disabling DDNS updates
+
+ +-----------------+--------------------+-------------------------------------+
+ | dhcp-ddns: | Global | Outcome |
+ | enable-updates | ddns-send-updates | |
+ +=================+====================+=====================================+
+ | false (default) | false | no updates at any scope |
+ +-----------------+--------------------+-------------------------------------+
+ | false | true (default) | no updates at any scope |
+ +-----------------+--------------------+-------------------------------------+
+ | true | false | updates only at scopes with |
+ | | | a local value of ``true`` for |
+ | | | ``ddns-enable-updates`` |
+ +-----------------+--------------------+-------------------------------------+
+ | true | true | updates at all scopes except those |
+ | | | with a local value of ``false`` |
+ | | | for ``ddns-enable-updates`` |
+ +-----------------+--------------------+-------------------------------------+
+
+Kea 1.9.1 added two new parameters; the first is ``ddns-update-on-renew``.
+Normally, when leases are renewed, the server only updates DNS if the DNS
+information for the lease (e.g. FQDN, DNS update direction flags) has changed.
+Setting ``ddns-update-on-renew`` to ``true`` instructs the server to always update
+the DNS information when a lease is renewed, even if its DNS information has not
+changed. This allows Kea to "self-heal" if it was previously unable
+to add DNS entries or they were somehow lost by the DNS server.
+
+.. note::
+
+ Setting ``ddns-update-on-renew`` to ``true`` may impact performance, especially
+ for servers with numerous clients that renew often.
+
+The second parameter added in Kea 1.9.1 is ``ddns-use-conflict-resolution``. This
+boolean parameter was passed through to D2 and enabled or disabled conflict resolution
+as described in `RFC 4703 <https://tools.ietf.org/html/rfc4703>`__. Beginning with
+Kea 2.5.0, it is deprecated and replaced by ``ddns-conflict-resolution-mode`` which
+offers four modes of conflict resolution-related behavior:
+
+ - ``check-with-dhcid`` - The default mode, it instructs D2 to carry out RFC
+ 4703-compliant conflict resolution. Existing DNS entries may only be
+ overwritten if they have a DHCID record and it matches the client's DHCID.
+ This is equivalent to ``ddns-use-conflict-resolution``: true;
+
+ - ``no-check-with-dhcid`` - Existing DNS entries may be overwritten by any
+ client, whether or not those entries include a DHCID record. The new entries
+ will include a DHCID record for the client to whom they belong.
+ This is equivalent to ``ddns-use-conflict-resolution``: false;
+
+ - ``check-exists-with-dhcid`` - Existing DNS entries may only be overwritten
+ if they have a DHCID record. The DHCID record need not match the client's DHCID.
+ This mode provides a way to protect static DNS entries (those that do not have
+ a DHCID record) while allowing dynamic entries (those that do have a DHCID
+ record) to be overwritten by any client. This behavior was not supported
+ prior to Kea 2.4.0.
+
+ - ``no-check-without-dhcid`` - Existing DNS entries may be overwritten by
+ any client. New entries will not include DHCID records. This behavior was
+ not supported prior to Kea 2.4.0.
+
+.. note::
+
+ For backward compatibility, ddns-use-conflict-resolution is still accepted in
+ JSON configuration. The server will replace the value internally, with the
+ ``ddns-conflict-resolution-mode`` and an appropriate value: `
+ `check-with-dhcid`` for ``true`` and ``no-check-with-dhcid`` for ``false``.
+
+.. note::
+
+ Setting ``ddns-conflict-resolution-mode`` to any value other than
+ ``check-with-dhcid`` disables the one or more overwrite safeguards
+ that the rules of conflict resolution (from
+ `RFC 4703 <https://tools.ietf.org/html/rfc4703>`__) are intended to
+ prevent. This means that existing entries for an FQDN or an
+ IP address made for Client-A can be deleted or replaced by entries
+ for Client-B. Furthermore, there are two scenarios by which entries
+ for multiple clients for the same key (e.g. FQDN or IP) can be created.
+
+ 1. Client-B uses the same FQDN as Client-A but a different IP address.
+ In this case, the forward DNS entries (A and DHCID RRs) for
+ Client-A will be deleted as they match the FQDN and new entries for
+ Client-B will be added. The reverse DNS entries (PTR and DHCID RRs)
+ for Client-A, however, will not be deleted as they belong to a different
+ IP address, while new entries for Client-B will still be added.
+
+ 2. Client-B uses the same IP address as Client-A but a different FQDN.
+ In this case the reverse DNS entries (PTR and DHCID RRs) for Client-A
+ will be deleted as they match the IP address, and new entries for
+ Client-B will be added. The forward DNS entries (A and DHCID RRs)
+ for Client-A, however, will not be deleted, as they belong to a different
+ FQDN, while new entries for Client-B will still be added.
+
+ Disabling conflict resolution should be done only after careful review of
+ specific use cases. The best way to avoid unwanted DNS entries is to
+ always ensure lease changes are processed through Kea, whether they are
+ released, expire, or are deleted via the :isccmd:`lease4-del` command, prior to
+ reassigning either FQDNs or IP addresses. Doing so causes :iscman:`kea-dhcp4`
+ to generate DNS removal requests to D2.
+
+The DNS entries Kea creates contain a value for TTL (time to live).
+The :iscman:`kea-dhcp4` server calculates that value based on
+`RFC 4702, Section 5 <https://tools.ietf.org/html/rfc4702#section-5>`__,
+which suggests that the TTL value be 1/3 of the lease's lifetime, with
+a minimum value of 10 minutes.
+
+The parameter ``ddns-ttl-percent``, when specified,
+causes the TTL to be calculated as a simple percentage of the lease's
+lifetime, using the parameter's value as the percentage. It is specified
+as a decimal percent (e.g. .25, .75, 1.00) and may be specified at the
+global, shared-network, and subnet levels. By default it is unspecified.
+
+.. _dhcpv4-d2-io-config:
+
+DHCP-DDNS Server Connectivity
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For NCRs to reach the D2 server, :iscman:`kea-dhcp4` must be able to communicate
+with it. :iscman:`kea-dhcp4` uses the following configuration parameters to
+control this communication:
+
+- ``enable-updates`` - Enables connectivity to :iscman:`kea-dhcp-ddns` such that DDNS
+ updates can be constructed and sent.
+ It must be ``true`` for NCRs to be generated and sent to D2.
+ It defaults to ``false``.
+
+- ``server-ip`` - This is the IP address on which D2 listens for requests. The
+ default is the local loopback interface at address 127.0.0.1.
+ Either an IPv4 or IPv6 address may be specified.
+
+- ``server-port`` - This is the port on which D2 listens for requests. The default
+ value is ``53001``.
+
+- ``sender-ip`` - This is the IP address which :iscman:`kea-dhcp4` uses to send requests to
+ D2. The default value is blank, which instructs :iscman:`kea-dhcp4` to select a
+ suitable address.
+
+- ``sender-port`` - This is the port which :iscman:`kea-dhcp4` uses to send requests to D2.
+ The default value of ``0`` instructs :iscman:`kea-dhcp4` to select a suitable port.
+
+- ``max-queue-size`` - This is the maximum number of requests allowed to queue
+ while waiting to be sent to D2. This value guards against requests
+ accumulating uncontrollably if they are being generated faster than
+ they can be delivered. If the number of requests queued for
+ transmission reaches this value, DDNS updating is turned off
+ until the queue backlog has been sufficiently reduced. The intent is
+ to allow the :iscman:`kea-dhcp4` server to continue lease operations without
+ running the risk that its memory usage grows without limit. The
+ default value is ``1024``.
+
+- ``ncr-protocol`` - This specifies the socket protocol to use when sending requests to
+ D2. Currently only UDP is supported.
+
+- ``ncr-format`` - This specifies the packet format to use when sending requests to D2.
+ Currently only JSON format is supported.
+
+By default, :iscman:`kea-dhcp-ddns` is assumed to be running on the same machine
+as :iscman:`kea-dhcp4`, and all of the default values mentioned above should be
+sufficient. If, however, D2 has been configured to listen on a different
+address or port, these values must be altered accordingly. For example, if
+D2 has been configured to listen on 192.168.1.10 port 900, the following
+configuration is required:
+
+::
+
+ "Dhcp4": {
+ "dhcp-ddns": {
+ "server-ip": "192.168.1.10",
+ "server-port": 900,
+ ...
+ },
+ ...
+ }
+
+.. _dhcpv4-d2-rules-config:
+
+When Does the :iscman:`kea-dhcp4` Server Generate a DDNS Request?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :iscman:`kea-dhcp4` server follows the behavior prescribed for DHCP servers in
+`RFC 4702 <https://tools.ietf.org/html/rfc4702>`__. It is important to keep
+in mind that :iscman:`kea-dhcp4` makes the initial decision of when and what to
+update and forwards that information to D2 in the form of NCRs. Carrying
+out the actual DNS updates and dealing with such things as conflict
+resolution are within the purview of D2 itself
+(see :ref:`dhcp-ddns-server`). This section describes when :iscman:`kea-dhcp4`
+generates NCRs and the configuration parameters that can be used to
+influence this decision. It assumes that both the connectivity parameter
+``enable-updates`` and the behavioral parameter ``ddns-send-updates``,
+are ``true``.
+
+In general, :iscman:`kea-dhcp4` generates DDNS update requests when:
+
+1. A new lease is granted in response to a DHCPREQUEST;
+
+2. An existing lease is renewed but the FQDN associated with it has
+ changed; or
+
+3. An existing lease is released in response to a DHCPRELEASE.
+
+In the second case, lease renewal, two DDNS requests are issued: one
+request to remove entries for the previous FQDN, and a second request to
+add entries for the new FQDN. In the third case, a lease release - a
+single DDNS request - to remove its entries will be made.
+
+As for the first case, the decisions involved when granting a new lease are
+more complex. When a new lease is granted, :iscman:`kea-dhcp4` generates a
+DDNS update request if the DHCPREQUEST contains either the FQDN option
+(code 81) or the Host Name option (code 12). If both are present, the
+server uses the FQDN option.
+By default, :iscman:`kea-dhcp4` respects the FQDN N and S flags
+specified by the client as shown in the following table:
+
+.. table:: Default FQDN flag behavior
+
+ +------------+-----------------+-----------------+-------------+
+ | Client | Client Intent | Server Response | Server |
+ | Flags:N-S | | | Flags:N-S-O |
+ +============+=================+=================+=============+
+ | 0-0 | Client wants to | Server | 1-0-0 |
+ | | do forward | generates | |
+ | | updates, server | reverse-only | |
+ | | should do | request | |
+ | | reverse updates | | |
+ +------------+-----------------+-----------------+-------------+
+ | 0-1 | Server should | Server | 0-1-0 |
+ | | do both forward | generates | |
+ | | and reverse | request to | |
+ | | updates | update both | |
+ | | | directions | |
+ +------------+-----------------+-----------------+-------------+
+ | 1-0 | Client wants no | Server does not | 1-0-0 |
+ | | updates done | generate a | |
+ | | | request | |
+ +------------+-----------------+-----------------+-------------+
+
+The first row in the table above represents "client delegation." Here
+the DHCP client states that it intends to do the forward DNS updates and
+the server should do the reverse updates. By default, :iscman:`kea-dhcp4`
+honors the client's wishes and generates a DDNS request to the D2 server
+to update only reverse DNS data. The parameter
+``ddns-override-client-update`` can be used to instruct the server to
+override client delegation requests. When this parameter is ``true``,
+:iscman:`kea-dhcp4` disregards requests for client delegation and generates a
+DDNS request to update both forward and reverse DNS data. In this case,
+the N-S-O flags in the server's response to the client will be 0-1-1
+respectively.
+
+(Note that the flag combination N=1, S=1 is prohibited according to `RFC
+4702 <https://tools.ietf.org/html/rfc4702>`__. If such a combination is
+received from the client, the packet will be dropped by :iscman:`kea-dhcp4`.)
+
+To override client delegation, set the following values in the
+configuration file:
+
+::
+
+ "Dhcp4": {
+ "ddns-override-client-update": true,
+ ...
+ }
+
+The third row in the table above describes the case in which the client
+requests that no DNS updates be done. The parameter
+``ddns-override-no-update`` can be used to instruct the server to disregard
+the client's wishes. When this parameter is ``true``, :iscman:`kea-dhcp4`
+generates DDNS update requests to :iscman:`kea-dhcp-ddns` even if the client
+requests that no updates be done. The N-S-O flags in the server's response to
+the client will be 0-1-1.
+
+To override client delegation, issue the following commands:
+
+::
+
+ "Dhcp4": {
+ "ddns-override-no-update": true,
+ ...
+ }
+
+The :iscman:`kea-dhcp4` server always generates DDNS update requests if the
+client request only contains the Host Name option. In addition, it includes
+an FQDN option in the response to the client with the FQDN N-S-O flags
+set to 0-1-0, respectively. The domain name portion of the FQDN option
+is the name submitted to D2 in the DDNS update request.
+
+.. _dhcpv4-fqdn-name-generation:
+
+:iscman:`kea-dhcp4` Name Generation for DDNS Update Requests
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Each NameChangeRequest must of course include the fully qualified domain
+name whose DNS entries are to be affected. :iscman:`kea-dhcp4` can be configured
+to supply a portion or all of that name, based on what it receives
+from the client in the DHCPREQUEST.
+
+The default rules for constructing the FQDN that will be used for DNS
+entries are:
+
+1. If the DHCPREQUEST contains the client FQDN option, take the
+ candidate name from there; otherwise, take it from the Host Name
+ option.
+
+2. If the candidate name is a partial (i.e. unqualified) name, then add
+ a configurable suffix to the name and use the result as the FQDN.
+
+3. If the candidate name provided is empty, generate an FQDN using a
+ configurable prefix and suffix.
+
+4. If the client provides neither option, then take no DNS action.
+
+These rules can be amended by setting the ``ddns-replace-client-name``
+parameter, which provides the following modes of behavior:
+
+- ``never`` - use the name the client sent. If the client sent no name,
+ do not generate one. This is the default mode.
+
+- ``always`` - replace the name the client sent. If the client sent no
+ name, generate one for the client.
+
+- ``when-present`` - replace the name the client sent. If the client
+ sent no name, do not generate one.
+
+- ``when-not-present`` - use the name the client sent. If the client
+ sent no name, generate one for the client.
+
+.. note::
+
+ In early versions of Kea, this parameter was a boolean and permitted only
+ values of ``true`` and ``false``. Boolean values have been deprecated
+ and are no longer accepted. Administrators currently using booleans
+ must replace them with the desired mode name. A value of ``true``
+ maps to ``when-present``, while ``false`` maps to ``never``.
+
+For example, to instruct :iscman:`kea-dhcp4` to always generate the FQDN for a
+client, set the parameter ``ddns-replace-client-name`` to ``always`` as
+follows:
+
+::
+
+ "Dhcp4": {
+ "ddns-replace-client-name": "always",
+ ...
+ }
+
+The prefix used in the generation of an FQDN is specified by the
+``ddns-generated-prefix`` parameter. The default value is "myhost". To alter
+its value, simply set it to the desired string:
+
+::
+
+ "Dhcp4": {
+ "ddns-generated-prefix": "another.host",
+ ...
+ }
+
+The suffix used when generating an FQDN, or when qualifying a partial
+name, is specified by the ``ddns-qualifying-suffix`` parameter. It is
+strongly recommended that the user supply a value for the qualifying
+suffix when DDNS updates are enabled. For obvious reasons, we cannot
+supply a meaningful default.
+
+::
+
+ "Dhcp4": {
+ "ddns-qualifying-suffix": "foo.example.org",
+ ...
+ }
+
+When qualifying a partial name, :iscman:`kea-dhcp4` constructs the name in the
+format:
+
+``[candidate-name].[ddns-qualifying-suffix].``
+
+where ``candidate-name`` is the partial name supplied in the DHCPREQUEST.
+For example, if the FQDN domain name value is "some-computer" and the
+``ddns-qualifying-suffix`` is "example.com", the generated FQDN is:
+
+``some-computer.example.com.``
+
+When generating the entire name, :iscman:`kea-dhcp4` constructs the name in
+the format:
+
+``[ddns-generated-prefix]-[address-text].[ddns-qualifying-suffix].``
+
+where ``address-text`` is simply the lease IP address converted to a
+hyphenated string. For example, if the lease address is 172.16.1.10, the
+qualifying suffix is "example.com", and the default value is used for
+``ddns-generated-prefix``, the generated FQDN is:
+
+``myhost-172-16-1-10.example.com.``
+
+.. _dhcp4-host-name-sanitization:
+
+Sanitizing Client Host Name and FQDN Names
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some DHCP clients may provide values in the Host Name
+option (option code 12) or FQDN option (option code 81) that contain undesirable
+characters. It is possible to configure :iscman:`kea-dhcp4` to sanitize these
+values. The most typical use case is ensuring that only characters that
+are permitted by RFC 1035 be included: A-Z, a-z, 0-9, and "-". This may be
+accomplished with the following two parameters:
+
+- ``hostname-char-set`` - a regular expression describing the invalid
+ character set. This can be any valid, regular expression using POSIX
+ extended expression syntax. Embedded nulls (0x00) are always
+ considered an invalid character to be replaced (or omitted).
+ The default is ``"[^A-Za-z0-9.-]"``. This matches any character that is not
+ a letter, digit, dot, hyphen, or null.
+
+- ``hostname-char-replacement`` - a string of zero or more characters
+ with which to replace each invalid character in the host name. An empty
+ string causes invalid characters to be OMITTED rather than replaced.
+ The default is ``""``.
+
+The following configuration replaces anything other than a letter,
+digit, dot, or hyphen with the letter "x":
+::
+
+ "Dhcp4": {
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+ "hostname-char-replacement": "x",
+ ...
+ }
+
+Thus, a client-supplied value of "myhost-$[123.org" would become
+"myhost-xx123.org". Sanitizing is performed only on the portion of the
+name supplied by the client, and it is performed before applying a
+qualifying suffix (if one is defined and needed).
+
+.. note::
+
+ Name sanitizing is meant to catch the more common cases of invalid
+ characters through a relatively simple character-replacement scheme.
+ It is difficult to devise a scheme that works well in all cases, for
+ both Host Name and FQDN options.
+ Administrators who find they have clients with odd corner cases of
+ character combinations that cannot be readily handled with this
+ mechanism should consider writing a hook that can carry out
+ sufficiently complex logic to address their needs.
+
+ If clients include domain names in the Host Name option and the administrator
+ wants these preserved, they need to make sure that the dot, ".",
+ is considered a valid character by the ``hostname-char-set`` expression,
+ such as this: ``"[^A-Za-z0-9.-]"``. This does not affect dots in FQDN
+ Option values.
+ When scrubbing FQDNs, dots are treated as delimiters and used to separate
+ the option value into individual domain labels that are scrubbed and
+ then re-assembled.
+
+ If clients are sending values that differ only by characters
+ considered as invalid by the ``hostname-char-set``, be aware that
+ scrubbing them will yield identical values. In such cases, DDNS
+ conflict rules will permit only one of them to register the name.
+
+ Finally, given the latitude clients have in the values they send, it
+ is virtually impossible to guarantee that a combination of these two
+ parameters will always yield a name that is valid for use in DNS. For
+ example, using an empty value for ``hostname-char-replacement`` could
+ yield an empty domain label within a name, if that label consists
+ only of invalid characters.
+
+.. note::
+
+ It is possible to specify ``hostname-char-set``
+ and/or ``hostname-char-replacement`` at the global scope. This allows
+ host names to be sanitized without requiring a ``dhcp-ddns`` entry. When
+ a ``hostname-char`` parameter is defined at both the global scope and
+ in a ``dhcp-ddns`` entry, the second (local) value is used.
+
+ For the ability to generate host names procedurally, based on an expression, and
+ for the ability to skip DDNS updates on a per-client basis, or fine-tuning various
+ DNS update aspects, the :iscman:`kea-dhcp4` can load the premium hook library
+ `libdhcp_ddns_tuning.so` which is available from ISC. Please refer to
+ :ref:`hooks-ddns-tuning` documentation for the configuration options.
+
+.. _dhcp4-next-server:
+
+Next Server (``siaddr``)
+------------------------
+
+In some cases, clients want to obtain configuration from a TFTP server.
+Although there is a dedicated option for it, some devices may use the
+``siaddr`` field in the DHCPv4 packet for that purpose. That specific field
+can be configured using the ``next-server`` directive. It is possible to
+define it in the global scope or for a given subnet only. If both are
+defined, the subnet value takes precedence. The value in the subnet can be
+set to "0.0.0.0", which means that ``next-server`` should not be sent. It
+can also be set to an empty string, which is equivalent to it
+not being defined at all; that is, it uses the global value.
+
+The ``server-hostname`` (which conveys a server hostname, can be up to
+64 bytes long, and is in the ``sname`` field) and
+``boot-file-name`` (which conveys the configuration file, can be up to
+128 bytes long, and is sent using the ``file`` field) directives are
+handled the same way as ``next-server``.
+
+::
+
+ "Dhcp4": {
+ "next-server": "192.0.2.123",
+ "boot-file-name": "/dev/null",
+ "subnet4": [
+ {
+ "next-server": "192.0.2.234",
+ "server-hostname": "some-name.example.org",
+ "boot-file-name": "bootfile.efi",
+ ...
+ }
+ ],
+ ...
+ }
+
+.. _dhcp4-echo-client-id:
+
+Echoing Client-ID (RFC 6842)
+----------------------------
+
+The original DHCPv4 specification (`RFC
+2131 <https://tools.ietf.org/html/rfc2131>`__) states that the DHCPv4
+server must not send back client-id options when responding to clients.
+However, in some cases that results in confused clients that do not have a MAC
+address or client-id; see `RFC
+6842 <https://tools.ietf.org/html/rfc6842>`__ for details. That behavior
+changed with the publication of `RFC
+6842 <https://tools.ietf.org/html/rfc6842>`__, which updated `RFC
+2131 <https://tools.ietf.org/html/rfc2131>`__. That update states that
+the server must send the client-id if the client sent it, and that is Kea's
+default behavior. However, in some cases older devices that do not
+support `RFC 6842 <https://tools.ietf.org/html/rfc6842>`__ may refuse to
+accept responses that include the client-id option. To enable backward
+compatibility, an optional configuration parameter has been introduced.
+To configure it, use the following configuration statement:
+
+::
+
+ "Dhcp4": {
+ "echo-client-id": false,
+ ...
+ }
+
+.. _dhcp4-match-client-id:
+
+Using Client Identifier and Hardware Address
+--------------------------------------------
+
+The DHCP server must be able to identify the client from which it
+receives the message and distinguish it from other clients. There are
+many reasons why this identification is required; the most important
+ones are:
+
+- When the client contacts the server to allocate a new lease, the
+ server must store the client identification information in the lease
+ database as a search key.
+
+- When the client tries to renew or release the existing lease, the
+ server must be able to find the existing lease entry in the database
+ for this client, using the client identification information as a
+ search key.
+
+- Some configurations use static reservations for the IP addresses and
+ other configuration information. The server's administrator uses
+ client identification information to create these static assignments.
+
+- In dual-stack networks there is often a need to correlate the lease
+ information stored in DHCPv4 and DHCPv6 servers for a particular
+ host. Using common identification information by the DHCPv4 and
+ DHCPv6 clients allows the network administrator to achieve this
+ correlation and better administer the network. Beginning with
+ release 2.1.2, Kea supports DHCPv6 DUIDs embedded within DHCPv4
+ Client Identifier options as described in
+ `RFC 4361 <https://tools.ietf.org/html/rfc4361>`__.
+
+DHCPv4 uses two distinct identifiers which are placed by the client in
+the queries sent to the server and copied by the server to its responses
+to the client: ``chaddr`` and ``client-identifier``. The former was
+introduced as a part of the BOOTP specification and it is also used by
+DHCP to carry the hardware address of the interface used to send the
+query to the server (MAC address for the Ethernet). The latter is
+carried in the client-identifier option, introduced in `RFC
+2132 <https://tools.ietf.org/html/rfc2132>`__.
+
+`RFC 2131 <https://tools.ietf.org/html/rfc2131>`__ indicates that the
+server may use both of these identifiers to identify the client but the
+client identifier, if present, takes precedence over ``chaddr``. One of
+the reasons for this is that the client identifier is independent from the
+hardware used by the client to communicate with the server. For example,
+if the client obtained the lease using one network card and then the
+network card is moved to another host, the server will wrongly identify
+this host as the one which obtained the lease. Moreover, `RFC
+4361 <https://tools.ietf.org/html/rfc4361>`__ gives the recommendation
+to use a DUID (see `RFC 8415 <https://tools.ietf.org/html/rfc8415>`__,
+the DHCPv6 specification) carried as a client identifier when dual-stack
+networks are in use to provide consistent identification information for
+the client, regardless of the type of protocol it is using. Kea adheres to
+these specifications, and the client identifier by default takes
+precedence over the value carried in the ``chaddr`` field when the server
+searches, creates, updates, or removes the client's lease.
+
+When the server receives a DHCPDISCOVER or DHCPREQUEST message from the
+client, it tries to find out if the client already has a lease in the
+database; if it does, the server hands out that lease rather than allocates a new one.
+Each lease in the lease database is associated with the client
+identifier and/or ``chaddr``. The server first uses the client
+identifier (if present) to search for the lease; if one is found, the
+server treats this lease as belonging to the client, even if the
+current ``chaddr`` and the ``chaddr`` associated with the lease do not
+match. This facilitates the scenario when the network card on the client
+system has been replaced and thus the new MAC address appears in the
+messages sent by the DHCP client. If the server fails to find the lease
+using the client identifier, it performs another lookup using the
+``chaddr``. If this lookup returns no result, the client is considered to
+not have a lease and a new lease is created.
+
+A common problem reported by network operators is that poor client
+implementations do not use stable client identifiers, instead generating
+a new client identifier each time the client connects to the network.
+Another well-known case is when the client changes its client
+identifier during the multi-stage boot process (PXE). In such cases,
+the MAC address of the client's interface remains stable, and using the
+``chaddr`` field to identify the client guarantees that the particular
+system is considered to be the same client, even though its client
+identifier changes.
+
+To address this problem, Kea includes a configuration option which
+enables client identification using ``chaddr`` only. This instructs the
+server to ignore the client identifier during lease lookups and allocations
+for a particular subnet. Consider the following simplified server configuration:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "match-client-id": true,
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.10.0/24",
+ "pools": [ { "pool": "192.0.2.23-192.0.2.87" } ],
+ "match-client-id": false
+ },
+ {
+ "id": 1,
+ "subnet": "10.0.0.0/8",
+ "pools": [ { "pool": "10.0.0.23-10.0.2.99" } ]
+ }
+ ]
+ }
+ }
+
+The ``match-client-id`` parameter is a boolean value which controls this
+behavior. The default value of ``true`` indicates that the server will use
+the client identifier for lease lookups and ``chaddr`` if the first lookup
+returns no results. ``false`` means that the server will only use
+the ``chaddr`` to search for the client's lease. Whether the DHCID for DNS
+updates is generated from the client identifier or ``chaddr`` is
+controlled through the same parameter.
+
+The ``match-client-id`` parameter may appear both in the global
+configuration scope and/or under any subnet declaration. In the example
+shown above, the effective value of the ``match-client-id`` will be
+``false`` for the subnet 192.0.10.0/24, because the subnet-specific
+setting of the parameter overrides the global value of the parameter.
+The effective value of the ``match-client-id`` for the subnet 10.0.0.0/8
+will be set to ``true``, because the subnet declaration lacks this
+parameter and the global setting is by default used for this subnet. In
+fact, the global entry for this parameter could be omitted in this case,
+because ``true`` is the default value.
+
+It is important to understand what happens when the client obtains its
+lease for one setting of the ``match-client-id`` and then renews it when
+the setting has been changed. First, consider the case when the client
+obtains the lease and the ``match-client-id`` is set to ``true``. The
+server stores the lease information, including the client identifier
+(if supplied) and ``chaddr``, in the lease database. When the setting is
+changed and the client renews the lease, the server will determine that
+it should use the ``chaddr`` to search for the existing lease. If the
+client has not changed its MAC address, the server should successfully
+find the existing lease. The client identifier associated with the
+returned lease will be ignored and the client will be allowed to use this lease.
+When the lease is renewed only the ``chaddr`` will be recorded for this lease,
+according to the new server setting.
+
+In the second case, the client has the lease with only a ``chaddr`` value
+recorded. When the ``match-client-id`` setting is changed to ``true``,
+the server will first try to use the client identifier to find the
+existing client's lease. This will return no results because the client
+identifier was not recorded for this lease. The server will then use
+the ``chaddr`` and the lease will be found. If the lease appears to have
+no client identifier recorded, the server will assume that this lease
+belongs to the client and that it was created with the previous setting
+of the ``match-client-id``. However, if the lease contains a client
+identifier which is different from the client identifier used by the
+client, the lease will be assumed to belong to another client and a
+new lease will be allocated.
+
+For a more visual representation of how Kea recognizes the same client,
+check :ref:`uml-recognizing-same-client`.
+
+.. _dhcp4-authoritative:
+
+Authoritative DHCPv4 Server Behavior
+------------------------------------
+
+The original DHCPv4 specification (`RFC
+2131 <https://tools.ietf.org/html/rfc2131>`__) states that if a client
+requests an address in the INIT-REBOOT state of which the server has no
+knowledge, the server must remain silent, except if the server knows
+that the client has requested an IP address from the wrong network. By
+default, Kea follows the behavior of the ISC ``dhcpd`` daemon instead of the
+specification and also remains silent if the client requests an IP
+address from the wrong network, because configuration information about
+a given network segment is not known to be correct. Kea only rejects a
+client's DHCPREQUEST with a DHCPNAK message if it already has a lease
+for the client with a different IP address. Administrators can
+override this behavior through the boolean ``authoritative`` (``false``
+by default) setting.
+
+In authoritative mode, ``authoritative`` set to ``true``, Kea always
+rejects INIT-REBOOT requests from unknown clients with DHCPNAK messages.
+The ``authoritative`` setting can be specified in global,
+shared-network, and subnet configuration scope and is automatically
+inherited from the parent scope, if not specified. All subnets in a
+shared-network must have the same ``authoritative`` setting.
+
+.. _dhcp4-dhcp4o6-config:
+
+DHCPv4-over-DHCPv6: DHCPv4 Side
+-------------------------------
+
+The support of DHCPv4-over-DHCPv6 transport is described in `RFC
+7341 <https://tools.ietf.org/html/rfc7341>`__ and is implemented using
+cooperating DHCPv4 and DHCPv6 servers. This section is about the
+configuration of the DHCPv4 side (the DHCPv6 side is described in
+:ref:`dhcp6-dhcp4o6-config`).
+
+.. note::
+
+ DHCPv4-over-DHCPv6 support is experimental and the details of the
+ inter-process communication may change; for instance, the
+ support of port relay (RFC 8357) introduced an incompatible change.
+ Both the DHCPv4 and DHCPv6 sides should be running the same version of Kea.
+
+The ``dhcp4o6-port`` global parameter specifies the first of the two
+consecutive ports of the UDP sockets used for the communication between
+the DHCPv6 and DHCPv4 servers. The DHCPv4 server is bound to ::1 on
+``port`` + 1 and connected to ::1 on ``port``.
+
+With DHCPv4-over-DHCPv6, the DHCPv4 server does not have access to
+several of the identifiers it would normally use to select a subnet. To
+address this issue, three new configuration entries are available; the
+presence of any of these allows the subnet to be used with
+DHCPv4-over-DHCPv6. These entries are:
+
+- ``4o6-subnet``: takes a prefix (i.e., an IPv6 address followed by a
+ slash and a prefix length) which is matched against the source
+ address.
+
+- ``4o6-interface-id``: takes a relay interface ID option value.
+
+- ``4o6-interface``: takes an interface name which is matched against
+ the incoming interface name.
+
+ISC tested the following configuration:
+
+::
+
+ {
+
+ # DHCPv4 conf
+ "Dhcp4": {
+
+ "interfaces-config": {
+ "interfaces": [ "eno33554984" ]
+ },
+
+ "lease-database": {
+ "type": "memfile",
+ "name": "leases4"
+ },
+
+ "valid-lifetime": 4000,
+
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.10.10.0/24",
+ "4o6-interface": "eno33554984",
+ "4o6-subnet": "2001:db8:1:1::/64",
+ "pools": [ { "pool": "10.10.10.100 - 10.10.10.199" } ]
+ }
+ ],
+
+ "dhcp4o6-port": 6767,
+
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "/tmp/kea-dhcp4.log"
+ }
+ ],
+ "severity": "DEBUG",
+ "debuglevel": 0
+ }
+ ]
+ }
+
+ }
+
+.. _sanity-checks4:
+
+Sanity Checks in DHCPv4
+-----------------------
+
+An important aspect of a well-running DHCP system is an assurance that
+the data remains consistent; however, in some cases it may be convenient
+to tolerate certain inconsistent data. For example, a network
+administrator who temporarily removes a subnet from a configuration
+would not want all the leases associated with it to disappear from the
+lease database. Kea has a mechanism to implement sanity checks for situations
+like this.
+
+Kea supports a configuration scope called ``sanity-checks``.
+A parameter, called ``lease-checks``,
+governs the verification carried out when a new lease is loaded from a
+lease file. This mechanism permits Kea to attempt to correct inconsistent data.
+
+Every subnet has a ``subnet-id`` value; this is how Kea internally
+identifies subnets. Each lease has a ``subnet-id`` parameter as well, which
+identifies the subnet it belongs to. However, if the configuration has
+changed, it is possible that a lease could exist with a ``subnet-id`` but
+without any subnet that matches it. Also, it is possible that the
+subnet's configuration has changed and the ``subnet-id`` now belongs to a
+subnet that does not match the lease.
+
+Kea's corrective algorithm first
+checks to see if there is a subnet with the ``subnet-id`` specified by the
+lease. If there is, it verifies whether the lease belongs to that
+subnet. If not, depending on the ``lease-checks`` setting, the lease is
+discarded, a warning is displayed, or a new subnet is selected for the
+lease that matches it topologically.
+
+There are five levels which are supported:
+
+- ``none`` - do no special checks; accept the lease as is.
+
+- ``warn`` - if problems are detected display a warning, but
+ accept the lease data anyway. This is the default value.
+
+- ``fix`` - if a data inconsistency is discovered, try to
+ correct it. If the correction is not successful, insert the incorrect data
+ anyway.
+
+- ``fix-del`` - if a data inconsistency is discovered, try to
+ correct it. If the correction is not successful, reject the lease.
+ This setting ensures the data's correctness, but some
+ incorrect data may be lost. Use with care.
+
+- ``del`` - if any inconsistency is
+ detected, reject the lease. This is the strictest mode; use with care.
+
+This feature is currently implemented for the memfile backend. The
+sanity check applies to the lease database in memory, not to the lease file,
+i.e. inconsistent leases will stay in the lease file.
+
+An example configuration that sets this parameter looks as follows:
+
+::
+
+ "Dhcp4": {
+ "sanity-checks": {
+ "lease-checks": "fix-del"
+ },
+ ...
+ }
+
+.. _dhcp4-store-extended-info:
+
+Storing Extended Lease Information
+----------------------------------
+
+To support such features as DHCP Leasequery
+(`RFC 4388 <https://tools.ietf.org/html/rfc4388>`__),
+additional information must be stored with each lease. Because the amount
+of information for each lease has ramifications in terms of
+performance and system resource consumption, storage of this additional
+information is configurable through the ``store-extended-info`` parameter.
+It defaults to ``false`` and may be set at the global, shared-network, and
+subnet levels.
+
+::
+
+ "Dhcp4": {
+ "store-extended-info": true,
+ ...
+ }
+
+When set to ``true``, information relevant to the DHCPREQUEST asking for the lease is
+added into the lease's user-context as a map element labeled "ISC". Since
+Kea version 2.3.2, when the DHCPREQUEST received contains the option
+(DHCP Option 82), the map contains the ``relay-agent-info`` map
+with the content option (DHCP Option 82) in the ``sub-options`` entry and,
+when present, the ``remote-id`` and ``relay-id`` options.
+Since DHCPREQUESTs sent as renewals are not likely to contain this
+information, the values taken from the last DHCPREQUEST that did contain it are
+retained on the lease. The lease's user-context looks something like this:
+
+::
+
+ { "ISC": { "relay-agent-info": { "sub-options": "0x0104AABBCCDD" } } }
+
+Or with remote and relay sub-options:
+
+::
+
+ {
+ "ISC": {
+ "relay-agent-info": {
+ "sub-options": "0x02030102030C03AABBCC",
+ "remote-id": "03010203",
+ "relay-id": "AABBCC"
+ }
+ }
+ }
+
+.. note::
+
+ It is possible that other hook libraries are already using ``user-context``.
+ Enabling ``store-extended-info`` should not interfere with any other ``user-context``
+ content, as long as it does not also use an element labeled "ISC". In other
+ words, ``user-context`` is intended to be a flexible container serving multiple
+ purposes. As long as no other purpose also writes an "ISC" element to
+ ``user-context`` there should not be a conflict.
+
+Extended lease information is also subject to configurable sanity checking.
+The parameter in the ``sanity-checks`` scope is named ``extended-info-checks``
+and supports these levels:
+
+- ``none`` - do no check nor upgrade. This level should be used only when
+ extended info is not used at all or when no badly formatted extended
+ info, including using the old format, is expected.
+
+- ``fix`` - fix some common inconsistencies and upgrade extended info
+ using the old format to the new one. It is the default level and is
+ convenient when the Leasequery hook library is not loaded.
+
+- ``strict`` - fix all inconsistencies which have an impact on the (Bulk)
+ Leasequery hook library.
+
+- ``pedantic`` - enforce full conformance to the format produced by the
+ Kea code; for instance, no extra entries are allowed with the exception
+ of ``comment``.
+
+.. note::
+
+ This feature is currently implemented only for the memfile
+ backend. The sanity check applies to the lease database in memory,
+ not to the lease file, i.e. inconsistent leases stay in the lease
+ file.
+
+.. _dhcp4-multi-threading-settings:
+
+Multi-Threading Settings
+------------------------
+
+The Kea server can be configured to process packets in parallel using multiple
+threads. These settings can be found under the ``multi-threading`` structure and are
+represented by:
+
+- ``enable-multi-threading`` - use multiple threads to process packets in
+ parallel. The default is ``true``.
+
+- ``thread-pool-size`` - specify the number of threads to process packets in
+ parallel. It may be set to ``0`` (auto-detect), or any positive number that
+ explicitly sets the thread count. The default is ``0``.
+
+- ``packet-queue-size`` - specify the size of the queue used by the thread
+ pool to process packets. It may be set to ``0`` (unlimited), or any positive
+ number that explicitly sets the queue size. The default is ``64``.
+
+An example configuration that sets these parameters looks as follows:
+
+::
+
+ "Dhcp4": {
+ "multi-threading": {
+ "enable-multi-threading": true,
+ "thread-pool-size": 4,
+ "packet-queue-size": 16
+ },
+ ...
+ }
+
+Multi-Threading Settings With Different Database Backends
+---------------------------------------------------------
+
+The Kea DHCPv4 server is benchmarked by ISC to determine which settings
+give the best performance. Although this section describes our results, they are merely
+recommendations and are very dependent on the particular hardware used
+for benchmarking. We strongly advise that administrators run their own performance benchmarks.
+
+A full report of performance results for the latest stable Kea version can be found
+`here <https://reports.kea.isc.org/>`_.
+This includes hardware and benchmark scenario descriptions, as well as
+current results.
+
+After enabling multi-threading, the number of threads is set by the ``thread-pool-size``
+parameter. Results from our experiments show that the best settings for
+:iscman:`kea-dhcp4` are:
+
+- ``thread-pool-size``: 4 when using ``memfile`` for storing leases.
+
+- ``thread-pool-size``: 12 or more when using ``mysql`` for storing leases.
+
+- ``thread-pool-size``: 8 when using ``postgresql``.
+
+Another very important parameter is ``packet-queue-size``; in our benchmarks we
+used it as a multiplier of ``thread-pool-size``. The actual setting strongly depends
+on ``thread-pool-size``.
+
+We saw the best results in our benchmarks with the following settings:
+
+- ``packet-queue-size``: 7 * ``thread-pool-size`` when using ``memfile`` for
+ storing leases; in our case it was 7 * 4 = 28. This means that at any given
+ time, up to 28 packets could be queued.
+
+- ``packet-queue-size``: 66 * ``thread-pool-size`` when using ``mysql`` for
+ storing leases; in our case it was 66 * 12 = 792. This means that up to
+ 792 packets could be queued.
+
+- ``packet-queue-size``: 11 * ``thread-pool-size`` when using ``postgresql`` for
+ storing leases; in our case it was 11 * 8 = 88.
+
+IPv6-Only Preferred Networks
+----------------------------
+
+`RFC8925 <https://tools.ietf.org/html/rfc8925>`_, recently published by the IETF,
+specifies a DHCPv4 option to indicate that a host supports an IPv6-only mode and is willing to
+forgo obtaining an IPv4 address if the network provides IPv6 connectivity. The general idea is that
+a network administrator can enable this option to signal to compatible dual-stack devices that
+IPv6 connectivity is available and they can shut down their IPv4 stack. The new option
+``v6-only-preferred`` content is a 32-bit unsigned integer and specifies for how long the device
+should disable its stack. The value is expressed in seconds.
+
+The RFC mentions the ``V6ONLY_WAIT`` timer. This is implemented in Kea by setting the value of
+the ``v6-only-preferred`` option. This follows the usual practice of setting options; the
+option value can be specified on the pool, subnet, shared network, or global levels, or even
+via host reservations.
+
+There is no special processing involved; it follows the standard Kea option processing
+regime. The option is not sent back unless the client explicitly requests it. For example, to
+enable the option for the whole subnet, the following configuration can be used:
+
+::
+
+ {
+ "subnet4": [
+ {
+ "id": 1,
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "subnet": "192.0.2.0/24",
+ "option-data": [
+ {
+ // This will make the v6-only capable devices to disable their
+ // v4 stack for half an hour and then try again
+ "name": "v6-only-preferred",
+ "data": "1800"
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+Lease Caching
+-------------
+
+Clients that attempt multiple renewals in a short period can cause the server to update
+and write to the database frequently, resulting in a performance impact
+on the server. The cache parameters instruct the DHCP server to avoid
+updating leases too frequently, thus avoiding this behavior. Instead,
+the server assigns the same lease (i.e. reuses it) with no
+modifications except for CLTT (Client Last Transmission Time), which
+does not require disk operations.
+
+The two parameters are the ``cache-threshold`` double and the
+``cache-max-age`` integer; they have no default setting, i.e. the lease caching
+feature must be explicitly enabled. These parameters can be configured
+at the global, shared-network, and subnet levels. The subnet level has
+precedence over the shared-network level, while the global level is used
+as a last resort. For example:
+
+::
+
+ {
+ "subnet4": [
+ {
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "subnet": "192.0.2.0/24",
+ "cache-threshold": .25,
+ "cache-max-age": 600,
+ "valid-lifetime": 2000,
+ ...
+ }
+ ],
+ ...
+ }
+
+When an already-assigned lease can fulfill a client query:
+
+ - any important change, e.g. for DDNS parameter, hostname, or
+ valid lifetime reduction, makes the lease not reusable.
+
+ - lease age, i.e. the difference between the creation or last modification
+ time and the current time, is computed (elapsed duration).
+
+ - if ``cache-max-age`` is explicitly configured, it is compared with the lease age;
+ leases that are too old are not reusable. This means that the value 0
+ for ``cache-max-age`` disables the lease cache feature.
+
+ - if ``cache-threshold`` is explicitly configured and is between 0.0 and 1.0,
+ it expresses the percentage of the lease valid lifetime which is
+ allowed for the lease age. Values below and including 0.0 and
+ values greater than 1.0 disable the lease cache feature.
+
+In our example, a lease with a valid lifetime of 2000 seconds can be
+reused if it was committed less than 500 seconds ago. With a lifetime
+of 3000 seconds, a maximum age of 600 seconds applies.
+
+In outbound client responses (e.g. DHCPACK messages), the
+``dhcp-lease-time`` option is set to the reusable valid lifetime,
+i.e. the expiration date does not change. Other options based on the
+valid lifetime e.g. ``dhcp-renewal-time`` and ``dhcp-rebinding-time``,
+also depend on the reusable lifetime.
+
+Temporary Allocation on DHCPDISCOVER
+------------------------------------
+
+By default, :iscman:`kea-dhcp4` does not allocate or store a lease when offering an address
+to a client in response to a DHCPDISCOVER. In general, :iscman:`kea-dhcp4` can fulfill client
+demands faster by deferring lease allocation and storage until it receives DHCPREQUESTs
+for them. Release 2.3.6 added a new parameter to :iscman:`kea-dhcp4`, ``offer-lifetime``, which
+(when not zero) instructs the server to allocate and persist a lease when generating a
+DHCPOFFER. In addition:
+
+- The persisted lease's lifetime is equal to ``offer-lifetime`` (in seconds).
+
+- The lifetime sent to the client in the DHCPOFFER via option 51 is still based
+ on ``valid-lifetime``. This avoids issues with clients that may reject offers whose
+ lifetimes they perceive as too short.
+
+- DDNS updates are not performed. As with the default behavior, those updates occur on DHCPREQUEST.
+
+- Updates are not sent to HA peers.
+
+- Assigned lease statistics are incremented.
+
+- Expiration processing and reclamation behave just as they do for leases allocated
+ during DHCPREQUEST processing.
+
+- Lease caching, if enabled, is honored.
+
+- In sites running multiple instances of :iscman:`kea-dhcp4` against a single, shared lease store, races
+ for given address values are lost during DHCPDISCOVER processing rather than during DHCPREQUEST
+ processing. Servers that lose the race for the address simply do not respond to the client,
+ rather than NAK them. The client in turn simply retries its DHCPDISCOVER. This should reduce
+ the amount of traffic such conflicts incur.
+
+- Clients repeating DHCPDISCOVERs are offered the same address each time.
+
+An example subnet configuration is shown below:
+
+::
+
+ {
+ "subnet4": [
+ {
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "subnet": "192.0.2.0/24",
+ "offer-lifetime": 60,
+ "valid-lifetime": 2000,
+ ...
+ }
+ ],
+ ...
+ }
+
+Here ``offer-lifetime`` has been configured to be 60 seconds, with a ``valid-lifetime``
+of 2000 seconds. This instructs :iscman:`kea-dhcp4` to persist leases for 60 seconds when
+sending them back in DHCPOFFERs, and then extend them to 2000 seconds when clients
+DHCPREQUEST them.
+
+The value, which defaults to 0, is supported at the global, shared-network, subnet,
+and class levels. Choosing an appropriate value for ``offer-lifetime`` is extremely
+site-dependent, but a value between 60 and 120 seconds is a reasonable starting
+point.
+
+.. _dnr4-options:
+
+DNR (Discovery of Network-designated Resolvers) Options for DHCPv4
+------------------------------------------------------------------
+
+One of the more recently added options is the Discovery of
+Network-designated Resolvers or DNR option,
+introduced in `RFC 9463 <https://tools.ietf.org/html/rfc9463>`__. The goal of that RFC is
+to provide a way to communicate location of DNS resolvers available over means other than
+the classic DNS over UDP over port 53. At the time of this writing, the supported technologies
+are DoT (DNS-over-TLS), DoH (DNS-over-HTTPS), and DoQ (DNS-over-QUIC), but the option was
+designed to be extensible to accommodate other protocols in the future.
+
+The DHCPv4 option and its corresponding DHCPv6 options are almost exactly the same,
+with the exception of cardinality. Only one DHCPv4 option is allowed, while for DHCPv6
+multiple options are allowed. To be able to convey multiple entries, the DHCPv4 is an
+array that allows multiple DNS instances. Each instance is logically equal to one
+DHCPv6 option, except the minor difference of using IPv4 rather than IPv6 addresses.
+
+For detailed example how to configure DNR option, see :ref:`dnr6-options`.
+The only difference for DNR DHCPv4 options configuration is that it allows
+to configure more than one DNR instance and the DNR instances are separated
+with the "pipe" (``0x7C``) character.
+For each DNR Instance comma delimited fields must be provided in the following order:
+
+- Service Priority (mandatory),
+- ADN FQDN (mandatory),
+- IP address(es) (optional - if more than one - they must be space-separated)
+- SvcParams as a set of key=value pairs (optional - if more than one - they must be space-separated;
+ to provide more than one alpn-id separate them with double backslash escaped comma like in the
+ example below).
+
+Example usage:
+
+::
+
+ {
+ "name": "v4-dnr",
+ // 2 DNR Instances:
+ // - Service priority 2, ADN, resolver IPv4 address and Service Parameters
+ // - Service priority 3, ADN - this is ADN-only mode as per RFC9463 3.1.6
+ "data": "2, resolver.example., 10.0.5.6, alpn=dot\\,doq port=8530 | 3, fooexp.resolver.example."
+ }
+
+
+.. note::
+
+ Note that whenever "comma" or "pipe" characters need to be used not as the delimiters, they must be escaped with
+ double backslash (``\\,`` or ``\\|``). E.g. one must use escaped commas when configuring more than one ``ALPN``
+ protocol to separate them. One might want to use "pipe" (``0x7C``) character in ``dohpath`` Service Parameter,
+ as it is allowed in URI. In that case it must be escaped with double backslash.
+
+Examples for DNR DHCPv4 options are provided in the Kea sources in
+`all-options.json` in the `doc/examples/kea4` directory.
+
+
+.. _host-reservation-v4:
+
+Host Reservations in DHCPv4
+===========================
+
+There are many cases where it is useful to provide a configuration on a
+per-host basis. The most obvious one is to reserve a specific, static
+address for exclusive use by a given client (host); the returning client
+receives the same address from the server every time, and other
+clients generally do not receive that address. Host
+reservations are also convenient when a host has
+specific requirements, e.g. a printer that needs additional DHCP
+options. Yet another possible use case is to define unique names for
+hosts.
+
+There may be cases when a new reservation has been made for a
+client for an address currently in use by another client. We call this
+situation a "conflict."
+These conflicts get resolved automatically over time, as described in
+subsequent sections. Once a conflict is resolved, the correct client will
+receive the reserved configuration when it renews.
+
+Host reservations are defined as parameters for each subnet. Each host
+must have its own unique identifier, such as the hardware/MAC
+address. There is an optional ``reservations`` array in the ``subnet4``
+structure; each element in that array is a structure that holds
+information about reservations for a single host. In particular, the
+structure has an identifier that uniquely identifies a host. In
+the DHCPv4 context, the identifier is usually a hardware or MAC address.
+In most cases an IP address will be specified. It is also possible to
+specify a hostname, host-specific options, or fields carried within the
+DHCPv4 message such as ``siaddr``, ``sname``, or ``file``.
+
+.. note::
+
+ The reserved address must be within the subnet.
+
+The following example shows how to reserve addresses for specific hosts
+in a subnet:
+
+::
+
+ {
+ "subnet4": [
+ {
+ "id": 1,
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "subnet": "192.0.2.0/24",
+ "interface": "eth0",
+ "reservations": [
+ {
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "ip-address": "192.0.2.202"
+ },
+ {
+ "duid": "0a:0b:0c:0d:0e:0f",
+ "ip-address": "192.0.2.100",
+ "hostname": "alice-laptop"
+ },
+ {
+ "circuit-id": "'charter950'",
+ "ip-address": "192.0.2.203"
+ },
+ {
+ "client-id": "01:11:22:33:44:55:66",
+ "ip-address": "192.0.2.204"
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+The first entry reserves the 192.0.2.202 address for the client that
+uses a MAC address of 1a:1b:1c:1d:1e:1f. The second entry reserves the
+address 192.0.2.100 and the hostname of "alice-laptop" for the client
+using a DUID 0a:0b:0c:0d:0e:0f. (If DNS updates are planned,
+it is strongly recommended that the hostnames be unique.) The
+third example reserves address 192.0.3.203 for a client whose request
+would be relayed by a relay agent that inserts a ``circuit-id`` option with
+the value "charter950". The fourth entry reserves address 192.0.2.204
+for a client that uses a client identifier with value
+01:11:22:33:44:55:66.
+
+The above example is used for illustrational purposes only; in actual
+deployments it is recommended to use as few types as possible
+(preferably just one). See :ref:`reservations4-tuning` for a detailed discussion of this
+point.
+
+Making a reservation for a mobile host that may visit multiple subnets
+requires a separate host definition in each subnet that host is expected to
+visit. It is not possible to define multiple host definitions with the
+same hardware address in a single subnet. Multiple host definitions with
+the same hardware address are valid if each is in a different subnet.
+
+Adding host reservations incurs a performance penalty. In principle, when
+a server that does not support host reservation responds to a query, it
+needs to check whether there is a lease for a given address being
+considered for allocation or renewal. The server that does support host
+reservation has to perform additional checks: not only whether the
+address is currently used (i.e., if there is a lease for it), but also
+whether the address could be used by someone else (i.e., if there is a
+reservation for it). That additional check incurs extra overhead.
+
+.. _reservation4-types:
+
+Address Reservation Types
+-------------------------
+
+In a typical Kea scenario there is an IPv4 subnet defined, e.g.
+192.0.2.0/24, with a certain part of it dedicated for dynamic allocation
+by the DHCPv4 server. That dynamic part is referred to as a dynamic pool
+or simply a pool. In principle, a host reservation can reserve any
+address that belongs to the subnet. The reservations that specify
+addresses that belong to configured pools are called "in-pool
+reservations." In contrast, those that do not belong to dynamic pools
+are called "out-of-pool reservations." There is no formal difference in
+the reservation syntax and both reservation types are handled uniformly.
+
+Kea supports global host reservations. These are reservations that are
+specified at the global level within the configuration and that do not
+belong to any specific subnet. Kea still matches inbound client
+packets to a subnet as before, but when the subnet's reservation mode is
+set to "global", Kea looks for host reservations only among the
+global reservations defined. Typically, such reservations would be used
+to reserve hostnames for clients which may move from one subnet to
+another.
+
+.. note::
+
+ Global reservations, while useful in certain circumstances, have aspects
+ that must be given due consideration when using them. Please see
+ :ref:`reservation4-conflict` for more details.
+
+.. note::
+
+ Since Kea 1.9.1, reservation mode has been replaced by three
+ boolean flags, ``reservations-global``, ``reservations-in-subnet``,
+ and ``reservations-out-of-pool``, which allow the configuration of
+ host reservations both globally and in a subnet. In such cases a subnet
+ host reservation has preference over a global reservation
+ when both exist for the same client.
+
+.. _reservation4-conflict:
+
+Conflicts in DHCPv4 Reservations
+--------------------------------
+
+As reservations and lease information are stored separately, conflicts
+may arise. Consider the following series of events: the server has
+configured the dynamic pool of addresses from the range of 192.0.2.10 to
+192.0.2.20. Host A requests an address and gets 192.0.2.10. Now the
+system administrator decides to reserve address 192.0.2.10 for Host B.
+In general, reserving an address that is currently assigned to someone
+else is not recommended, but there are valid use cases where such an
+operation is warranted.
+
+The server now has a conflict to resolve. If Host B boots up and
+requests an address, the server cannot immediately assign the reserved
+address 192.0.2.10. A naive approach would to be immediately remove the
+existing lease for Host A and create a new one for Host B. That would
+not solve the problem, though, because as soon as Host B gets the
+address, it will detect that the address is already in use (by Host A) and
+will send a DHCPDECLINE message. Therefore, in this situation, the
+server has to temporarily assign a different address from the dynamic
+pool (not matching what has been reserved) to Host B.
+
+When Host A renews its address, the server will discover that the
+address being renewed is now reserved for another host - Host B.
+The server will inform Host A that it is no longer allowed to
+use it by sending a DHCPNAK message. The server will not remove the
+lease, though, as there's a small chance that the DHCPNAK will not be delivered if
+the network is lossy. If that happens, the client will not receive any
+responses, so it will retransmit its DHCPREQUEST packet. Once the
+DHCPNAK is received by Host A, it will revert to server discovery and
+will eventually get a different address. Besides allocating a new lease,
+the server will also remove the old one. As a result, address 192.0.2.10
+will become free.
+
+When Host B tries to renew its temporarily assigned
+address, the server will detect that it has a valid lease, but will note
+that there is a reservation for a different address. The server will
+send DHCPNAK to inform Host B that its address is no longer usable, but
+will keep its lease (again, the DHCPNAK may be lost, so the server will
+keep it until the client returns for a new address). Host B will revert
+to the server discovery phase and will eventually send a DHCPREQUEST
+message. This time the server will find that there is a reservation for
+that host and that the reserved address 192.0.2.10 is not used, so it
+will be granted. It will also remove the lease for the temporarily
+assigned address that Host B previously obtained.
+
+This recovery will succeed, even if other hosts attempt to get the
+reserved address. If Host C requests the address 192.0.2.10 after the
+reservation is made, the server will either offer a different address
+(when responding to DHCPDISCOVER) or send DHCPNAK (when responding to
+DHCPREQUEST).
+
+This mechanism allows the server to fully recover from a case
+where reservations conflict with existing leases; however, this procedure
+takes roughly as long as the value set for ``renew-timer``. The
+best way to avoid such a recovery is not to define new reservations that
+conflict with existing leases. Another recommendation is to use
+out-of-pool reservations; if the reserved address does not belong to a
+pool, there is no way that other clients can get it.
+
+.. note::
+
+ The conflict-resolution mechanism does not work for global
+ reservations. Although the global address reservations feature may be useful
+ in certain settings, it is generally recommended not to use
+ global reservations for addresses. Administrators who do choose
+ to use global reservations must manually ensure that the reserved
+ addresses are not in dynamic pools.
+
+.. _reservation4-hostname:
+
+Reserving a Hostname
+--------------------
+
+When the reservation for a client includes the ``hostname``, the server
+returns this hostname to the client in the Client FQDN or Hostname
+option. The server responds with the Client FQDN option only if the
+client has included the Client FQDN option in its message to the server. The
+server responds with the Hostname option if the client included
+the Hostname option in its message to the server, or if the client
+requested the Hostname option using the Parameter Request List option.
+The server returns the Hostname option even if it is not configured
+to perform DNS updates. The reserved hostname always takes precedence
+over the hostname supplied by the client or the autogenerated (from the
+IPv4 address) hostname.
+
+The server qualifies the reserved hostname with the value of the
+``ddns-qualifying-suffix`` parameter. For example, the following subnet
+configuration:
+
+.. code-block:: json
+
+ {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.0.0.0/24",
+ "pools": [ { "pool": "10.0.0.10-10.0.0.100" } ],
+ "ddns-qualifying-suffix": "example.isc.org.",
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:ff",
+ "hostname": "alice-laptop"
+ }
+ ]
+ }
+ ],
+ "dhcp-ddns": {
+ "enable-updates": true
+ }
+ }
+
+will result in the "alice-laptop.example.isc.org." hostname being assigned to
+the client using the MAC address "aa:bb:cc:dd:ee:ff". If the
+``ddns-qualifying-suffix`` is not specified, the default (empty) value will
+be used, and in this case the value specified as a ``hostname`` will be
+treated as a fully qualified name. Thus, by leaving the
+``ddns-qualifying-suffix`` empty it is possible to qualify hostnames for
+different clients with different domain names:
+
+.. code-block:: json
+
+ {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.0.0.0/24",
+ "pools": [ { "pool": "10.0.0.10-10.0.0.100" } ],
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:ff",
+ "hostname": "alice-laptop.isc.org."
+ },
+ {
+ "hw-address": "12:34:56:78:99:AA",
+ "hostname": "mark-desktop.example.org."
+ }
+ ]
+ }
+ ],
+ "dhcp-ddns": {
+ "enable-updates": true
+ }
+ }
+
+The above example results in the assignment of the
+"alice-laptop.isc.org." hostname to the client using the MAC
+address "aa:bb:cc:dd:ee:ff", and the hostname "mark-desktop.example.org."
+to the client using the MAC address "12:34:56:78:99:AA".
+
+.. _reservation4-options:
+
+Including Specific DHCPv4 Options in Reservations
+-------------------------------------------------
+
+Kea offers the ability to specify options on a per-host basis. These
+options follow the same rules as any other options. These can be
+standard options (see :ref:`dhcp4-std-options`),
+custom options (see :ref:`dhcp4-custom-options`),
+or vendor-specific options (see :ref:`dhcp4-vendor-opts`). The following
+example demonstrates how standard options can be defined:
+
+::
+
+ {
+ "subnet4": [
+ {
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:ff",
+ "ip-address": "192.0.2.1",
+ "option-data": [
+ {
+ "name": "cookie-servers",
+ "data": "10.1.1.202,10.1.1.203"
+ },
+ {
+ "name": "log-servers",
+ "data": "10.1.1.200,10.1.1.201"
+ }
+ ]
+ }
+ ],
+ ...
+ }
+ ],
+ ...
+ }
+
+Vendor-specific options can be reserved in a similar manner:
+
+::
+
+ {
+ "subnet4": [
+ {
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:ff",
+ "ip-address": "10.0.0.7",
+ "option-data": [
+ {
+ "name": "vivso-suboptions",
+ "data": "4491"
+ },
+ {
+ "name": "tftp-servers",
+ "space": "vendor-4491",
+ "data": "10.1.1.202,10.1.1.203"
+ }
+ ]
+ }
+ ],
+ ...
+ }
+ ],
+ ...
+ }
+
+Options defined at the host level have the highest priority. In other words,
+if there are options defined with the same type on the global, subnet,
+class, and host levels, the host-specific values are used.
+
+.. _reservation4-message-fields:
+
+Reserving Next Server, Server Hostname, and Boot File Name
+----------------------------------------------------------
+
+BOOTP/DHCPv4 messages include "siaddr", "sname", and "file" fields. Even
+though DHCPv4 includes corresponding options, such as option 66 and
+option 67, some clients may not support these options. For this reason,
+server administrators often use the "siaddr", "sname", and "file" fields
+instead.
+
+With Kea, it is possible to make static reservations for these DHCPv4
+message fields:
+
+::
+
+ {
+ "subnet4": [
+ {
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:ff",
+ "next-server": "10.1.1.2",
+ "server-hostname": "server-hostname.example.org",
+ "boot-file-name": "/tmp/bootfile.efi"
+ }
+ ],
+ ...
+ }
+ ],
+ ...
+ }
+
+Note that those parameters can be specified in combination with other
+parameters for a reservation, such as a reserved IPv4 address. These
+parameters are optional; a subset of them can be specified, or all
+of them can be omitted.
+
+.. _reservation4-client-classes:
+
+Reserving Client Classes in DHCPv4
+----------------------------------
+
+:ref:`classification-using-expressions` explains how to configure
+the server to assign classes to a client, based on the content of the
+options that this client sends to the server. Host reservation
+mechanisms also allow for the static assignment of classes to clients.
+The definitions of these classes are placed in the Kea configuration file or
+a database. The following configuration snippet shows how to specify that
+a client belongs to the classes ``reserved-class1`` and ``reserved-class2``. Those
+classes are associated with specific options sent to the clients which belong
+to them.
+
+::
+
+ {
+ "client-classes": [
+ {
+ "name": "reserved-class1",
+ "option-data": [
+ {
+ "name": "routers",
+ "data": "10.0.0.200"
+ }
+ ]
+ },
+ {
+ "name": "reserved-class2",
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "data": "10.0.0.201"
+ }
+ ]
+ }
+ ],
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.0.0.0/24",
+ "pools": [ { "pool": "10.0.0.10-10.0.0.100" } ],
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:ff",
+
+ "client-classes": [ "reserved-class1", "reserved-class2" ]
+
+ }
+ ]
+ }
+ ]
+ }
+
+In some cases the host reservations can be used in conjunction with client
+classes specified within the Kea configuration. In particular, when a
+host reservation exists for a client within a given subnet, the "KNOWN"
+built-in class is assigned to the client. Conversely, when there is no
+static assignment for the client, the "UNKNOWN" class is assigned to the
+client. Class expressions within the Kea configuration file can
+refer to "KNOWN" or "UNKNOWN" classes using the "member" operator.
+For example:
+
+::
+
+ {
+ "client-classes": [
+ {
+ "name": "dependent-class",
+ "test": "member('KNOWN')",
+ "only-if-required": true
+ }
+ ]
+ }
+
+The ``only-if-required`` parameter is needed here to force evaluation
+of the class after the lease has been allocated and thus the reserved
+class has been also assigned.
+
+.. note::
+
+ The classes specified in non-global host reservations
+ are assigned to the processed packet after all classes with the
+ ``only-if-required`` parameter set to ``false`` have been evaluated.
+ This means that these classes must not depend on the
+ statically assigned classes from the host reservations. If
+ such a dependency is needed, the ``only-if-required`` parameter must
+ be set to ``true`` for the dependent classes. Such classes are
+ evaluated after the static classes have been assigned to the packet.
+ This, however, imposes additional configuration overhead, because
+ all classes marked as ``only-if-required`` must be listed in the
+ ``require-client-classes`` list for every subnet where they are used.
+
+.. note::
+
+ Client classes specified within the Kea configuration file may
+ depend on the classes specified within the global host reservations.
+ In such a case the ``only-if-required`` parameter is not needed.
+ Refer to :ref:`pool-selection-with-class-reservations4` and
+ :ref:`subnet-selection-with-class-reservations4`
+ for specific use cases.
+
+.. _reservations4-mysql-pgsql:
+
+Storing Host Reservations in MySQL or PostgreSQL
+------------------------------------------------
+
+Kea can store host reservations in MySQL or PostgreSQL.
+See :ref:`hosts4-storage` for information on how to
+configure Kea to use reservations stored in MySQL or PostgreSQL.
+Kea provides a dedicated hook for managing reservations in a
+database; section :ref:`hooks-host-cmds` provides detailed information.
+The `Kea wiki
+<https://gitlab.isc.org/isc-projects/kea/wikis/designs/commands#23-host-reservations-hr-management>`__
+provides some examples of how to conduct common host reservation
+operations.
+
+.. note::
+
+ In Kea, the maximum length of an option specified per-host-reservation is
+ arbitrarily set to 4096 bytes.
+
+.. _reservations4-tuning:
+
+Fine-Tuning DHCPv4 Host Reservation
+-----------------------------------
+
+The host reservation capability introduces additional restrictions for
+the allocation engine (the component of Kea that selects an address for
+a client) during lease selection and renewal. In particular, three major
+checks are necessary. First, when selecting a new lease, it is not
+sufficient for a candidate lease to simply not be in use by another DHCP
+client; it also must not be reserved for another client. Similarly, when
+renewing a lease, an additional check must be performed to see whether
+the address being renewed is reserved for another client. Finally, when
+a host renews an address, the server must check whether there is a
+reservation for this host, which would mean the existing (dynamically allocated)
+address should be revoked and the reserved one be used instead.
+
+Some of those checks may be unnecessary in certain deployments, and not
+performing them may improve performance. The Kea server provides the
+``reservation-mode`` configuration parameter to select the types of
+reservations allowed for a particular subnet. Each reservation type has
+different constraints for the checks to be performed by the server when
+allocating or renewing a lease for the client. Although ``reservation-mode``
+was deprecated in Kea 1.9.1, it is still available; the allowed values are:
+
+- ``all`` - enables both in-pool and out-of-pool host reservation
+ types. This setting is the default value, and is the safest and most
+ flexible. However, as all checks are conducted, it is also the slowest.
+ It does not check against global reservations.
+
+- ``out-of-pool`` - allows only out-of-pool host reservations. With
+ this setting in place, the server assumes that all host
+ reservations are for addresses that do not belong to the dynamic
+ pool. Therefore, it can skip the reservation checks when dealing with
+ in-pool addresses, thus improving performance. Do not use this mode
+ if any reservations use in-pool addresses. Caution is advised
+ when using this setting; Kea does not sanity-check the reservations
+ against ``reservation-mode`` and misconfiguration may cause problems.
+
+- ``global`` - allows only global host reservations. With this setting
+ in place, the server searches for reservations for a client only
+ among the defined global reservations. If an address is specified,
+ the server skips the reservation checks carried out in
+ other modes, thus improving performance. Caution is advised when
+ using this setting; Kea does not sanity-check reservations when
+ ``global`` is set, and misconfiguration may cause problems.
+
+- ``disabled`` - host reservation support is disabled. As there are no
+ reservations, the server skips all checks. Any reservations
+ defined are completely ignored. As checks are skipped, the
+ server may operate faster in this mode.
+
+Since Kea 1.9.1, the ``reservation-mode`` parameter is replaced by the
+``reservations-global``, ``reservations-in-subnet``, and
+``reservations-out-of-pool`` flags.
+The flags can be activated independently and can produce various combinations,
+some of which were not supported by the deprecated ``reservation-mode``.
+
+The ``reservation-mode`` parameter can be specified at:
+
+- global level: ``.Dhcp4["reservation-mode"]`` (lowest priority: gets overridden
+ by all others)
+
+- subnet level: ``.Dhcp4.subnet4[]["reservation-mode"]`` (low priority)
+
+- shared-network level: ``.Dhcp4["shared-networks"][]["reservation-mode"]``
+ (high priority)
+
+- shared-network subnet-level:
+ ``.Dhcp4["shared-networks"][].subnet4[]["reservation-mode"]`` (highest
+ priority: overrides all others)
+
+To decide which ``reservation-mode`` to choose, the
+following decision diagram may be useful:
+
+::
+
+ O
+ |
+ v
+ +-----------------------------+------------------------------+
+ | Is per-host configuration needed, such as |
+ | reserving specific addresses, |
+ | assigning specific options or |
+ | assigning packets to specific classes on per-device basis? |
+ +-+-----------------+----------------------------------------+
+ | |
+ no| yes|
+ | | +--------------------------------------+
+ | | | For all given hosts, |
+ +--> "disabled" +-->+ can the reserved resources |
+ | be used in all configured subnets? |
+ +--------+---------------------------+-+
+ | |
+ +----------------------------+ |no |yes
+ | Is | | |
+ | at least one reservation +<--+ "global" <--+
+ | used to reserve addresses? |
+ +-+------------------------+-+
+ | |
+ no| yes| +---------------------------+
+ | | | Is high leases-per-second |
+ +--> "out-of-pool" +-->+ performance or efficient |
+ ^ | resource usage |
+ | | (CPU ticks, RAM usage, |
+ | | database roundtrips) |
+ | | important to your setup? |
+ | +-+----------------+--------+
+ | | |
+ | yes| no|
+ | | |
+ | +-------------+ |
+ | | |
+ | | +----------------------+ |
+ | | | Can it be guaranteed | |
+ | +-->+ that the reserved | |
+ | | addresses | |
+ | | aren't part of the | |
+ | | pools configured | |
+ | | in the respective | |
+ | | subnet? | |
+ | +-+------------------+-+ |
+ | | | |
+ | yes| no| |
+ | | | V
+ +----------------+ +--> "all"
+
+An example configuration that disables reservations looks as follows:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "pools": [
+ {
+ "pool": "192.0.2.10-192.0.2.100"
+ }
+ ],
+ "reservation-mode": "disabled",
+ "subnet": "192.0.2.0/24"
+ }
+ ]
+ }
+ }
+
+An example configuration using global reservations is shown below:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "reservation-mode": "global",
+ "reservations": [
+ {
+ "hostname": "host-one",
+ "hw-address": "01:bb:cc:dd:ee:ff"
+ },
+ {
+ "hostname": "host-two",
+ "hw-address": "02:bb:cc:dd:ee:ff"
+ }
+ ],
+ "subnet4": [
+ {
+ "id": 1,
+ "pools": [
+ {
+ "pool": "192.0.2.10-192.0.2.100"
+ }
+ ],
+ "subnet": "192.0.2.0/24"
+ }
+ ]
+ }
+ }
+
+The meaning of the reservation flags are:
+
+- ``reservations-global``: fetch global reservations.
+
+- ``reservations-in-subnet``: fetch subnet reservations. For a shared network
+ this includes all subnet members of the shared network.
+
+- ``reservations-out-of-pool``: this makes sense only when the
+ ``reservations-in-subnet`` flag is ``true``. When ``reservations-out-of-pool``
+ is ``true``, the server assumes that all host reservations are for addresses
+ that do not belong to the dynamic pool. Therefore, it can skip the reservation
+ checks when dealing with in-pool addresses, thus improving performance.
+ The server will not assign reserved addresses that are inside the dynamic
+ pools to the respective clients. This also means that the addresses matching
+ the respective reservations from inside the dynamic pools (if any) can be
+ dynamically assigned to any client.
+
+The ``disabled`` value from the deprecated ``reservation-mode`` corresponds to:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "reservations-global": false,
+ "reservations-in-subnet": false
+ }
+ }
+
+The ``global`` value from the deprecated ``reservation-mode`` corresponds to:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "reservations-global": true,
+ "reservations-in-subnet": false
+ }
+ }
+
+The ``out-of-pool`` value from the deprecated ``reservation-mode`` corresponds to:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "reservations-global": false,
+ "reservations-in-subnet": true,
+ "reservations-out-of-pool": true
+ }
+ }
+
+And the ``all`` value from the deprecated ``reservation-mode`` corresponds to:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "reservations-global": false,
+ "reservations-in-subnet": true,
+ "reservations-out-of-pool": false
+ }
+ }
+
+To activate both ``global`` and ``all``, the following combination can be used:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "reservations-global": true,
+ "reservations-in-subnet": true,
+ "reservations-out-of-pool": false
+ }
+ }
+
+To activate both ``global`` and ``out-of-pool``, the following combination can
+be used:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "reservations-global": true,
+ "reservations-in-subnet": true,
+ "reservations-out-of-pool": true
+ }
+ }
+
+Enabling ``out-of-pool`` and disabling ``in-subnet`` at the same time
+is not recommended because ``out-of-pool`` applies to host reservations in a
+subnet, which are fetched only when the ``in-subnet`` flag is ``true``.
+
+The parameter can be specified at the global, subnet, and shared-network
+levels.
+
+An example configuration that disables reservations looks as follows:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "reservations-global": false,
+ "reservations-in-subnet": false,
+ "subnet": "192.0.2.0/24",
+ "id": 1
+ }
+ ]
+ }
+ }
+
+An example configuration using global reservations is shown below:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "reservations": [
+ {
+ "hostname": "host-one",
+ "hw-address": "01:bb:cc:dd:ee:ff"
+ },
+ {
+ "hostname": "host-two",
+ "hw-address": "02:bb:cc:dd:ee:ff"
+ }
+ ],
+ "reservations-global": true,
+ "reservations-in-subnet": false,
+ "subnet4": [
+ {
+ "pools": [
+ {
+ "pool": "192.0.2.10-192.0.2.100"
+ }
+ ],
+ "subnet": "192.0.2.0/24",
+ "id": 1
+ }
+ ]
+ }
+ }
+
+For more details regarding global reservations, see :ref:`global-reservations4`.
+
+Another aspect of host reservations is the different types of
+identifiers. Kea currently supports four types of identifiers:
+``hw-address``, ``duid``, ``client-id``, and ``circuit-id``. This is beneficial from a
+usability perspective; however, there is one drawback. For each incoming
+packet, Kea has to extract each identifier type and then query the
+database to see if there is a reservation by this particular identifier.
+If nothing is found, the next identifier is extracted and the next query
+is issued. This process continues until either a reservation is found or
+all identifier types have been checked. Over time, with an increasing
+number of supported identifier types, Kea would become slower and
+slower.
+
+To address this problem, a parameter called
+``host-reservation-identifiers`` is available. It takes a list of
+identifier types as a parameter. Kea checks only those identifier
+types enumerated in ``host-reservation-identifiers``. From a performance
+perspective, the number of identifier types should be kept to a minimum,
+ideally one. If the deployment uses several reservation types, please
+enumerate them from most- to least-frequently used, as this increases
+the chances of Kea finding the reservation using the fewest queries. An
+example of a ``host-reservation-identifiers`` configuration looks as follows:
+
+::
+
+ {
+ "host-reservation-identifiers": [ "circuit-id", "hw-address", "duid", "client-id" ],
+ "subnet4": [
+ {
+ "subnet": "192.0.2.0/24",
+ ...
+ }
+ ],
+ ...
+ }
+
+If not specified, the default value is:
+
+::
+
+ "host-reservation-identifiers": [ "hw-address", "duid", "circuit-id", "client-id" ]
+
+.. note::
+
+ As soon as a host reservation is found the search is stopped so
+ when a client has two host reservations using different enabled
+ identifier types the first is always returned and the second
+ ignored. In other words, this is usually a configuration mistake.
+ In rare cases when having two reservations for the same host makes sense,
+ you can control which of those will be used by ordering the list of
+ identifier types in `host-reservation-identifiers`.
+
+
+.. _global-reservations4:
+
+Global Reservations in DHCPv4
+-----------------------------
+
+In some deployments, such as mobile, clients can roam within the network
+and certain parameters must be specified regardless of the client's
+current location. To meet such a need, Kea offers a global reservation
+mechanism. The idea behind it is that regular host
+reservations are tied to specific subnets, by using a specific
+subnet ID. Kea can specify a global reservation that can be used in
+every subnet that has global reservations enabled.
+
+This feature can be used to assign certain parameters, such as hostname
+or other dedicated, host-specific options. It can also be used to assign
+addresses.
+
+An address assigned via global host reservation must be feasible for the
+subnet the server selects for the client. In other words, the address must
+lie within the subnet; otherwise, it is ignored and the server will
+attempt to dynamically allocate an address. If the selected subnet
+belongs to a shared network, the server checks for feasibility against
+the subnet's siblings, selecting the first in-range subnet. If no such
+subnet exists, the server falls back to dynamically allocating the address.
+
+.. note::
+
+ Prior to release 2.3.5, the server did not perform feasibility checks on
+ globally reserved addresses, which allowed the server to be configured to
+ hand out nonsensical leases for arbitrary address values. Later versions
+ of Kea perform these checks.
+
+To use global host reservations, a configuration similar to the
+following can be used:
+
+::
+
+ "Dhcp4": {
+ # This specifies global reservations.
+ # They will apply to all subnets that
+ # have global reservations enabled.
+
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:ff",
+ "hostname": "hw-host-dynamic"
+ },
+ {
+ "hw-address": "01:02:03:04:05:06",
+ "hostname": "hw-host-fixed",
+
+ # Use of IP addresses in global reservations is risky.
+ # If used outside of a matching subnet, such as 192.0.1.0/24,
+ # it will result in a broken configuration being handed
+ # to the client.
+ "ip-address": "192.0.1.77"
+ },
+ {
+ "duid": "01:02:03:04:05",
+ "hostname": "duid-host"
+ },
+ {
+ "circuit-id": "'charter950'",
+ "hostname": "circuit-id-host"
+ },
+ {
+ "client-id": "01:11:22:33:44:55:66",
+ "hostname": "client-id-host"
+ }
+ ],
+ "valid-lifetime": 600,
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.0.0.0/24",
+ # It is replaced by the "reservations-global",
+ # "reservations-in-subnet", and "reservations-out-of-pool"
+ # parameters.
+ # "reservation-mode": "global",
+ # Specify if the server should look up global reservations.
+ "reservations-global": true,
+ # Specify if the server should look up in-subnet reservations.
+ "reservations-in-subnet": false,
+ # Specify if the server can assume that all reserved addresses
+ # are out-of-pool. It can be ignored because "reservations-in-subnet"
+ # is false.
+ # "reservations-out-of-pool": false,
+ "pools": [ { "pool": "10.0.0.10-10.0.0.100" } ]
+ }
+ ]
+ }
+
+When using database backends, the global host reservations are
+distinguished from regular reservations by using a ``subnet-id`` value of
+0.
+
+.. _pool-selection-with-class-reservations4:
+
+Pool Selection with Client Class Reservations
+---------------------------------------------
+
+Client classes can be specified in the Kea configuration file and/or via
+host reservations. The classes specified in the Kea configuration file are
+evaluated immediately after receiving the DHCP packet and therefore can be
+used to influence subnet selection using the ``client-class`` parameter
+specified in the subnet scope. The classes specified within the host
+reservations are fetched and assigned to the packet after the server has
+already selected a subnet for the client. This means that the client
+class specified within a host reservation cannot be used to influence
+subnet assignment for this client, unless the subnet belongs to a
+shared network. If the subnet belongs to a shared network, the server may
+dynamically change the subnet assignment while trying to allocate a lease.
+If the subnet does not belong to a shared network, the subnet
+is not changed once selected.
+
+If the subnet does not belong to a shared network, it is possible to
+use host reservation-based client classification to select an address pool
+within the subnet as follows:
+
+::
+
+ "Dhcp4": {
+ "client-classes": [
+ {
+ "name": "reserved_class"
+ },
+ {
+ "name": "unreserved_class",
+ "test": "not member('reserved_class')"
+ }
+ ],
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:fe",
+ "client-classes": [ "reserved_class" ]
+ }
+ ],
+ "pools": [
+ {
+ "pool": "192.0.2.10-192.0.2.20",
+ "client-class": "reserved_class"
+ },
+ {
+ "pool": "192.0.2.30-192.0.2.40",
+ "client-class": "unreserved_class"
+ }
+ ]
+ }
+ ]
+ }
+
+The ``reserved_class`` is declared without the ``test`` parameter because
+it may only be assigned to the client via the host reservation mechanism. The
+second class, ``unreserved_class``, is assigned to clients which do not
+belong to the ``reserved_class``. The first pool within the subnet is only
+used for clients having a reservation for the ``reserved_class``. The
+second pool is used for clients not having such a reservation. The
+configuration snippet includes one host reservation which causes the client
+with the MAC address aa:bb:cc:dd:ee:fe to be assigned to the
+``reserved_class``. Thus, this client will be given an IP address from the
+first address pool.
+
+.. _subnet-selection-with-class-reservations4:
+
+Subnet Selection with Client Class Reservations
+-----------------------------------------------
+
+There is one specific use case when subnet selection may be influenced by
+client classes specified within host reservations: when the
+client belongs to a shared network. In such a case it is possible to use
+classification to select a subnet within this shared network. Consider the
+following example:
+
+::
+
+ "Dhcp4": {
+ "client-classes": [
+ {
+ "name": "reserved_class"
+ },
+ {
+ "name": "unreserved_class",
+ "test": "not member('reserved_class')"
+ }
+ ],
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:fe",
+ "client-classes": [ "reserved_class" ]
+ }
+ ],
+ # It is replaced by the "reservations-global",
+ # "reservations-in-subnet", and "reservations-out-of-pool" parameters.
+ # Specify if the server should look up global reservations.
+ "reservations-global": true,
+ # Specify if the server should look up in-subnet reservations.
+ "reservations-in-subnet": false,
+ # Specify if the server can assume that all reserved addresses
+ # are out-of-pool. It can be ignored because "reservations-in-subnet"
+ # is false, but if specified, it is inherited by "shared-networks"
+ # and "subnet4" levels.
+ # "reservations-out-of-pool": false,
+ "shared-networks": [
+ {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "pools": [
+ {
+ "pool": "192.0.2.10-192.0.2.20",
+ "client-class": "reserved_class"
+ }
+ ]
+ },
+ {
+ "id": 2,
+ "subnet": "192.0.3.0/24",
+ "pools": [
+ {
+ "pool": "192.0.3.10-192.0.3.20",
+ "client-class": "unreserved_class"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+
+This is similar to the example described in
+:ref:`pool-selection-with-class-reservations4`. This time, however, there
+are two subnets, each of which has a pool associated with a different
+class. The clients that do not have a reservation for the ``reserved_class``
+are assigned an address from the subnet 192.0.3.0/24. Clients with
+a reservation for the ``reserved_class`` are assigned an address from
+the subnet 192.0.2.0/24. The subnets must belong to the same shared network.
+In addition, the reservation for the client class must be specified at the
+global scope (global reservation) and ``reservations-global`` must be
+set to ``true``.
+
+In the example above, the ``client-class`` could also be specified at the
+subnet level rather than the pool level, and would yield the same effect.
+
+.. _multiple-reservations-same-ip4:
+
+Multiple Reservations for the Same IP
+-------------------------------------
+
+Host reservations were designed to preclude the creation of multiple
+reservations for the same IP address within a particular subnet, to avoid
+having two different clients compete for the same address.
+When using the default settings, the server returns a configuration error
+when it finds two or more reservations for the same IP address within
+a subnet in the Kea configuration file. :ischooklib:`libdhcp_host_cmds.so`
+returns an error in response to the :isccmd:`reservation-add` command
+when it detects that the reservation exists in the database for the IP
+address for which the new reservation is being added.
+
+In some deployments a single host can select one of several network
+interfaces to communicate with the DHCP server, and the server must assign
+the same IP address to the host regardless of the interface used. Since
+each interface is assigned a different MAC address, it implies that
+several host reservations must be created to associate all of the MAC
+addresses present on this host with IP addresses. Using different
+IP addresses for each interface is impractical and is considered a waste
+of the IPv4 address space, especially since the host typically uses only one
+interface for communication with the server, hence only one IP address
+is in use.
+
+This causes a need to create multiple host reservations for a single
+IP address within a subnet; this is supported since the Kea 1.9.1
+release as an optional mode of operation, enabled with the
+``ip-reservations-unique`` global parameter.
+
+The ``ip-reservations-unique`` is a boolean parameter that defaults to
+``true``, which forbids the specification of more than one reservation
+for the same IP address within a given subnet. Setting this parameter to
+``false`` allows such reservations to be created both in the Kea configuration
+file and in the host database backend, via :ischooklib:`libdhcp_host_cmds.so`.
+
+Setting ``ip-reservations-unique`` to ``false`` when using memfile, MySQL or PostgreSQL is supported.
+This setting is not supported when using Host Cache (see :ref:`hooks-host-cache`), and the RADIUS backend
+(see :ref:`hooks-radius`). These reservation backends simply do not support multiple reservations for the
+same IP. If either of these hooks are loaded and ``ip-reservations-unique`` is set to ``false``, then a
+configuration error will be emitted and the server will fail to start.
+
+.. note::
+
+ When ``ip-reservations-unique`` is set to ``true`` (the default value),
+ the server ensures that IP reservations are unique for a subnet within
+ a single host backend and/or Kea configuration file. It does not
+ guarantee that the reservations are unique across multiple backends.
+ On server startup, only IP reservations defined in the Kea configuration
+ file are checked for uniqueness.
+
+The following is an example configuration with two reservations for
+the same IP address but different MAC addresses:
+
+::
+
+ "Dhcp4": {
+ "ip-reservations-unique": false,
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "reservations": [
+ {
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "ip-address": "192.0.2.11"
+ },
+ {
+ "hw-address": "2a:2b:2c:2d:2e:2f",
+ "ip-address": "192.0.2.11"
+ }
+ ]
+ }
+ ]
+ }
+
+It is possible to control the ``ip-reservations-unique`` parameter via the
+:ref:`dhcp4-cb`. If the new setting of this parameter conflicts with
+the currently used backends (i.e. backends do not support the new setting),
+the new setting is ignored and a warning log message is generated.
+The backends continue to use the default setting, expecting that
+IP reservations are unique within each subnet. To allow the
+creation of non-unique IP reservations, the administrator must remove
+the backends which lack support for them from the configuration file.
+
+Administrators must be careful when they have been using multiple
+reservations for the same IP address and later decide to return to
+the default mode in which this is no longer allowed. They
+must make sure that at most one reservation for a given IP address
+exists within a subnet, prior to switching back to the default mode.
+If such duplicates are left in the configuration file, the server
+reports a configuration error. Leaving such reservations in the host
+databases does not cause configuration errors but may lead to lease
+allocation errors during the server's operation, when it unexpectedly
+finds multiple reservations for the same IP address.
+
+.. note::
+
+ Currently the Kea server does not verify whether multiple reservations for
+ the same IP address exist in MySQL and/or PostgreSQL host databases when
+ ``ip-reservations-unique`` is updated from ``false`` to ``true``. This may
+ cause issues with lease allocations. The administrator must ensure that there
+ is at most one reservation for each IP address within each subnet, prior to
+ the configuration update.
+
+The ``reservations-lookup-first`` is a boolean parameter which controls whether
+host reservations lookup should be performed before lease lookup. This parameter
+has effect only when multi-threading is disabled. When multi-threading is
+enabled, host reservations lookup is always performed first to avoid lease-lookup
+resource locking. The ``reservations-lookup-first`` parameter defaults to ``false``
+when multi-threading is disabled.
+
+.. _host_reservations_as_basic_access_control4:
+
+Host Reservations as Basic Access Control
+-----------------------------------------
+
+Starting with Kea 2.3.5, it is possible to define a host reservation that
+contains just an identifier, without any address, options, or values. In some
+deployments this is useful, as the hosts that have a reservation belong to
+the KNOWN class while others do not. This can be used as a basic access control
+mechanism.
+
+The following example demonstrates this concept. It indicates a single IPv4 subnet
+and all clients will get an address from it. However, only known clients (those that
+have reservations) will get their default router configured. Empty reservations
+i.e. reservations that only have the identification criterion, can be
+specifically useful in this regard of making the clients known.
+
+::
+
+ "Dhcp4": {
+ "client-classes": [
+ {
+ "name": "KNOWN",
+ "option-data": [
+ {
+ "name": "routers",
+ "data": "192.0.2.250"
+ }
+ ]
+ }
+ ],
+ "reservations": [
+ // Clients on this list will be added to the KNOWN class.
+ { "hw-address": "aa:bb:cc:dd:ee:fe" },
+ { "hw-address": "11:22:33:44:55:66" }
+ ],
+ "reservations-in-subnet": true,
+
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "pools": [
+ {
+ "pool": "192.0.2.1-192.0.2.200"
+ }
+ ]
+ }
+ ]
+ }
+
+This concept can be extended further. A good real-life scenario might be a
+situation where some customers of an ISP have not paid their bills. A new class can be
+defined to use an alternative default router that, instead of relaying traffic,
+redirects those customers to a captive portal urging them to bring their accounts up to date.
+
+::
+
+ "Dhcp4": {
+ "client-classes": [
+ {
+ "name": "blocked",
+ "option-data": [
+ {
+ "name": "routers",
+ "data": "192.0.2.251"
+ }
+ ]
+ }
+ ],
+ "reservations": [
+ // Clients on this list will be added to the KNOWN class. Some
+ // will also be added to the blocked class.
+ { "hw-address": "aa:bb:cc:dd:ee:fe",
+ "client-classes": [ "blocked" ] },
+ { "hw-address": "11:22:33:44:55:66" }
+ ],
+ "reservations-in-subnet": true,
+
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "pools": [
+ {
+ "pool": "192.0.2.1-192.0.2.200"
+ }
+ ],
+ "option-data": [
+ {
+ "name": "routers",
+ "data": "192.0.2.250"
+ }
+ ]
+ }
+ ]
+ }
+
+.. _shared-network4:
+
+Shared Networks in DHCPv4
+=========================
+
+DHCP servers use subnet information in two ways. It is used to
+both determine the point of attachment, i.e. where the client is
+connected to the network, and to
+group information pertaining to a specific location in the network.
+Sometimes it is useful to have more than one
+logical IP subnet deployed on the same physical link.
+Understanding that two or more subnets are used on the same link requires
+additional logic in the DHCP server. This capability is called "shared
+networks" in Kea, and sometimes also
+"shared subnets"; in Microsoft's nomenclature it is called "multinet."
+
+There are many cases where the shared networks feature is useful; here we
+explain just a handful of the most common ones. The first and by far
+most common use case is an existing IPv4 network that has grown and is
+running out of available address space. Rather than migrating all
+devices to a new, larger subnet, it is easier to simply configure
+additional subnets on top of the existing one. Sometimes, due to address
+space fragmentation (e.g. only many disjointed /24s are available), this
+is the only choice. Also, configuring additional subnets has the
+advantage of not disrupting the operation of existing devices.
+
+Another very frequent use case comes from cable networks. There are two
+types of devices in cable networks: cable modems and the end-user
+devices behind them. It is a common practice to use different subnets
+for cable modems to prevent users from tinkering with them. In this
+case, the distinction is based on the type of device, rather than
+on address-space exhaustion.
+
+A client connected to a shared network may be assigned an address from
+any of the pools defined within the subnets belonging to the shared
+network. Internally, the server selects one of the subnets belonging to
+a shared network and tries to allocate an address from this subnet. If
+the server is unable to allocate an address from the selected subnet
+(e.g., due to address-pool exhaustion), it uses another subnet from
+the same shared network and tries to allocate an address from this subnet.
+The server typically allocates all
+addresses available in a given subnet before it starts allocating
+addresses from other subnets belonging to the same shared network.
+However, in certain situations the client can be allocated an address
+from another subnet before the address pools in the first subnet get
+exhausted; this sometimes occurs when the client provides a hint that belongs to another
+subnet, or the client has reservations in a subnet other than the
+default.
+
+.. note::
+
+ Deployments should not assume that Kea waits until it has allocated
+ all the addresses from the first subnet in a shared network before
+ allocating addresses from other subnets.
+
+To define a shared network, an additional configuration scope is
+introduced:
+
+::
+
+ {
+ "Dhcp4": {
+ "shared-networks": [
+ {
+ # Name of the shared network. It may be an arbitrary string
+ # and it must be unique among all shared networks.
+ "name": "my-secret-lair-level-1",
+
+ # The subnet selector can be specified at the shared-network level.
+ # Subnets from this shared network will be selected for directly
+ # connected clients sending requests to the server's "eth0" interface.
+ "interface": "eth0",
+
+ # This starts a list of subnets in this shared network.
+ # There are two subnets in this example.
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.0.0.0/8",
+ "pools": [ { "pool": "10.0.0.1 - 10.0.0.99" } ]
+ },
+ {
+ "id": 2,
+ "subnet": "192.0.2.0/24",
+ "pools": [ { "pool": "192.0.2.100 - 192.0.2.199" } ]
+ }
+ ]
+ }
+ ],
+ # end of shared-networks
+
+ # It is likely that in the network there will be a mix of regular,
+ # "plain" subnets and shared networks. It is perfectly valid to mix
+ # them in the same configuration file.
+ #
+ # This is a regular subnet. It is not part of any shared network.
+ "subnet4": [
+ {
+ "id": 3,
+ "subnet": "192.0.3.0/24",
+ "pools": [ { "pool": "192.0.3.1 - 192.0.3.200" } ],
+ "interface": "eth1"
+ }
+ ]
+ }
+ }
+
+As demonstrated in the example, it is possible to mix shared and regular
+("plain") subnets. Each shared network must have a unique name. This is
+similar to the ID for subnets, but gives administrators more
+flexibility. It is used for logging, but also internally for identifying
+shared networks.
+
+In principle it makes sense to define only shared networks that consist
+of two or more subnets. However, for testing purposes, an empty subnet
+or a network with just a single subnet is allowed. This is not a
+recommended practice in production networks, as the shared network logic
+requires additional processing and thus lowers the server's performance.
+To avoid unnecessary performance degradation, shared subnets should
+only be defined when required by the deployment.
+
+Shared networks provide the ability to specify many parameters in the
+shared network scope that apply to all subnets within it. If
+necessary, it is possible to specify a parameter in the shared-network scope and
+then override its value in the subnet scope. For example:
+
+::
+
+ {
+ "shared-networks": [
+ {
+ "name": "lab-network3",
+
+ "interface": "eth0",
+
+ # This applies to all subnets in this shared network, unless
+ # values are overridden on subnet scope.
+ "valid-lifetime": 600,
+
+ # This option is made available to all subnets in this shared
+ # network.
+ "option-data": [
+ {
+ "name": "log-servers",
+ "data": "1.2.3.4"
+ }
+ ],
+
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.0.0.0/8",
+ "pools": [ { "pool": "10.0.0.1 - 10.0.0.99" } ],
+
+ # This particular subnet uses different values.
+ "valid-lifetime": 1200,
+ "option-data": [
+ {
+ "name": "log-servers",
+ "data": "10.0.0.254"
+ },
+ {
+ "name": "routers",
+ "data": "10.0.0.254"
+ } ]
+ },
+ {
+ "id": 2,
+ "subnet": "192.0.2.0/24",
+ "pools": [ { "pool": "192.0.2.100 - 192.0.2.199" } ],
+
+ # This subnet does not specify its own valid-lifetime value,
+ # so it is inherited from shared network scope.
+ "option-data": [
+ {
+ "name": "routers",
+ "data": "192.0.2.1"
+ } ]
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+In this example, there is a ``log-servers`` option defined that is available
+to clients in both subnets in this shared network. Also, the valid
+lifetime is set to 10 minutes (600s). However, the first subnet
+overrides some of the values (the valid lifetime is 20 minutes, there is a different IP
+address for ``log-servers``), but also adds its own option (the router address).
+Assuming a client asking for router and ``log-servers`` options is assigned
+a lease from this subnet, it will get a lease for 20 minutes and a
+``log-servers`` and routers value of 10.0.0.254. If the same client is
+assigned to the second subnet, it will get a 10-minute lease, a
+``log-servers`` value of 1.2.3.4, and routers set to 192.0.2.1.
+
+Local and Relayed Traffic in Shared Networks
+--------------------------------------------
+
+It is possible to specify an interface name at the shared-network level,
+to tell the server that this specific shared network is reachable
+directly (not via relays) using the local network interface. As all
+subnets in a shared network are expected to be used on the same physical
+link, it is a configuration error to attempt to define a shared network
+using subnets that are reachable over different interfaces. In other
+words, all subnets within the shared network must have the same value
+for the ``interface`` parameter. The following configuration is an
+example of what **NOT** to do:
+
+::
+
+ {
+ "shared-networks": [
+ {
+ "name": "office-floor-2",
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.0.0.0/8",
+ "pools": [ { "pool": "10.0.0.1 - 10.0.0.99" } ],
+ "interface": "eth0"
+ },
+ {
+ "id": 2,
+ "subnet": "192.0.2.0/24",
+ "pools": [ { "pool": "192.0.2.100 - 192.0.2.199" } ],
+
+ # Specifying the different interface name is a configuration
+ # error. This value should rather be "eth0" or the interface
+ # name in the other subnet should be "eth1".
+ "interface": "eth1"
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+To minimize the chance of configuration errors, it is often more convenient
+to simply specify the interface name once, at the shared-network level, as
+shown in the example below.
+
+::
+
+ {
+ "shared-networks": [
+ {
+ "name": "office-floor-2",
+
+ # This tells Kea that the whole shared network is reachable over a
+ # local interface. This applies to all subnets in this network.
+ "interface": "eth0",
+
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.0.0.0/8",
+ "pools": [ { "pool": "10.0.0.1 - 10.0.0.99" } ]
+ },
+ {
+ "id": 2,
+ "subnet": "192.0.2.0/24",
+ "pools": [ { "pool": "192.0.2.100 - 192.0.2.199" } ]
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+
+With relayed traffic, subnets are typically selected using
+the relay agents' addresses. If the subnets are used independently (not
+grouped within a shared network), a different relay
+address can be specified for each of these subnets. When multiple subnets belong to a
+shared network they must be selected via the same relay address and,
+similarly to the case of the local traffic described above, it is a
+configuration error to specify different relay addresses for the respective
+subnets in the shared network. The following configuration is another example
+of what **NOT** to do:
+
+::
+
+ {
+ "shared-networks": [
+ {
+ "name": "kakapo",
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/26",
+ "relay": {
+ "ip-addresses": [ "192.1.1.1" ]
+ },
+ "pools": [ { "pool": "192.0.2.63 - 192.0.2.63" } ]
+ },
+ {
+ "id": 2,
+ "subnet": "10.0.0.0/24",
+ "relay": {
+ # Specifying a different relay address for this
+ # subnet is a configuration error. In this case
+ # it should be 192.1.1.1 or the relay address
+ # in the previous subnet should be 192.2.2.2.
+ "ip-addresses": [ "192.2.2.2" ]
+ },
+ "pools": [ { "pool": "10.0.0.16 - 10.0.0.16" } ]
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+Again, it is better to specify the relay address at the shared-network
+level; this value will be inherited by all subnets belonging to the
+shared network.
+
+::
+
+ {
+ "shared-networks": [
+ {
+ "name": "kakapo",
+ "relay": {
+ # This relay address is inherited by both subnets.
+ "ip-addresses": [ "192.1.1.1" ]
+ },
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/26",
+ "pools": [ { "pool": "192.0.2.63 - 192.0.2.63" } ]
+ },
+ {
+ "id": 2,
+ "subnet": "10.0.0.0/24",
+ "pools": [ { "pool": "10.0.0.16 - 10.0.0.16" } ]
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+Even though it is technically possible to configure two (or more) subnets
+within the shared network to use different relay addresses, this will almost
+always lead to a different behavior than what the user expects. In this
+case, the Kea server will initially select one of the subnets by matching
+the relay address in the client's packet with the subnet's configuration.
+However, it MAY end up using the other subnet (even though it does not match
+the relay address) if the client already has a lease in this subnet or has a
+host reservation in this subnet, or simply if the initially selected subnet has no
+more addresses available. Therefore, it is strongly recommended to always
+specify subnet selectors (interface or relay address) at the shared-network
+level if the subnets belong to a shared network, as it is rarely useful to
+specify them at the subnet level and may lead to the configuration errors
+described above.
+
+Client Classification in Shared Networks
+----------------------------------------
+
+Sometimes it is desirable to segregate clients into specific subnets
+based on certain properties. This mechanism is called client
+classification and is described in :ref:`classify`. Client
+classification can be applied to subnets belonging to shared networks in
+the same way as it is used for subnets specified outside of shared
+networks. It is important to understand how the server selects subnets
+for clients when client classification is in use, to ensure that the
+appropriate subnet is selected for a given client type.
+
+If a subnet is associated with a class, only the clients belonging to
+this class can use this subnet. If there are no classes specified for a
+subnet, any client connected to a given shared network can use this
+subnet. A common mistake is to assume that a subnet that includes a client
+class is preferred over subnets without client classes. Consider the
+following example:
+
+::
+
+ {
+ "client-classes": [
+ {
+ "name": "b-devices",
+ "test": "option[93].hex == 0x0002"
+ }
+ ],
+ "shared-networks": [
+ {
+ "name": "galah",
+ "interface": "eth0",
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/26",
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.63" } ]
+ },
+ {
+ "id": 2,
+ "subnet": "10.0.0.0/24",
+ "pools": [ { "pool": "10.0.0.2 - 10.0.0.250" } ],
+ "client-class": "b-devices"
+ }
+ ]
+ }
+ ]
+ }
+
+If the client belongs to the "b-devices" class (because it includes
+option 93 with a value of 0x0002), that does not guarantee that the
+subnet 10.0.0.0/24 will be used (or preferred) for this client. The
+server can use either of the two subnets, because the subnet 192.0.2.0/26
+is also allowed for this client. The client classification used in this
+case should be perceived as a way to restrict access to certain subnets,
+rather than as a way to express subnet preference. For example, if the
+client does not belong to the "b-devices" class, it may only use the
+subnet 192.0.2.0/26 and will never use the subnet 10.0.0.0/24.
+
+A typical use case for client classification is in a cable network,
+where cable modems should use one subnet and other devices should use
+another subnet within the same shared network. In this case it is
+necessary to apply classification on all subnets. The following example
+defines two classes of devices, and the subnet selection is made based
+on option 93 values.
+
+::
+
+ {
+ "client-classes": [
+ {
+
+ "name": "a-devices",
+ "test": "option[93].hex == 0x0001"
+ },
+ {
+ "name": "b-devices",
+ "test": "option[93].hex == 0x0002"
+ }
+ ],
+ "shared-networks": [
+ {
+ "name": "galah",
+ "interface": "eth0",
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/26",
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.63" } ],
+ "client-class": "a-devices"
+ },
+ {
+ "id": 2,
+ "subnet": "10.0.0.0/24",
+ "pools": [ { "pool": "10.0.0.2 - 10.0.0.250" } ],
+ "client-class": "b-devices"
+ }
+ ]
+ }
+ ]
+ }
+
+In this example each class has its own restriction. Only clients that
+belong to class "a-devices" are able to use subnet 192.0.2.0/26 and
+only clients belonging to "b-devices" are able to use subnet
+10.0.0.0/24. Care should be taken not to define too-restrictive
+classification rules, as clients that are unable to use any subnets will
+be refused service. However, this may be a desired outcome if one wishes
+to provide service only to clients with known properties (e.g. only VoIP
+phones allowed on a given link).
+
+It is possible to achieve an effect similar to the one
+presented in this section without the use of shared networks. If the
+subnets are placed in the global subnets scope, rather than in the
+shared network, the server will still use classification rules to pick
+the right subnet for a given class of devices. The major benefit of
+placing subnets within the shared network is that common parameters for
+the logically grouped subnets can be specified once in the
+shared-network scope, e.g. the ``interface`` or ``relay`` parameter. All subnets
+belonging to this shared network will inherit those parameters.
+
+Host Reservations in Shared Networks
+------------------------------------
+
+Subnets that are part of a shared network allow host reservations,
+similar to regular subnets:
+
+::
+
+ {
+ "shared-networks": [
+ {
+ "name": "frog",
+ "interface": "eth0",
+ "subnet4": [
+ {
+ "subnet": "192.0.2.0/26",
+ "id": 100,
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.63" } ],
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:ff",
+ "ip-address": "192.0.2.28"
+ }
+ ]
+ },
+ {
+ "subnet": "10.0.0.0/24",
+ "id": 101,
+ "pools": [ { "pool": "10.0.0.1 - 10.0.0.254" } ],
+ "reservations": [
+ {
+ "hw-address": "11:22:33:44:55:66",
+ "ip-address": "10.0.0.29"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+
+
+It is worth noting that Kea conducts additional checks when processing a
+packet if shared networks are defined. First, instead of simply checking
+whether there is a reservation for a given client in its initially
+selected subnet, Kea looks through all subnets in a shared network for a
+reservation. This is one of the reasons why defining a shared network
+may impact performance. If there is a reservation for a client in any
+subnet, that particular subnet is selected for the client. Although
+it is technically not an error, it is considered bad practice to define
+reservations for the same host in multiple subnets belonging to the same
+shared network.
+
+While not strictly mandatory, it is strongly recommended to use explicit
+"id" values for subnets if database storage will be used for host
+reservations. If an ID is not specified, the values for it are
+auto-generated, i.e. Kea assigns increasing integer values starting from
+1. Thus, the auto-generated IDs are not stable across configuration
+changes.
+
+.. _dhcp4-serverid:
+
+Server Identifier in DHCPv4
+===========================
+
+The DHCPv4 protocol uses a "server identifier" to allow clients to
+discriminate between several servers present on the same link; this
+value is an IPv4 address of the server. The server chooses the IPv4
+address of the interface on which the message from the client (or relay)
+has been received. A single server instance uses multiple server
+identifiers if it is receiving queries on multiple interfaces.
+
+It is possible to override the default server identifier values by specifying
+the ``dhcp-server-identifier`` option. This option configuration is only
+supported at the subnet, shared network, client class, and global levels. It
+must not be specified at the host-reservation level.
+When configuring the ``dhcp-server-identifier`` option at client-class level, the
+class must not set the ``only-if-required`` flag, because this class would not
+be evaluated before the server determines if the received DHCP message should
+be accepted for processing. Such classes are evaluated after subnet selection.
+See :ref:`dhcp4-required-class` for details.
+
+The following example demonstrates how to override the server identifier
+for a subnet:
+
+::
+
+ {
+ "subnet4": [
+ {
+ "subnet": "192.0.2.0/24",
+ "option-data": [
+ {
+ "name": "dhcp-server-identifier",
+ "data": "10.2.5.76"
+ }
+ ],
+ ...
+ }
+ ],
+ ...
+ }
+
+.. _dhcp4-subnet-selection:
+
+How the DHCPv4 Server Selects a Subnet for the Client
+=====================================================
+
+The DHCPv4 server differentiates among directly connected clients,
+clients trying to renew leases, and clients sending their messages
+through relays. For directly connected clients, the server checks
+the configuration for the interface on which the message has been
+received and, if the server configuration does not match any configured
+subnet, the message is discarded.
+
+An optional interface parameter is available within a subnet definition to
+designate that a given subnet is local, i.e. reachable directly over the
+specified interface. For example, a server that is intended to serve a local
+subnet over eth0 may be configured as follows:
+
+::
+
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "pools": [
+ {
+ "pool": "192.0.2.100 - 192.0.2.199"
+ }
+ ],
+ "interface": "eth0"
+ }
+ ],
+ ...
+ }
+
+Assuming that the server's interface is configured with the IPv4 address
+192.0.2.3, the server only processes messages received through this
+interface from a directly connected client if there is a subnet
+configured to which this IPv4 address belongs, such as 192.0.2.0/24. The
+server uses this subnet to assign an IPv4 address for the client.
+
+The rule above does not apply when the client unicasts its message, i.e.
+is trying to renew its lease; such a message is accepted through any
+interface. The renewing client sets ``ciaddr`` to the currently used IPv4
+address, and the server uses this address to select the subnet for the
+client (in particular, to extend the lease using this address).
+
+If the message is relayed it is accepted through any interface. The
+``giaddr`` set by the relay agent is used to select the subnet for the
+client.
+
+It is also possible to specify a relay IPv4 address for a given subnet.
+It can be used to match incoming packets into a subnet in uncommon
+configurations, e.g. shared networks. See :ref:`dhcp4-relay-override` for details.
+
+.. note::
+
+ The subnet selection mechanism described in this section is based on
+ the assumption that client classification is not used. The
+ classification mechanism alters the way in which a subnet is selected
+ for the client, depending on the classes to which the client belongs.
+
+.. note::
+
+ When the selected subnet is a member of a shared network, the
+ whole shared network is selected.
+
+.. _dhcp4-relay-override:
+
+Using a Specific Relay Agent for a Subnet
+-----------------------------------------
+
+A relay must have an interface connected to the link on which the
+clients are being configured. Typically the relay has an IPv4 address
+configured on that interface, which belongs to the subnet from which the
+server assigns addresses. Normally, the server is able to use the
+IPv4 address inserted by the relay (in the ``giaddr`` field of the DHCPv4
+packet) to select the appropriate subnet.
+
+However, that is not always the case. In certain uncommon — but valid —
+deployments, the relay address may not match the subnet. This usually
+means that there is more than one subnet allocated for a given link. The
+two most common examples of this are long-lasting network
+renumbering (where both old and new address spaces are still being used)
+and a cable network. In a cable network, both cable modems and the
+devices behind them are physically connected to the same link, yet they
+use distinct addressing. In such a case, the DHCPv4 server needs
+additional information (the IPv4 address of the relay) to properly
+select an appropriate subnet.
+
+The following example assumes that there is a subnet 192.0.2.0/24 that
+is accessible via a relay that uses 10.0.0.1 as its IPv4 address. The
+server is able to select this subnet for any incoming packets that come
+from a relay that has an address in the 192.0.2.0/24 subnet. It also
+selects that subnet for a relay with address 10.0.0.1.
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ],
+ "relay": {
+ "ip-addresses": [ "10.0.0.1" ]
+ }
+ }
+ ]
+ }
+ }
+
+If ``relay`` is specified, the ``ip-addresses`` parameter within it is
+mandatory. The ``ip-addresses`` parameter supports specifying a list of addresses.
+
+.. _dhcp4-srv-example-client-class-relay:
+
+Segregating IPv4 Clients in a Cable Network
+-------------------------------------------
+
+In certain cases, it is useful to mix relay address information
+(introduced in :ref:`dhcp4-relay-override`) with client classification (explained
+in :ref:`classify`). One specific example is in a cable network,
+where modems typically get addresses from a different subnet than all
+the devices connected behind them.
+
+Let us assume that there is one Cable Modem Termination System (CMTS)
+with one CM MAC (a physical link that modems are connected to). We want
+the modems to get addresses from the 10.1.1.0/24 subnet, while
+everything connected behind the modems should get addresses from the
+192.0.2.0/24 subnet. The CMTS that acts as a relay uses address
+10.1.1.1. The following configuration can serve that situation:
+
+::
+
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.1.1.0/24",
+ "pools": [ { "pool": "10.1.1.2 - 10.1.1.20" } ],
+ "client-class": "docsis3.0",
+ "relay": {
+ "ip-addresses": [ "10.1.1.1" ]
+ }
+ },
+ {
+ "id": 2,
+ "subnet": "192.0.2.0/24",
+ "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ],
+ "relay": {
+ "ip-addresses": [ "10.1.1.1" ]
+ }
+ }
+ ],
+ ...
+ }
+
+.. _dhcp4-decline:
+
+Duplicate Addresses (DHCPDECLINE Support)
+=========================================
+
+The DHCPv4 server is configured with a certain pool of addresses that it
+is expected to hand out to DHCPv4 clients. It is assumed that the server
+is authoritative and has complete jurisdiction over those addresses.
+However, for various reasons such as misconfiguration or a faulty
+client implementation that retains its address beyond the valid
+lifetime, there may be devices connected that use those addresses
+without the server's approval or knowledge.
+
+Such an unwelcome event can be detected by legitimate clients (using ARP
+or ICMP Echo Request mechanisms) and reported to the DHCPv4 server using
+a DHCPDECLINE message. The server does a sanity check (to see whether
+the client declining an address really was supposed to use it) and then
+conducts a clean-up operation. Any DNS entries related to that
+address are removed, the event is logged, and hooks are
+triggered. After that is complete, the address is marked as
+declined (which indicates that it is used by an unknown entity and thus
+not available for assignment) and a probation time is set on it.
+Unless otherwise configured, the probation period lasts 24 hours; after
+that time, the server will recover the lease (i.e. put it back into
+the available state) and the address will be available for assignment
+again. It should be noted that if the underlying issue of a
+misconfigured device is not resolved, the duplicate-address scenario
+will repeat. If reconfigured correctly, this mechanism provides an
+opportunity to recover from such an event automatically, without any
+system administrator intervention.
+
+To configure the decline probation period to a value other than the
+default, the following syntax can be used:
+
+::
+
+ "Dhcp4": {
+ "decline-probation-period": 3600,
+ "subnet4": [
+ {
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+The parameter is expressed in seconds, so the example above
+instructs the server to recycle declined leases after one hour.
+
+There are several statistics and hook points associated with the decline
+handling procedure. The ``lease4_decline`` hook point is triggered after the
+incoming DHCPDECLINE message has been sanitized and the server is about
+to decline the lease. The ``declined-addresses`` statistic is increased
+after the hook returns (both the global and subnet-specific variants). (See
+:ref:`dhcp4-stats` and :ref:`hooks-libraries`
+for more details on DHCPv4 statistics and Kea hook points.)
+
+Once the probation time elapses, the declined lease is recovered using
+the standard expired-lease reclamation procedure, with several
+additional steps. In particular, both ``declined-addresses`` statistics
+(global and subnet-specific) are decreased. At the same time,
+``reclaimed-declined-addresses`` statistics (again in two variants, global
+and subnet-specific) are increased.
+
+A note about statistics: The Kea server does not decrease the
+``assigned-addresses`` statistics when a DHCPDECLINE is received and
+processed successfully. While technically a declined address is no
+longer assigned, the primary usage of the ``assigned-addresses`` statistic
+is to monitor pool utilization. Most people would forget to include
+``declined-addresses`` in the calculation, and would simply use
+``assigned-addresses``/``total-addresses``. This would cause a bias towards
+under-representing pool utilization. As this has a potential to cause serious
+confusion, ISC decided not to decrease ``assigned-addresses`` immediately after
+receiving DHCPDECLINE, but to do it later when Kea recovers the address
+back to the available pool.
+
+.. _dhcp4-stats:
+
+Statistics in the DHCPv4 Server
+===============================
+
+The DHCPv4 server supports the following statistics:
+
+.. tabularcolumns:: |p{0.2\linewidth}|p{0.1\linewidth}|p{0.7\linewidth}|
+
+.. table:: DHCPv4 statistics
+ :class: longtable
+ :widths: 20 10 70
+
+
+ +----------------------------------------------------+----------------+------------------------------------+
+ | Statistic | Data Type | Description |
+ +====================================================+================+====================================+
+ | pkt4-received | integer | Number of DHCPv4 packets received. |
+ | | | This includes all packets: valid, |
+ | | | bogus, corrupted, rejected, etc. |
+ | | | This statistic is expected to grow |
+ | | | rapidly. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-discover-received | integer | Number of DHCPDISCOVER packets |
+ | | | received. This statistic is |
+ | | | expected to grow; its increase |
+ | | | means that clients that just |
+ | | | booted started their configuration |
+ | | | process and their initial packets |
+ | | | reached the Kea server. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-offer-received | integer | Number of DHCPOFFER packets |
+ | | | received. This statistic is |
+ | | | expected to remain zero at all |
+ | | | times, as DHCPOFFER packets are |
+ | | | sent by the server and the server |
+ | | | is never expected to receive them. |
+ | | | A non-zero value indicates an |
+ | | | error. One likely cause would be a |
+ | | | misbehaving relay agent that |
+ | | | incorrectly forwards DHCPOFFER |
+ | | | messages towards the server, |
+ | | | rather than back to the clients. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-request-received | integer | Number of DHCPREQUEST packets |
+ | | | received. This statistic is |
+ | | | expected to grow. Its increase |
+ | | | means that clients that just |
+ | | | booted received the server's |
+ | | | response (DHCPOFFER) and accepted |
+ | | | it, and are now requesting an |
+ | | | address (DHCPREQUEST). |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-ack-received | integer | Number of DHCPACK packets |
+ | | | received. This statistic is |
+ | | | expected to remain zero at all |
+ | | | times, as DHCPACK packets are sent |
+ | | | by the server and the server is |
+ | | | never expected to receive them. A |
+ | | | non-zero value indicates an error. |
+ | | | One likely cause would be a |
+ | | | misbehaving relay agent that |
+ | | | incorrectly forwards DHCPACK |
+ | | | messages towards the server, |
+ | | | rather than back to the clients. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-nak-received | integer | Number of DHCPNAK packets |
+ | | | received. This statistic is |
+ | | | expected to remain zero at all |
+ | | | times, as DHCPNAK packets are sent |
+ | | | by the server and the server is |
+ | | | never expected to receive them. A |
+ | | | non-zero value indicates an error. |
+ | | | One likely cause would be a |
+ | | | misbehaving relay agent that |
+ | | | incorrectly forwards DHCPNAK |
+ | | | messages towards the server, |
+ | | | rather than back to the clients. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-release-received | integer | Number of DHCPRELEASE packets |
+ | | | received. This statistic is |
+ | | | expected to grow. Its increase |
+ | | | means that clients that had an |
+ | | | address are shutting down or |
+ | | | ceasing to use their addresses. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-decline-received | integer | Number of DHCPDECLINE packets |
+ | | | received. This statistic is |
+ | | | expected to remain close to zero. |
+ | | | Its increase means that a client |
+ | | | leased an address, but discovered |
+ | | | that the address is currently |
+ | | | used by an unknown device |
+ | | | elsewhere in the network. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-inform-received | integer | Number of DHCPINFORM packets |
+ | | | received. This statistic is |
+ | | | expected to grow. Its increase |
+ | | | means that there are clients |
+ | | | that either do not need an address |
+ | | | or already have an address and are |
+ | | | interested only in getting |
+ | | | additional configuration |
+ | | | parameters. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-unknown-received | integer | Number of packets received of an |
+ | | | unknown type. A non-zero value of |
+ | | | this statistic indicates that the |
+ | | | server received a packet that it |
+ | | | was not able to recognize, either |
+ | | | with an unsupported type or |
+ | | | possibly malformed (without a |
+ | | | message-type option). |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-sent | integer | Number of DHCPv4 packets sent. |
+ | | | This statistic is expected to grow |
+ | | | every time the server transmits a |
+ | | | packet. In general, it should |
+ | | | roughly match pkt4-received, as |
+ | | | most incoming packets cause the |
+ | | | server to respond. There are |
+ | | | exceptions (e.g. DHCPRELEASE), so |
+ | | | do not worry if it is less than |
+ | | | pkt4-received. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-offer-sent | integer | Number of DHCPOFFER packets sent. |
+ | | | This statistic is expected to grow |
+ | | | in most cases after a DHCPDISCOVER |
+ | | | is processed. There are certain |
+ | | | uncommon, but valid, cases where |
+ | | | incoming DHCPDISCOVER packets are |
+ | | | dropped, but in general this |
+ | | | statistic is expected to be close |
+ | | | to pkt4-discover-received. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-ack-sent | integer | Number of DHCPACK packets sent. |
+ | | | This statistic is expected to grow |
+ | | | in most cases after a DHCPREQUEST |
+ | | | is processed. There are certain |
+ | | | cases where DHCPNAK is sent |
+ | | | instead. In general, the sum of |
+ | | | pkt4-ack-sent and pkt4-nak-sent |
+ | | | should be close to |
+ | | | pkt4-request-received. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-nak-sent | integer | Number of DHCPNAK packets sent. |
+ | | | This statistic is expected to grow |
+ | | | when the server chooses not to |
+ | | | honor the address requested by a |
+ | | | client. In general, the sum of |
+ | | | pkt4-ack-sent and pkt4-nak-sent |
+ | | | should be close to |
+ | | | pkt4-request-received. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-parse-failed | integer | Number of incoming packets that |
+ | | | could not be parsed. A non-zero |
+ | | | value of this statistic indicates |
+ | | | that the server received a |
+ | | | malformed or truncated packet. |
+ | | | This may indicate problems in the |
+ | | | network, faulty clients, or a bug |
+ | | | in the server. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-receive-drop | integer | Number of incoming packets that |
+ | | | were dropped. The exact reason for |
+ | | | dropping packets is logged, but |
+ | | | the most common reasons may be: an |
+ | | | unacceptable packet type was |
+ | | | received, direct responses are |
+ | | | forbidden, or the server-id sent |
+ | | | by the client does not match the |
+ | | | server's server-id. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].total-addresses | integer | Total number of addresses |
+ | | | available for DHCPv4 management |
+ | | | for a given subnet; in other |
+ | | | words, this is the count of all |
+ | | | addresses in all configured pools. |
+ | | | This statistic changes only during |
+ | | | configuration updates. It does not |
+ | | | take into account any addresses |
+ | | | that may be reserved due to host |
+ | | | reservation. The *id* is the |
+ | | | the subnet-id of a given subnet. |
+ | | | This statistic is exposed for each |
+ | | | subnet separately, and is reset |
+ | | | during a reconfiguration event. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pool[pid].total-addresses | integer | Total number of addresses |
+ | | | available for DHCPv4 management |
+ | | | for a given subnet pool; in other |
+ | | | words, this is the count of all |
+ | | | addresses in configured subnet |
+ | | | pool. This statistic changes only |
+ | | | during configuration updates. It |
+ | | | does not take into account any |
+ | | | addresses that may be reserved due |
+ | | | to host reservation. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | The *pid* is the pool-id of a |
+ | | | given pool. This statistic is |
+ | | | exposed for each subnet pool |
+ | | | separately, and is reset during a |
+ | | | reconfiguration event. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | cumulative-assigned-addresses | integer | Cumulative number of addresses |
+ | | | that have been assigned since |
+ | | | server startup. It is incremented |
+ | | | each time an address is assigned |
+ | | | and is not reset when the server |
+ | | | is reconfigured. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].cumulative-assigned-addresses | integer | Cumulative number of assigned |
+ | | | addresses in a given subnet. It |
+ | | | increases every time a new lease |
+ | | | is allocated (as a result of |
+ | | | receiving a DHCPREQUEST message) |
+ | | | and never decreases. The *id* is |
+ | | | the subnet-id of the subnet. This |
+ | | | statistic is exposed for each |
+ | | | subnet separately, and is reset |
+ | | | during a reconfiguration event. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pool[pid].cumulative-assigned-addresses | integer | Cumulative number of assigned |
+ | | | addresses in a given subnet pool. |
+ | | | It increases every time a new |
+ | | | lease is allocated (as a result of |
+ | | | receiving a DHCPREQUEST message) |
+ | | | and never decreases. The *id* is |
+ | | | the subnet-id of the subnet. The |
+ | | | *pid* is the pool-id of the pool. |
+ | | | This statistic is exposed for each |
+ | | | subnet pool separately, and is |
+ | | | reset during a reconfiguration |
+ | | | event. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].assigned-addresses | integer | Number of assigned addresses in a |
+ | | | given subnet. It increases every |
+ | | | time a new lease is allocated (as |
+ | | | a result of receiving a |
+ | | | DHCPREQUEST message) and decreases |
+ | | | every time a lease is released (a |
+ | | | DHCPRELEASE message is received) |
+ | | | or expires. The *id* is the |
+ | | | subnet-id of the subnet. This |
+ | | | statistic is exposed for each |
+ | | | subnet separately, and is reset |
+ | | | during a reconfiguration event. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pool[pid].assigned-addresses | integer | Number of assigned addresses in a |
+ | | | given subnet pool. It increases |
+ | | | every time a new lease is |
+ | | | allocated (as a result of |
+ | | | receiving a DHCPREQUEST message) |
+ | | | and decreases every time a lease |
+ | | | is released (a DHCPRELEASE message |
+ | | | is received) or expires. The *id* |
+ | | | is the subnet-id of the subnet. |
+ | | | The *pid* is the pool-id of the |
+ | | | pool. This statistic is exposed |
+ | | | for each subnet pool separately, |
+ | | | and is reset during a |
+ | | | reconfiguration event. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | reclaimed-leases | integer | Number of expired leases that have |
+ | | | been reclaimed since server |
+ | | | startup. It is incremented each |
+ | | | time an expired lease is reclaimed |
+ | | | and never decreases. It can be |
+ | | | used as a long-term indicator of |
+ | | | how many actual leases have been |
+ | | | reclaimed. This is a global |
+ | | | statistic that covers all subnets. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].reclaimed-leases | integer | Number of expired leases |
+ | | | associated with a given subnet |
+ | | | that have been reclaimed since |
+ | | | server startup. It is incremented |
+ | | | each time an expired lease is |
+ | | | reclaimed. The *id* is the |
+ | | | subnet-id of a given subnet. This |
+ | | | statistic is exposed for each |
+ | | | subnet separately. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pool[pid].reclaimed-leases | integer | Number of expired leases |
+ | | | associated with a given subnet |
+ | | | pool that have been reclaimed |
+ | | | since server startup. It is |
+ | | | incremented each time an expired |
+ | | | lease is reclaimed. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | The *pid* is the pool-id of the |
+ | | | pool. This statistic is exposed |
+ | | | for each subnet pool separately. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | declined-addresses | integer | Number of IPv4 addresses that are |
+ | | | currently declined; a count of the |
+ | | | number of leases currently |
+ | | | unavailable. Once a lease is |
+ | | | recovered, this statistic is |
+ | | | decreased; ideally, this statistic |
+ | | | should be zero. If this statistic |
+ | | | is non-zero or increasing, a |
+ | | | network administrator should |
+ | | | investigate whether there is a |
+ | | | misbehaving device in the network. |
+ | | | This is a global statistic that |
+ | | | covers all subnets. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].declined-addresses | integer | Number of IPv4 addresses that are |
+ | | | currently declined in a given |
+ | | | subnet; a count of the number of |
+ | | | leases currently unavailable. Once |
+ | | | a lease is recovered, this |
+ | | | statistic is decreased; ideally, |
+ | | | this statistic should be zero. If |
+ | | | this statistic is non-zero or |
+ | | | increasing, a network |
+ | | | administrator should investigate |
+ | | | whether there is a misbehaving |
+ | | | device in the network. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | This statistic is exposed for each |
+ | | | subnet separately. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pool[pid].declined-addresses | integer | Number of IPv4 addresses that are |
+ | | | currently declined in a given |
+ | | | subnet pool; a count of the number |
+ | | | of leases currently unavailable. |
+ | | | Once a lease is recovered, this |
+ | | | statistic is decreased; ideally, |
+ | | | this statistic should be zero. If |
+ | | | this statistic is non-zero or |
+ | | | increasing, a network |
+ | | | administrator should investigate |
+ | | | whether there is a misbehaving |
+ | | | device in the network. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | The *pid* is the pool-id of the |
+ | | | pool. This statistic is exposed |
+ | | | for each subnet pool separately. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | reclaimed-declined-addresses | integer | Number of IPv4 addresses that were |
+ | | | declined, but have now been |
+ | | | recovered. Unlike |
+ | | | declined-addresses, this statistic |
+ | | | never decreases. It can be used as |
+ | | | a long-term indicator of how many |
+ | | | actual valid declines were |
+ | | | processed and recovered from. This |
+ | | | is a global statistic that covers |
+ | | | all subnets. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].reclaimed-declined-addresses | integer | Number of IPv4 addresses that were |
+ | | | declined, but have now been |
+ | | | recovered. Unlike |
+ | | | declined-addresses, this statistic |
+ | | | never decreases. It can be used as |
+ | | | a long-term indicator of how many |
+ | | | actual valid declines were |
+ | | | processed and recovered from. The |
+ | | | *id* is the subnet-id of a given |
+ | | | subnet. This statistic is exposed |
+ | | | for each subnet separately. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pool[pid].reclaimed-declined-addresses | integer | Number of IPv4 addresses that were |
+ | | | declined, but have now been |
+ | | | recovered. Unlike |
+ | | | declined-addresses, this statistic |
+ | | | never decreases. It can be used as |
+ | | | a long-term indicator of how many |
+ | | | actual valid declines were |
+ | | | processed and recovered from. The |
+ | | | *id* is the subnet-id of a given |
+ | | | subnet. The *pid* is the pool-id |
+ | | | of the pool. This statistic is |
+ | | | exposed for each subnet pool |
+ | | | separately. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-lease-query-received | integer | Number of IPv4 DHCPLEASEQUERY |
+ | | | packets received. (Only exists if |
+ | | | Leasequery hook library is |
+ | | | loaded.) |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-lease-query-response-unknown-sent | integer | Number of IPv4 DHCPLEASEUNKNOWN |
+ | | | responses sent. (Only exists if |
+ | | | Leasequery hook library is |
+ | | | loaded.) |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-lease-query-response-unassigned-sent | integer | Number of IPv4 DHCPLEASEUNASSIGNED |
+ | | | responses sent. (Only exists if |
+ | | | Leasequery hook library is |
+ | | | loaded.) |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | pkt4-lease-query-response-active-sent | integer | Number of IPv4 DHCPLEASEACTIVE |
+ | | | responses sent. (Only exists if |
+ | | | Leasequery hook library is |
+ | | | loaded.) |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | v4-allocation-fail | integer | Number of total address allocation |
+ | | | failures for a particular client. |
+ | | | This consists in the number of |
+ | | | lease allocation attempts that the |
+ | | | server made before giving up and |
+ | | | was unable to use any of the |
+ | | | address pools. This is a global |
+ | | | statistic that covers all subnets. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].v4-allocation-fail | integer | Number of total address allocation |
+ | | | failures for a particular client. |
+ | | | This consists in the number of |
+ | | | lease allocation attempts that the |
+ | | | server made before giving up and |
+ | | | was unable to use any of the |
+ | | | address pools. The *id* is the |
+ | | | subnet-id of a given subnet. This |
+ | | | statistic is exposed for each |
+ | | | subnet separately. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | v4-allocation-fail-shared-network | integer | Number of address allocation |
+ | | | failures for a particular client |
+ | | | connected to a shared network. |
+ | | | This is a global statistic that |
+ | | | covers all subnets. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].v4-allocation-fail-shared-network | integer | Number of address allocation |
+ | | | failures for a particular client |
+ | | | connected to a shared network. |
+ | | | The *id* is the subnet-id of a |
+ | | | given subnet. This statistic is |
+ | | | exposed for each subnet |
+ | | | separately. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | v4-allocation-fail-subnet | integer | Number of address allocation |
+ | | | failures for a particular client |
+ | | | connected to a subnet that does |
+ | | | not belong to a shared network. |
+ | | | This is a global statistic that |
+ | | | covers all subnets. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].v4-allocation-fail-subnet | integer | Number of address allocation |
+ | | | failures for a particular client |
+ | | | connected to a subnet that does |
+ | | | not belong to a shared network. |
+ | | | The *id* is the subnet-id of a |
+ | | | given subnet. This statistic is |
+ | | | exposed for each subnet |
+ | | | separately. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | v4-allocation-fail-no-pools | integer | Number of address allocation |
+ | | | failures because the server could |
+ | | | not use any configured pools for |
+ | | | a particular client. It is also |
+ | | | possible that all of the subnets |
+ | | | from which the server attempted to |
+ | | | assign an address lack address |
+ | | | pools. In this case, it should be |
+ | | | considered misconfiguration if an |
+ | | | operator expects that some clients |
+ | | | should be assigned dynamic |
+ | | | addresses. This is a global |
+ | | | statistic that covers all subnets. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].v4-allocation-fail-no-pools | integer | Number of address allocation |
+ | | | failures because the server could |
+ | | | not use any configured pools for |
+ | | | a particular client. It is also |
+ | | | possible that all of the subnets |
+ | | | from which the server attempted to |
+ | | | assign an address lack address |
+ | | | pools. In this case, it should be |
+ | | | considered misconfiguration if an |
+ | | | operator expects that some clients |
+ | | | should be assigned dynamic |
+ | | | addresses. The *id* is the |
+ | | | subnet-id of a given subnet. This |
+ | | | statistic is exposed for each |
+ | | | subnet separately. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | v4-allocation-fail-classes | integer | Number of address allocation |
+ | | | failures when the client's packet |
+ | | | belongs to one or more classes. |
+ | | | There may be several reasons why a |
+ | | | lease was not assigned. One of |
+ | | | them may be a case when all pools |
+ | | | require packet to belong to |
+ | | | certain classes and the incoming |
+ | | | packet didn't belong to any of |
+ | | | them. Another case where this |
+ | | | information may be useful is to |
+ | | | point out that the pool reserved |
+ | | | to a given class has ran out of |
+ | | | addresses. This is a global |
+ | | | statistic that covers all subnets. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].v4-allocation-fail-classes | integer | Number of address allocation |
+ | | | failures when the client's packet |
+ | | | belongs to one or more classes. |
+ | | | There may be several reasons why a |
+ | | | lease was not assigned. One of |
+ | | | them may be a case when all pools |
+ | | | require packet to belong to |
+ | | | certain classes and the incoming |
+ | | | packet didn't belong to any of |
+ | | | them. Another case where this |
+ | | | information may be useful is to |
+ | | | point out that the pool reserved |
+ | | | to a given class has ran out of |
+ | | | addresses. The *id* is the |
+ | | | subnet-id of a given subnet. This |
+ | | | statistic is exposed for each |
+ | | | subnet separately. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | v4-lease-reuses | integer | Number of times an IPv4 lease had |
+ | | | its CLTT increased in memory and |
+ | | | its expiration time left unchanged |
+ | | | in persistent storage as part of |
+ | | | the lease caching feature. This is |
+ | | | referred to as a lease reuse. |
+ | | | This statistic is global. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].v4-lease-reuses | integer | Number of times an IPv4 lease had |
+ | | | its CLTT increased in memory and |
+ | | | its expiration time left unchanged |
+ | | | in persistent storage as part of |
+ | | | the lease caching feature. This is |
+ | | | referred to as a lease reuse. |
+ | | | This statistic is on a per-subnet |
+ | | | basis. The *id* is the subnet-id |
+ | | | of a given subnet. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | v4-reservation-conflicts | integer | Number of host reservation |
+ | | | allocation conflicts which have |
+ | | | occurred across every subnet. When |
+ | | | a client sends a DHCP Discover and |
+ | | | is matched to a host reservation |
+ | | | which is already leased to another |
+ | | | client, this counter is increased |
+ | | | by 1. |
+ +----------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].v4-reservation-conflicts | integer | Number of host reservation |
+ | | | allocation conflicts which have |
+ | | | occurred in a specific subnet. |
+ | | | When a client sends a DHCP |
+ | | | Discover and is matched to a host |
+ | | | reservation which is already |
+ | | | leased to another client, this |
+ | | | counter is increased by 1. |
+ +----------------------------------------------------+----------------+------------------------------------+
+
+.. note::
+
+ The pool ID can be configured on each pool by explicitly setting the ``pool-id``
+ parameter in the pool parameter map. If not configured, ``pool-id`` defaults to 0.
+ The statistics related to pool ID 0 refer to all the statistics of all the pools
+ that have unconfigured ``pool-id``.
+ The pool ID does not need to be unique within the subnet or across subnets.
+ The statistics regarding a specific pool ID within a subnet will be combined with the
+ other statistics of all other pools with the same pool ID in the respective subnet.
+
+.. note::
+
+ This section describes DHCPv4-specific statistics. For a general
+ overview and usage of statistics, see :ref:`stats`.
+
+The DHCPv4 server provides two global parameters to control the default sample
+limits of statistics:
+
+- ``statistic-default-sample-count`` - determines the default maximum
+ number of samples which are kept. The special value of 0
+ indicates that a default maximum age should be used.
+
+- ``statistic-default-sample-age`` - determines the default maximum
+ age in seconds of samples which are kept.
+
+For instance, to reduce the statistic-keeping overhead, set
+the default maximum sample count to 1 so only one sample is kept:
+
+::
+
+ "Dhcp4": {
+ "statistic-default-sample-count": 1,
+ "subnet4": [
+ {
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+Statistics can be retrieved periodically to gain more insight into Kea operations. One tool that
+leverages that capability is ISC Stork. See :ref:`stork` for details.
+
+
+.. _dhcp4-ctrl-channel:
+
+Management API for the DHCPv4 Server
+====================================
+
+The management API allows the issuing of specific management commands,
+such as statistics retrieval, reconfiguration, or shutdown. For more
+details, see :ref:`ctrl-channel`. Currently, the only supported
+communication channel type is the UNIX stream socket. By default there are
+no sockets open; to instruct Kea to open a socket, the following entry
+in the configuration file can be used:
+
+::
+
+ "Dhcp4": {
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/path/to/the/unix/socket"
+ },
+
+ "subnet4": [
+ {
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+The length of the path specified by the ``socket-name`` parameter is
+restricted by the maximum length for the UNIX socket name on the administrator's
+operating system, i.e. the size of the ``sun_path`` field in the
+``sockaddr_un`` structure, decreased by 1. This value varies on
+different operating systems, between 91 and 107 characters. Typical
+values are 107 on Linux and 103 on FreeBSD.
+
+Communication over the control channel is conducted using JSON
+structures. See the
+`Control Channel section in the Kea Developer's Guide
+<https://reports.kea.isc.org/dev_guide/d2/d96/ctrlSocket.html>`__
+for more details.
+
+The DHCPv4 server supports the following operational commands:
+
+- :isccmd:`build-report`
+- :isccmd:`config-get`
+- :isccmd:`config-hash-get`
+- :isccmd:`config-reload`
+- :isccmd:`config-set`
+- :isccmd:`config-test`
+- :isccmd:`config-write`
+- :isccmd:`dhcp-disable`
+- :isccmd:`dhcp-enable`
+- :isccmd:`leases-reclaim`
+- :isccmd:`list-commands`
+- :isccmd:`shutdown`
+- :isccmd:`status-get`
+- :isccmd:`version-get`
+
+as described in :ref:`commands-common`. In addition, it supports the
+following statistics-related commands:
+
+- :isccmd:`statistic-get`
+- :isccmd:`statistic-reset`
+- :isccmd:`statistic-remove`
+- :isccmd:`statistic-get`-all
+- :isccmd:`statistic-reset`-all
+- :isccmd:`statistic-remove`-all
+- :isccmd:`statistic-sample-age-set`
+- :isccmd:`statistic-sample-age-set`-all
+- :isccmd:`statistic-sample-count-set`
+- :isccmd:`statistic-sample-count-set`-all
+
+as described in :ref:`command-stats`.
+
+.. _dhcp4-user-contexts:
+
+User Contexts in IPv4
+=====================
+
+Kea allows the loading of hook libraries that can sometimes benefit from
+additional parameters. If such a parameter is specific to the whole
+library, it is typically defined as a parameter for the hook library.
+However, sometimes there is a need to specify parameters that are
+different for each pool.
+
+See :ref:`user-context` for additional background regarding the
+user-context idea. See :ref:`user-context-hooks` for a discussion from the
+hooks perspective.
+
+User contexts can be specified at global scope; at the shared-network, subnet,
+pool, client-class, option-data, or definition level; and via host
+reservation. One other useful feature is the ability to store comments or
+descriptions.
+
+Let's consider an imaginary case of devices that have colored LED lights.
+Depending on their location, they should glow red, blue, or green. It
+would be easy to write a hook library that would send specific values,
+maybe as a vendor option. However, the server has to have some way to
+specify that value for each pool. This need is addressed by user
+contexts. In essence, any user data can be specified in the user context
+as long as it is a valid JSON map. For example, the aforementioned case
+of LED devices could be configured in the following way:
+
+::
+
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "pools": [
+ {
+ "pool": "192.0.2.10 - 192.0.2.20",
+ # This is pool specific user context
+ "user-context": { "color": "red" }
+ }
+ ],
+
+ # This is a subnet-specific user context. Any type
+ # of information can be entered here as long as it is valid JSON.
+ "user-context": {
+ "comment": "network on the second floor",
+ "last-modified": "2017-09-04 13:32",
+ "description": "you can put anything you like here",
+ "phones": [ "x1234", "x2345" ],
+ "devices-registered": 42,
+ "billing": false
+ }
+ }
+ ]
+ }
+
+Kea does not interpret or use the user-context information; it simply
+stores it and makes it available to the hook libraries. It is up to each
+hook library to extract that information and use it. The parser
+translates a ``comment`` entry into a user context with the entry, which
+allows a comment to be attached inside the configuration itself.
+
+
+.. _dhcp4-std:
+
+Supported DHCP Standards
+========================
+
+The following standards are currently supported in Kea:
+
+- *BOOTP Vendor Information Extensions*, `RFC 1497
+ <https://tools.ietf.org/html/rfc1497>`__: This requires the open source
+ BOOTP hook to be loaded. See :ref:`hooks-bootp` for details.
+
+- *Dynamic Host Configuration Protocol*, `RFC 2131
+ <https://tools.ietf.org/html/rfc2131>`__: Supported messages are
+ DHCPDISCOVER (1), DHCPOFFER (2), DHCPREQUEST (3), DHCPRELEASE (7),
+ DHCPINFORM (8), DHCPACK (5), and DHCPNAK(6).
+
+- *DHCP Options and BOOTP Vendor Extensions*, `RFC 2132
+ <https://tools.ietf.org/html/rfc2132>`__: Supported options are PAD (0),
+ END(255), Message Type(53), DHCP Server Identifier (54), Domain Name (15),
+ DNS Servers (6), IP Address Lease Time (51), Subnet Mask (1), and Routers (3).
+
+- *The IPv4 Subnet Selection Option for DHCP*, `RFC 3011
+ <https://tools.ietf.org/html/rfc3011>`__: The subnet-selection option is
+ supported; if received in a packet, it is used in the subnet-selection
+ process.
+
+- *DHCP Relay Agent Information Option*, `RFC 3046
+ <https://tools.ietf.org/html/rfc3046>`__: Relay Agent Information,
+ Circuit ID, and Remote ID options are supported.
+
+- *Link Selection sub-option for the Relay Agent Option*, `RFC 3527
+ <https://tools.ietf.org/html/rfc3527>`__: The link selection sub-option
+ is supported.
+
+- *Vendor-Identifying Vendor Options for Dynamic Host Configuration
+ Protocol version 4*, `RFC 3925
+ <https://tools.ietf.org/html/rfc3925>`__: The Vendor-Identifying Vendor Class
+ and Vendor-Identifying Vendor-Specific Information options are supported.
+
+- *Subscriber-ID Suboption for the DHCP Relay Agent Option*, `RFC 3993
+ <https://tools.ietf.org/html/rfc3993>`__: The Subscriber-ID option is
+ supported.
+
+- *The Dynamic Host Configuration Protocol (DHCP) Client Fully
+ Qualified Domain Name (FQDN) Option*, `RFC 4702
+ <https://tools.ietf.org/html/rfc4702>`__: The Kea server is able to handle
+ the Client FQDN option. Also, it is able to use the :iscman:`kea-dhcp-ddns`
+ component to initiate appropriate DNS Update operations.
+
+- *Resolution of Fully Qualified Domain Name (FQDN) Conflicts among Dynamic
+ Host Configuration Protocol (DHCP) Clients*, `RFC 4703
+ <https://tools.ietf.org/html/rfc4703>`__: The DHCPv6 server uses a DHCP-DDNS
+ server to resolve conflicts.
+
+- *Server Identifier Override sub-option for the Relay Agent Option*, `RFC 5107
+ <https://tools.ietf.org/html/rfc5107>`__: The server identifier override
+ sub-option is supported. The implementation is not complete according to the
+ RFC, because the server does not store the RAI, but the functionality handles
+ expected use cases.
+
+- *Client Identifier Option in DHCP Server Replies*, `RFC 6842
+ <https://tools.ietf.org/html/rfc6842>`__: The server by default sends back
+ the ``client-id`` option. That capability can be disabled. See
+ :ref:`dhcp4-echo-client-id` for details.
+
+- *Generalized UDP Source Port for the DHCP Relay Agent Option*, `RFC 8357
+ <https://tools.ietf.org/html/rfc8357>`__: The Kea server handles the Relay
+ Agent Information Source Port sub-option in a received message, remembers the
+ UDP port, and sends back a reply to the same relay agent using this UDP port.
+
+- *Captive-Portal Identification in DHCP and Router Advertisements (RAs)*, `RFC
+ 8910 <https://tools.ietf.org/html/rfc8910>`__: The Kea server can configure
+ both v4 and v6 versions of the captive portal options.
+
+- *IPv6-Only Preferred Option for DHCPv4*, `RFC 8925
+ <https://tools.ietf.org/html/rfc8925>`__: The Kea server is able to designate
+ its pools and subnets as IPv6-Only Preferred and send back the
+ ``v6-only-preferred`` option to clients that requested it.
+
+- *DHCP and Router Advertisement Options for the Discovery of Network-designated
+ Resolvers (DNR)*, `RFC 9463 <https://tools.ietf.org/html/rfc9463>`__. The Kea server
+ supports the DNR option. Part of its value (SvcParams) must be configured in
+ hex.
+
+Known RFC Violations
+--------------------
+
+In principle, Kea aspires to be a reference implementation and aims to implement 100% of the RFC standards.
+However, in some cases there are practical aspects that prevent Kea from completely adhering to the text of the RFC documents.
+
+- `RFC 2131 <https://tools.ietf.org/html/rfc2131>`__, page 30, says that if the incoming DHCPREQUEST packet has no
+ "requested IP address" option and ``ciaddr`` is not set, the server is supposed to respond with NAK. However,
+ broken clients exist that will always send a DHCPREQUEST without those options indicated. In that event, Kea accepts the DHCPREQUEST,
+ assigns an address, and responds with an ACK.
+
+- `RFC 2131 <https://tools.ietf.org/html/rfc2131>`__, table 5, says that messages
+ of type DHCPDECLINE or DHCPRELEASE must have the server identifier set and
+ should be dropped if that option is missing. However, ISC DHCP does not enforce this, presumably as a compatibility
+ effort for broken clients, and the Kea team decided to follow suit.
+
+.. _dhcp4-limit:
+
+DHCPv4 Server Limitations
+=========================
+
+These are the current known limitations of the Kea DHCPv4 server software. Most of
+them are reflections of the current stage of development and should be
+treated as “not implemented yet”, rather than as actual limitations.
+However, some of them are implications of the design choices made. Those
+are clearly marked as such.
+
+- On the Linux and BSD system families, DHCP messages are sent and
+ received over raw sockets (using LPF and BPF) and all packet
+ headers (including data link layer, IP, and UDP headers) are created
+ and parsed by Kea, rather than by the system kernel. Currently, Kea
+ can only parse the data-link layer headers with a format adhering to
+ the IEEE 802.3 standard, and assumes this data-link-layer header
+ format for all interfaces. Thus, Kea does not work on interfaces
+ which use different data-link-layer header formats (e.g. Infiniband).
+
+.. _dhcp4-srv-examples:
+
+Kea DHCPv4 Server Examples
+==========================
+
+A collection of simple-to-use examples for the DHCPv4 component of Kea
+is available with the source files, located in the ``doc/examples/kea4``
+directory.
+
+.. _dhcp4-cb:
+
+Configuration Backend in DHCPv4
+===============================
+
+In the :ref:`config-backend` section we have described the Configuration
+Backend (CB) feature, its applicability, and its limitations. This section focuses
+on the usage of the CB with the Kea DHCPv4 server. It lists the supported
+parameters, describes limitations, and gives examples of DHCPv4
+server configurations to take advantage of the CB. Please also refer to
+the corresponding section :ref:`dhcp6-cb` for DHCPv6-specific usage of
+the CB.
+
+.. _dhcp4-cb-parameters:
+
+Supported Parameters
+--------------------
+
+The ultimate goal for the CB is to serve as a central configuration
+repository for one or multiple Kea servers connected to a database.
+In currently supported Kea versions, only a subset of
+the DHCPv4 server parameters can be configured in the database. All other
+parameters must be specified in the JSON configuration file, if
+required.
+
+All supported parameters can be configured via :ischooklib:`libdhcp_cb_cmds.so`.
+The general rule is that
+scalar global parameters are set using
+:isccmd:`remote-global-parameter4-set`; shared-network-specific parameters
+are set using :isccmd:`remote-network4-set`; and subnet-level and pool-level
+parameters are set using :isccmd:`remote-subnet4-set`. Whenever
+there is an exception to this general rule, it is highlighted in the
+table. Non-scalar global parameters have dedicated commands; for example,
+the global DHCPv4 options (``option-data``) are modified using
+:isccmd:`remote-option4-global-set`. Client classes, together with class-specific
+option definitions and DHCPv4 options, are configured using the
+:isccmd:`remote-class4-set` command.
+
+The :ref:`cb-sharing` section explains the concept of shareable
+and non-shareable configuration elements and the limitations for
+sharing them between multiple servers. In the DHCP configuration (both DHCPv4
+and DHCPv6), the shareable configuration elements are subnets and shared
+networks. Thus, they can be explicitly associated with multiple server tags.
+The global parameters, option definitions, and global options are non-shareable
+and can be associated with only one server tag. This rule does not apply
+to the configuration elements associated with ``all`` servers. Any configuration
+element associated with ``all`` servers (using the ``all`` keyword as a server tag) is
+used by all servers connecting to the configuration database.
+
+The following table lists DHCPv4-specific parameters supported by the
+Configuration Backend, with an indication of the level of the hierarchy
+at which it is currently supported.
+
+.. table:: List of DHCPv4 parameters supported by the Configuration Backend
+
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | Parameter | Global | Client | Shared | Subnet | Pool |
+ | | | Class | Network | | |
+ +=============================+============================+==============+=============+=============+=============+
+ | 4o6-interface | n/a | n/a | n/a | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | 4o6-interface-id | n/a | n/a | n/a | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | 4o6-subnet | n/a | n/a | n/a | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | allocator | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | boot-file-name | yes | yes | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | cache-max-age | yes | n/a | no | no | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | cache-threshold | yes | n/a | no | no | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | calculate-tee-times | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | client-class | n/a | n/a | yes | yes | yes |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | ddns-send-update | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | ddns-override-no-update | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | ddns-override-client-update | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | ddns-replace-client-name | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | ddns-generated-prefix | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | ddns-qualifying-suffix | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | decline-probation-period | yes | n/a | n/a | n/a | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | dhcp4o6-port | yes | n/a | n/a | n/a | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | echo-client-id | yes | n/a | n/a | n/a | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | hostname-char-set | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | hostname-char-replacement | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | interface | n/a | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | match-client-id | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | min-valid-lifetime | yes | yes | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | max-valid-lifetime | yes | yes | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | next-server | yes | yes | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | option-data | yes (via | yes | yes | yes | yes |
+ | | remote-option4-global-set) | | | | |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | option-def | yes (via | yes | n/a | n/a | n/a |
+ | | remote-option-def4-set) | | | | |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | rebind-timer | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | renew-timer | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | server-hostname | yes | yes | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | valid-lifetime | yes | yes | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | relay | n/a | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | require-client-classes | no | n/a | yes | yes | yes |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | reservation-mode | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | reservations-global | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | reservations-in-subnet | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | reservations-out-of-pool | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | t1-percent | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+ | t2-percent | yes | n/a | yes | yes | n/a |
+ +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+
+
+- ``yes`` - indicates that the parameter is supported at the given
+ level of the hierarchy and can be configured via the Configuration Backend.
+
+- ``no`` - indicates that a parameter is supported at the given level
+ of the hierarchy but cannot be configured via the Configuration Backend.
+
+- ``n/a`` - indicates that a given parameter is not applicable
+ at the particular level of the hierarchy or that the
+ server does not support the parameter at that level.
+
+Some scalar parameters contained by top level global maps are supported by the Configuration Backend.
+
+.. table:: List of DHCPv4 map parameters supported by the Configuration Backend
+
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | Parameter name (flat naming format) | Global map | Parameter name |
+ +==================================================================+==============================+==================================+
+ | compatibility.ignore-dhcp-server-identifier | compatibility | ignore-dhcp-server-identifier |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | compatibility.ignore-rai-link-selection | compatibility | ignore-rai-link-selection |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | compatibility.lenient-option-parsing | compatibility | lenient-option-parsing |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | compatibility.exclude-first-last-24 | compatibility | exclude-first-last-24 |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | control-socket.socket-name | control-socket | socket-name |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | control-socket.socket-type | control-socket | socket-type |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.enable-updates | dhcp-ddns | enable-updates |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.max-queue-size | dhcp-ddns | max-queue-size |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.ncr-format | dhcp-ddns | ncr-format |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.ncr-protocol | dhcp-ddns | ncr-protocol |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.sender-ip | dhcp-ddns | sender-ip |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.sender-port | dhcp-ddns | sender-port |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.server-ip | dhcp-ddns | server-ip |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.server-port | dhcp-ddns | server-port |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.generated-prefix | dhcp-ddns | generated-prefix |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.hostname-char-replacement | dhcp-ddns | hostname-char-replacement |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.hostname-char-set | dhcp-ddns | hostname-char-set |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.override-client-update | dhcp-ddns | override-client-update |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.override-no-update | dhcp-ddns | override-no-update |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.qualifying-suffix | dhcp-ddns | qualifying-suffix |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.replace-client-name | dhcp-ddns | replace-client-name |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | expired-leases-processing.flush-reclaimed-timer-wait-time | expired-leases-processing | flush-reclaimed-timer-wait-time |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | expired-leases-processing.hold-reclaimed-time | expired-leases-processing | hold-reclaimed-time |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | expired-leases-processing.max-reclaim-leases | expired-leases-processing | max-reclaim-leases |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | expired-leases-processing.max-reclaim-time | expired-leases-processing | max-reclaim-time |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | expired-leases-processing.reclaim-timer-wait-time | expired-leases-processing | reclaim-timer-wait-time |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | expired-leases-processing.unwarned-reclaim-cycles | expired-leases-processing | unwarned-reclaim-cycles |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | multi-threading.enable-multi-threading | multi-threading | enable-multi-threading |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | multi-threading.thread-pool-size | multi-threading | thread-pool-size |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | multi-threading.packet-queue-size | multi-threading | packet-queue-size |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | sanity-checks.lease-checks | sanity-checks | lease-checks |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | sanity-checks.extended-info-checks | sanity-checks | extended-info-checks |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-queue-control.enable-queue | dhcp-queue-control | enable-queue |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-queue-control.queue-type | dhcp-queue-control | queue-type |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-queue-control.capacity | dhcp-queue-control | capacity |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+
+.. _dhcp4-cb-json:
+
+Enabling the Configuration Backend
+----------------------------------
+
+Consider the following configuration snippet, which uses a MySQL configuration
+database:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "server-tag": "my DHCPv4 server",
+ "config-control": {
+ "config-databases": [
+ {
+ "type": "mysql",
+ "name": "kea",
+ "user": "kea",
+ "password": "kea",
+ "host": "192.0.2.1",
+ "port": 3302
+ }
+ ],
+ "config-fetch-wait-time": 20
+ },
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/kea/hooks/libdhcp_mysql_cb.so"
+ }, {
+ "library": "/usr/local/lib/kea/hooks/libdhcp_cb_cmds.so"
+ }
+ ]
+ }
+ }
+
+The ``config-control`` map contains two parameters. ``config-databases``
+is a list that contains one element, which includes the database type, its location,
+and the credentials to be used to connect to this database. (Note that
+the parameters specified here correspond to the database specification
+for the lease database backend and hosts database backend.) Currently
+only one database connection can be specified on the
+``config-databases`` list. The server connects to this database
+during startup or reconfiguration, and fetches the configuration
+available for this server from the database. This configuration is
+merged into the configuration read from the configuration file.
+
+The following snippet illustrates the use of a PostgreSQL database:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "server-tag": "my DHCPv4 server",
+ "config-control": {
+ "config-databases": [
+ {
+ "type": "postgresql",
+ "name": "kea",
+ "user": "kea",
+ "password": "kea",
+ "host": "192.0.2.1",
+ "port": 5432
+ }
+ ],
+ "config-fetch-wait-time": 20
+ },
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/kea/hooks/libdhcp_pgsql_cb.so"
+ }, {
+ "library": "/usr/local/lib/kea/hooks/libdhcp_cb_cmds.so"
+ }
+ ]
+ }
+ }
+
+.. note::
+
+ Whenever there is a conflict between the parameters specified in the
+ configuration file and the database, the parameters from the database
+ take precedence. We strongly recommend avoiding the duplication of
+ parameters in the file and the database, but this recommendation is
+ not enforced by the Kea servers. In particular, if the subnets'
+ configuration is sourced from the database, we recommend that all
+ subnets be specified in the database and that no subnets be specified in
+ the configuration file. It is possible to specify the subnets in both
+ places, but the subnets in the
+ configuration file with overlapping IDs and/or prefixes with the
+ subnets from the database will be superseded by those from the
+ database.
+
+Once the Kea server is configured, it starts periodically polling
+the database for configuration changes. The polling frequency is
+controlled by the ``config-fetch-wait-time`` parameter, expressed
+in seconds; it is the period between the time when the server
+completed its last poll (and possibly the local configuration update) and
+the time when it will begin polling again. In the example above, this period
+is set to 20 seconds. This means that after adding a new configuration
+into the database (e.g. adding a new subnet), it will take up to 20 seconds
+(plus the time needed to fetch and apply the new configuration) before
+the server starts using this subnet. The lower the
+``config-fetch-wait-time`` value, the shorter the time for the server to
+react to incremental configuration updates in the database. On the
+other hand, polling the database too frequently may impact the DHCP
+server's performance, because the server needs to make at least one query
+to the database to discover any pending configuration updates. The
+default value of ``config-fetch-wait-time`` is 30 seconds.
+
+The :isccmd:`config-backend-pull` command can be used to force the server to
+immediately poll any configuration changes from the database and avoid
+waiting for the next fetch cycle.
+
+In the configuration examples above, two hook libraries are loaded. The first
+is a library which implements the Configuration Backend for a specific database
+type: :ischooklib:`libdhcp_mysql_cb.so` provides support for MySQL and :ischooklib:`libdhcp_pgsql_cb.so`
+provides support for PostgreSQL. The library loaded must match the database
+``type`` specified within the ``config-control`` parameter or an will error be
+logged when the server attempts to load its configuration and the load will
+fail.
+
+The second hook library, :ischooklib:`libdhcp_cb_cmds.so`, is optional. It should
+be loaded when the Kea server instance is to be used to manage the
+configuration in the database. See the :ref:`hooks-cb-cmds` section for
+details. This hook library is only available to ISC
+customers with a paid support contract.
+
+.. _dhcp4-compatibility:
+
+Kea DHCPv4 Compatibility Configuration Parameters
+=================================================
+
+ISC's intention is for Kea to follow the RFC documents to promote better standards
+compliance. However, many buggy DHCP implementations already exist that cannot be
+easily fixed or upgraded. Therefore, Kea provides an easy-to-use compatibility
+mode for broken or non-compliant clients. For that purpose, the compatibility option must be
+enabled to permit uncommon practices:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "compatibility": {
+ }
+ }
+ }
+
+
+Lenient Option Parsing
+----------------------
+
+By default, tuple fields defined in custom options are parsed as a set of
+length-value pairs.
+
+With ``"lenient-option-parsing": true``, if a length ever exceeds the rest of
+the option's buffer, previous versions of Kea returned a log message ``unable to
+parse the opaque data tuple, the buffer length is x, but the tuple length is y``
+with ``x < y``; this no longer occurs. Instead, the value is considered to be the rest of the buffer,
+or in terms of the log message above, the tuple length ``y`` becomes ``x``.
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "compatibility": {
+ "lenient-option-parsing": true
+ }
+ }
+ }
+
+Ignore DHCP Server Identifier
+-----------------------------
+
+With ``"ignore-dhcp-server-identifier": true``, the server does not check the
+address in the DHCP Server Identifier option, i.e. whether a query is sent
+to this server or another one (and in the second case dropping the query).
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "compatibility": {
+ "ignore-dhcp-server-identifier": true
+ }
+ }
+ }
+
+
+Ignore RAI Link Selection
+-------------------------
+
+With ``"ignore-rai-link-selection": true``, Relay Agent Information Link
+Selection sub-option data is not used for subnet selection. In this case,
+normal logic drives the subnet selection, instead of attempting to use the subnet specified
+by the sub-option. This option is not RFC-compliant and is set to ``false`` by
+default. Setting this option to ``true`` can help with subnet selection in
+certain scenarios; for example, when DHCP relays do not allow the administrator to
+specify which sub-options are included in the Relay Agent Information option,
+and include incorrect Link Selection information.
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "compatibility": {
+ "ignore-rai-link-selection": true
+ }
+ }
+ }
+
+Exclude First Last Addresses in /24 Subnets or Larger
+-----------------------------------------------------
+
+The ``exclude-first-last-24`` compatibility flag is described in
+:ref:`dhcp4-address-config` (when true .0 and .255 addresses are excluded
+from subnets with prefix length less than or equal to 24).
+
+.. _dhcp4_allocation_strategies:
+
+Address Allocation Strategies in DHCPv4
+=======================================
+
+A DHCP server follows a complicated algorithm to select an IPv4 address for a client.
+It prefers assigning specific addresses requested by the client and the addresses for
+which the client has reservations.
+
+If the client requests no particular address and
+has no reservations, or other clients are already using any requested addresses, the server must
+find another available address within the configured pools. A server function called
+an "allocator" is responsible in Kea for finding an available address in such a case.
+
+The Kea DHCPv4 server provides configuration parameters to select different allocators
+at the global, shared-network, and subnet levels.
+Consider the following example:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "allocator": "random",
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.0.0.0/8",
+ "allocator": "iterative"
+ },
+ {
+ "id": 2,
+ "subnet": "192.0.2.0/24"
+ }
+ ]
+ }
+ }
+
+This allocator overrides the default iterative allocation strategy at the global level and
+selects the random allocation instead. The random allocation will be used
+for the subnet with ID 2, while the iterative allocation will be used for the subnet
+with ID 1.
+
+The following sections describe the supported allocators and their
+recommended uses.
+
+Allocators Comparison
+---------------------
+
+In the table below, we briefly compare the supported allocators. The
+detailed allocators' descriptions are in later sections.
+
+.. table:: Comparison of the lease allocators supported by Kea DHCPv4
+
+ +------------------+-----------------------------+------------------------------+-----------------------+------------------------------+----------------+
+ | Allocator | Low Utilization Performance | High Utilization Performance | Lease Randomization | Startup/Configuration | Memory Usage |
+ +==================+=============================+==============================+=======================+==============================+================+
+ | Iterative | very high | low | no | very fast | low |
+ +------------------+-----------------------------+------------------------------+-----------------------+------------------------------+----------------+
+ | Random | high | low | yes | very fast | high (varying) |
+ +------------------+-----------------------------+------------------------------+-----------------------+------------------------------+----------------+
+ | Free Lease Queue | high | high | yes | slow (depends on pool sizes) | high (varying) |
+ +------------------+-----------------------------+------------------------------+-----------------------+------------------------------+----------------+
+
+
+Iterative Allocator
+-------------------
+This is the default allocator used by the Kea DHCPv4 server. It remembers the
+last offered address and offers this address, increased by one, to the next client.
+For example, it may offer addresses in this order: ``192.0.2.10``, ``192.0.2.11``,
+``192.0.2.12``, and so on. The time to find and offer the next address is very
+short; thus, this is the most performant allocator when pool utilization
+is low and there is a high probability that the next address is available.
+
+The iterative allocation underperforms when multiple DHCP servers share a lease
+database or are connected to a cluster. The servers tend to offer and allocate
+the same blocks of addresses to different clients independently, which causes many
+allocation conflicts between the servers and retransmissions by clients. A random
+allocation addresses this issue by dispersing the allocation order.
+
+Random Allocator
+----------------
+
+The random allocator uses a uniform randomization function to select offered
+addresses from subnet pools. It is suitable in deployments where multiple servers
+are connected to a shared
+database or a database cluster. By dispersing the offered addresses, the servers
+minimize the risk of allocating the same address to two different clients at
+the same or nearly the same time. In addition, it improves the server's resilience against
+attacks based on allocation predictability.
+
+The random allocator is, however, slightly slower than the iterative allocator.
+Moreover, it increases the server's memory consumption because it must remember
+randomized addresses to avoid offering them repeatedly. Memory consumption grows
+with the number of offered addresses; in other words, larger pools and more
+clients increase memory consumption by random allocation.
+
+The following configuration snippet shows how to select the random allocator
+for a subnet:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "allocator": "random",
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "10.0.0.0/8",
+ "allocator": "random"
+ }
+ ]
+ }
+ }
+
+Free Lease Queue Allocator
+--------------------------
+
+This is a sophisticated allocator whose use should be considered in subnets
+with highly utilized address pools. In such cases, it can take a considerable
+amount of time for the iterative or random allocator to find an available
+address, because they must repeatedly check whether there is a valid lease for
+an address they will offer. The number of checks can be as high as the number
+of addresses in the subnet when the subnet pools are exhausted, which can have a
+direct negative impact on the DHCP response time for each request.
+
+The Free Lease Queue (FLQ) allocator tracks lease allocations and de-allocations
+and maintains a running list of available addresses for each address pool.
+It allows an available lease to be selected within a constant time, regardless of
+the subnet pools' utilization. The allocator continuously updates the list of
+free leases by removing any allocated leases and adding released or
+reclaimed ones.
+
+The following configuration snippet shows how to select the FLQ allocator
+for a subnet:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "subnet4": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "allocator": "flq"
+ }
+ ]
+ }
+ }
+
+
+There are several considerations that the administrator should take into account
+before using this allocator. The FLQ allocator can heavily impact the server's
+startup and reconfiguration time, because the allocator has to populate the
+list of free leases for each subnet where it is used. These delays can be
+observed both during the configuration reload and when the subnets are
+created using :ischooklib:`libdhcp_subnet_cmds.so`. The allocator increases the
+memory consumption to hold the list of free leases, proportional
+to the total size of the address pools for which this allocator is used.
+Finally, lease reclamation must be enabled with a low value of the
+``reclaim-timer-wait-time`` parameter, to ensure that the server frequently
+collects expired leases and makes them available for allocation via the
+free lease queue. Expired leases are not considered free by
+the allocator until they are reclaimed by the server. See
+:ref:`lease-reclamation` for more details about the lease reclamation process.
+
+We recommend that the FLQ allocator be selected
+only after careful consideration. For example, using it for a subnet with a
+``/8`` pool may delay the server's startup by 15 seconds or more. On the
+other hand, the startup delay and the memory consumption increase should
+be acceptable for subnets with a ``/16`` pool or smaller. We also recommend
+specifying another allocator type in the global configuration settings
+and overriding this selection at the subnet or shared-network level, to use
+the FLQ allocator only for selected subnets. That way, when a new subnet is
+added without an allocator specification, the global setting is used, thus
+avoiding unnecessary impact on the server's startup time.
+
+Like the random allocator, the FLQ allocator offers leases in
+random order, which makes it suitable for use with a shared lease database.
diff --git a/doc/sphinx/arm/dhcp6-srv.rst b/doc/sphinx/arm/dhcp6-srv.rst
new file mode 100644
index 0000000..f2f64de
--- /dev/null
+++ b/doc/sphinx/arm/dhcp6-srv.rst
@@ -0,0 +1,8184 @@
+.. _dhcp6:
+
+*****************
+The DHCPv6 Server
+*****************
+
+.. _dhcp6-start-stop:
+
+Starting and Stopping the DHCPv6 Server
+=======================================
+
+It is recommended that the Kea DHCPv6 server be started and stopped
+using :iscman:`keactrl` (described in :ref:`keactrl`); however, it is also
+possible to run the server directly via the :iscman:`kea-dhcp6` command, which accepts
+the following command-line switches:
+
+- ``-c file`` - specifies the configuration file. This is the only
+ mandatory switch.
+
+- ``-d`` - specifies whether the server logging should be switched to
+ debug/verbose mode. In verbose mode, the logging severity and debuglevel
+ specified in the configuration file are ignored; "debug" severity
+ and the maximum debuglevel (99) are assumed. The flag is convenient
+ for temporarily switching the server into maximum verbosity, e.g.
+ when debugging.
+
+- ``-p server-port`` - specifies the local UDP port on which the server
+ listens. This is only useful during testing, as a DHCPv6 server
+ listening on ports other than the standard ones is not able to
+ handle regular DHCPv6 queries.
+
+- ``-P client-port`` - specifies the remote UDP port to which the
+ server sends all responses. This is only useful during testing,
+ as a DHCPv6 server sending responses to ports other than the standard
+ ones is not able to handle regular DHCPv6 queries.
+
+- ``-t file`` - specifies a configuration file to be tested. :iscman:`kea-dhcp6`
+ loads it, checks it, and exits. During the test, log messages are
+ printed to standard output and error messages to standard error. The
+ result of the test is reported through the exit code (0 =
+ configuration looks OK, 1 = error encountered). The check is not
+ comprehensive; certain checks are possible only when running the
+ server.
+
+- ``-T file`` - specifies a configuration file to be tested. :iscman:`kea-dhcp6`
+ loads it, checks it, and exits. It performs extra checks beyond what ``-t``
+ offers, such as establishing database connections (for the lease backend,
+ host reservations backend, configuration backend, and forensic logging
+ backend), loading hook libraries, parsing hook-library configurations, etc.
+ It does not open UNIX or TCP/UDP sockets, nor does it open or rotate
+ files, as any of these actions could interfere with a running process on the
+ same machine.
+
+- ``-v`` - displays the Kea version and exits.
+
+- ``-V`` - displays the Kea extended version with additional parameters
+ and exits. The listing includes the versions of the libraries
+ dynamically linked to Kea.
+
+- ``-W`` - displays the Kea configuration report and exits. The report
+ is a copy of the ``config.report`` file produced by ``./configure``;
+ it is embedded in the executable binary.
+
+ The contents of the ``config.report`` file may also be accessed by examining
+ certain libraries in the installation tree or in the source tree.
+
+ .. code-block:: shell
+
+ # from installation using libkea-process.so
+ $ strings ${prefix}/lib/libkea-process.so | sed -n 's/;;;; //p'
+
+ # from sources using libkea-process.so
+ $ strings src/lib/process/.libs/libkea-process.so | sed -n 's/;;;; //p'
+
+ # from sources using libkea-process.a
+ $ strings src/lib/process/.libs/libkea-process.a | sed -n 's/;;;; //p'
+
+ # from sources using libcfgrpt.a
+ $ strings src/lib/process/cfgrpt/.libs/libcfgrpt.a | sed -n 's/;;;; //p'
+
+On startup, the server detects available network interfaces and
+attempts to open UDP sockets on all interfaces listed in the
+configuration file. Since the DHCPv6 server opens privileged ports, it
+requires root access; this daemon must be run as root.
+
+During startup, the server attempts to create a PID file of the
+form: ``[runstatedir]/kea/[conf name].kea-dhcp6.pid``, where:
+
+- ``runstatedir``: The value as passed into the build configure
+ script; it defaults to ``/usr/local/var/run``. Note that this value may be
+ overridden at runtime by setting the environment variable
+ ``KEA_PIDFILE_DIR``, although this is intended primarily for testing
+ purposes.
+
+- ``conf name``: The configuration file name used to start the server,
+ minus all preceding paths and the file extension. For example, given
+ a pathname of ``/usr/local/etc/kea/myconf.txt``, the portion used would
+ be ``myconf``.
+
+If the file already exists and contains the PID of a live process, the
+server issues a ``DHCP6_ALREADY_RUNNING`` log message and exits. It is
+possible, though unlikely, that the file is a remnant of a system crash
+and the process to which the PID belongs is unrelated to Kea. In such a
+case, it would be necessary to manually delete the PID file.
+
+The server can be stopped using the ``kill`` command. When running in a
+console, the server can also be shut down by pressing Ctrl-c. Kea detects
+the key combination and shuts down gracefully.
+
+The reconfiguration of each Kea server is triggered by the SIGHUP signal.
+When a server receives the SIGHUP signal it rereads its configuration file and,
+if the new configuration is valid, uses the new configuration.
+If the new configuration proves to be invalid, the server retains its
+current configuration; however, in some cases a fatal error message is logged
+indicating that the server is no longer providing any service: a working
+configuration must be loaded as soon as possible.
+
+.. _dhcp6-configuration:
+
+DHCPv6 Server Configuration
+===========================
+
+Introduction
+------------
+
+This section explains how to configure the Kea DHCPv6 server using a
+configuration file.
+
+Before DHCPv6 is started, its configuration file must
+be created. The basic configuration is as follows:
+
+::
+
+ {
+ # DHCPv6 configuration starts on the next line
+ "Dhcp6": {
+
+ # First we set up global values
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+ "preferred-lifetime": 3000,
+
+ # Next we set up the interfaces to be used by the server.
+ "interfaces-config": {
+ "interfaces": [ "eth0" ]
+ },
+
+ # And we specify the type of lease database
+ "lease-database": {
+ "type": "memfile",
+ "persist": true,
+ "name": "/var/lib/kea/dhcp6.leases"
+ },
+
+ # Finally, we list the subnets from which we will be leasing addresses.
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "pools": [
+ {
+ "pool": "2001:db8:1::1-2001:db8:1::ffff"
+ }
+ ]
+ }
+ ]
+ # DHCPv6 configuration ends with the next line
+ }
+
+ }
+
+The following paragraphs provide a brief overview of the parameters in
+the above example, along with their format. Subsequent sections of this
+chapter go into much greater detail for these and other parameters.
+
+The lines starting with a hash (#) are comments and are ignored by the
+server; they do not impact its operation in any way.
+
+The configuration starts in the first line with the initial opening
+curly bracket (or brace). Each configuration must contain an object
+specifying the configuration of the Kea module using it. In the example
+above, this object is called ``Dhcp6``.
+
+The ``Dhcp6`` configuration starts with the ``"Dhcp6": {`` line and ends
+with the corresponding closing brace (in the above example, the brace
+after the last comment). Everything defined between those lines is
+considered to be the ``Dhcp6`` configuration.
+
+In general, the order in which those parameters appear does not
+matter, but there are two caveats. The first one is that the
+configuration file must be well-formed JSON, meaning that the
+parameters for any given scope must be separated by a comma, and there
+must not be a comma after the last parameter. When reordering a
+configuration file, moving a parameter to or from the
+last position in a given scope may also require moving the comma. The
+second caveat is that it is uncommon — although legal JSON — to repeat
+the same parameter multiple times. If that happens, the last occurrence
+of a given parameter in a given scope is used, while all previous
+instances are ignored. This is unlikely to cause any confusion as there
+are no real-life reasons to keep multiple copies of the same parameter
+in the configuration file.
+
+The first few DHCPv6 configuration elements
+define some global parameters. ``valid-lifetime`` defines how long the
+addresses (leases) given out by the server are valid; the default
+is for a client to be allowed to use a given address for 4000
+seconds. (Note that integer numbers are specified as is, without any
+quotes around them.) The address will become deprecated in 3000 seconds,
+i.e. clients are allowed to keep old connections, but cannot use this
+address to create new connections. ``renew-timer`` and
+``rebind-timer`` are values (also in seconds) that define T1 and T2 timers, which govern
+when the client begins the renewal and rebind procedures.
+
+The ``interfaces-config`` map specifies the network interfaces on which the
+server should listen to DHCP messages. The ``interfaces`` parameter specifies
+a list of network interfaces on which the server should listen. Lists are
+opened and closed with square brackets, with elements separated by commas. To
+listen on two interfaces, the ``interfaces-config`` element should look like
+this:
+
+::
+
+ {
+ "interfaces-config": {
+ "interfaces": [ "eth0", "eth1" ]
+ },
+ ...
+ }
+
+The next lines define the lease database, the place where the
+server stores its lease information. This particular example tells the
+server to use memfile, which is the simplest and fastest database
+backend. It uses an in-memory database and stores leases on disk in a
+CSV (comma-separated values) file. This is a very simple configuration example;
+usually the lease database configuration is more extensive and contains
+additional parameters. Note that ``lease-database`` is an object and opens up a
+new scope, using an opening brace. Its parameters (just one in this example:
+``type``) follow. If there were more than one, they would be separated
+by commas. This scope is closed with a closing brace. As more parameters
+for the ``Dhcp6`` definition follow, a trailing comma is present.
+
+Finally, we need to define a list of IPv6 subnets. This is the most
+important DHCPv6 configuration structure, as the server uses that
+information to process clients' requests. It defines all subnets from
+which the server is expected to receive DHCP requests. The subnets are
+specified with the ``subnet6`` parameter. It is a list, so it starts and
+ends with square brackets. Each subnet definition in the list has
+several attributes associated with it, so it is a structure and is
+opened and closed with braces. At a minimum, a subnet definition must
+have at least two parameters: ``subnet``, which defines the whole
+subnet; and ``pools``, which is a list of dynamically allocated pools
+that are governed by the DHCP server.
+
+The example contains a single subnet. If more than one were defined,
+additional elements in the ``subnet6`` parameter would be specified and
+separated by commas. For example, to define two subnets, the following
+syntax would be used:
+
+::
+
+ {
+ "subnet6": [
+ {
+ "id": 1,
+ "pools": [ { "pool": "2001:db8:1::/112" } ],
+ "subnet": "2001:db8:1::/64"
+ },
+ {
+ "id": 2,
+ "pools": [ { "pool": "2001:db8:2::1-2001:db8:2::ffff" } ],
+ "subnet": "2001:db8:2::/64"
+ }
+ ],
+ ...
+ }
+
+Note that indentation is optional and is used for aesthetic purposes
+only. In some cases it may be preferable to use more compact notation.
+
+After all the parameters have been specified, there are two contexts open:
+``global`` and ``Dhcp6``; thus, two closing curly brackets must be used to close
+them.
+
+Lease Storage
+-------------
+
+All leases issued by the server are stored in the lease database.
+There are three database backends available: memfile
+(the default), MySQL, PostgreSQL.
+
+Memfile - Basic Storage for Leases
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The server is able to store lease data in different repositories. Larger
+deployments may elect to store leases in a database;
+:ref:`database-configuration6` describes this option. In
+typical smaller deployments, though, the server stores lease
+information in a CSV file rather than a database. As well as requiring
+less administration, an advantage of using a file for storage is that it
+eliminates a dependency on third-party database software.
+
+The configuration of the memfile backend is controlled through
+the ``Dhcp6``/``lease-database`` parameters. The ``type`` parameter is mandatory
+and specifies which storage for leases the server should use, through
+the ``"memfile"`` value. The following list gives additional optional parameters
+that can be used to configure the memfile backend.
+
+- ``persist``: controls whether the new leases and updates to existing
+ leases are written to the file. It is strongly recommended that the
+ value of this parameter be set to ``true`` at all times during the
+ server's normal operation. Not writing leases to disk means that if a
+ server is restarted (e.g. after a power failure), it will not know
+ which addresses have been assigned. As a result, it may assign new clients
+ addresses that are already in use. The value of
+ ``false`` is mostly useful for performance-testing purposes. The
+ default value of the ``persist`` parameter is ``true``, which enables
+ writing lease updates to the lease file.
+
+- ``name``: specifies an absolute location of the lease file in which
+ new leases and lease updates are recorded. The default value for
+ this parameter is ``"[kea-install-dir]/var/lib/kea/kea-leases6.csv"``.
+
+- ``lfc-interval``: specifies the interval, in seconds, at which the
+ server will perform a lease file cleanup (LFC). This removes
+ redundant (historical) information from the lease file and
+ effectively reduces the lease file size. The cleanup process is
+ described in more detail later in this section. The default
+ value of the ``lfc-interval`` is ``3600``. A value of ``0`` disables the LFC.
+
+- ``max-row-errors``: specifies the number of row errors before the server
+ stops attempting to load a lease file. When the server loads a lease file, it is processed
+ row by row, each row containing a single lease. If a row is flawed and
+ cannot be processed correctly the server logs it, discards the row,
+ and goes on to the next row. This parameter can be used to set a limit on
+ the number of such discards that can occur, after which the server
+ abandons the effort and exits. The default value of ``0`` disables the limit
+ and allows the server to process the entire file, regardless of how many
+ rows are discarded.
+
+An example configuration of the memfile backend is presented below:
+
+::
+
+ "Dhcp6": {
+ "lease-database": {
+ "type": "memfile",
+ "persist": true,
+ "name": "/tmp/kea-leases6.csv",
+ "lfc-interval": 1800,
+ "max-row-errors": 100
+ }
+ }
+
+This configuration selects ``/tmp/kea-leases6.csv`` as the storage file
+for lease information and enables persistence (writing lease updates to
+this file). It also configures the backend to perform a periodic cleanup
+of the lease file every 1800 seconds (30 minutes) and sets the maximum number of
+row errors to 100.
+
+Why Is Lease File Cleanup Necessary?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is important to know how the lease file contents are organized to
+understand why the periodic lease file cleanup is needed. Every time the
+server updates a lease or creates a new lease for a client, the new
+lease information must be recorded in the lease file. For performance
+reasons, the server does not update the existing client's lease in the
+file, as this would potentially require rewriting the entire file.
+Instead, it simply appends the new lease information to the end of the
+file; the previous lease entries for the client are not removed. When
+the server loads leases from the lease file, e.g. at server startup,
+it assumes that the latest lease entry for the client is the valid one.
+Previous entries are discarded, meaning that the server can
+reconstruct accurate information about the leases even though there
+may be many lease entries for each client. However, storing many entries
+for each client results in a bloated lease file and impairs the
+performance of the server's startup and reconfiguration, as it needs to
+process a larger number of lease entries.
+
+Lease file cleanup (LFC) removes all previous entries for each client
+and leaves only the latest ones. The interval at which the cleanup is
+performed is configurable, and it should be selected according to the
+frequency of lease renewals initiated by the clients. The more frequent
+the renewals, the smaller the value of ``lfc-interval`` should be. Note,
+however, that the LFC takes time and thus it is possible (although
+unlikely) that, if the ``lfc-interval`` is too short, a new cleanup may
+be started while the previous one is still running. The server would
+recover from this by skipping the new cleanup when it detected that the
+previous cleanup was still in progress, but it implies that the actual
+cleanups will be triggered more rarely than the configured interval. Moreover,
+triggering a new cleanup adds overhead to the server, which is not
+able to respond to new requests for a short period of time when the new
+cleanup process is spawned. Therefore, it is recommended that the
+``lfc-interval`` value be selected in a way that allows the LFC
+to complete the cleanup before a new cleanup is triggered.
+
+Lease file cleanup is performed by a separate process (in the
+background) to avoid a performance impact on the server process. To
+avoid conflicts between two processes using the same lease
+files, the LFC process starts with Kea opening a new lease file; the
+actual LFC process operates on the lease file that is no longer used by
+the server. There are also other files created as a side effect of the
+lease file cleanup. The detailed description of the LFC process is located later
+in this Kea Administrator's Reference Manual: :ref:`kea-lfc`.
+
+.. _database-configuration6:
+
+Lease Database Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. note::
+
+ Lease database access information must be configured for the DHCPv6
+ server, even if it has already been configured for the DHCPv4 server.
+ The servers store their information independently, so each server can
+ use a separate database or both servers can use the same database.
+
+.. note::
+
+ Kea requires the database timezone to match the system timezone.
+ For more details, see :ref:`mysql-database-create` and
+ :ref:`pgsql-database-create`.
+
+Lease database configuration is controlled through the
+``Dhcp6``/``lease-database`` parameters. The database type must be set to
+``memfile``, ``mysql`` or ``postgresql``, e.g.:
+
+::
+
+ "Dhcp6": { "lease-database": { "type": "mysql", ... }, ... }
+
+Next, the name of the database to hold the leases must be set; this is
+the name used when the database was created (see
+:ref:`mysql-database-create` or :ref:`pgsql-database-create`).
+
+For MySQL or PostgreSQL:
+
+::
+
+ "Dhcp6": { "lease-database": { "name": "database-name" , ... }, ... }
+
+If the database is located on a different system from the DHCPv6 server,
+the database host name must also be specified:
+
+::
+
+ "Dhcp6": { "lease-database": { "host": "remote-host-name", ... }, ... }
+
+Normally, the database is on the same machine as the DHCPv6 server.
+In this case, set the value to the empty string:
+
+::
+
+ "Dhcp6": { "lease-database": { "host" : "", ... }, ... }
+
+Should the database use a port other than the default, it may be
+specified as well:
+
+::
+
+ "Dhcp6": { "lease-database": { "port" : 12345, ... }, ... }
+
+Should the database be located on a different system, the administrator may need to
+specify a longer interval for the connection timeout:
+
+::
+
+ "Dhcp6": { "lease-database": { "connect-timeout" : timeout-in-seconds, ... }, ... }
+
+The default value of five seconds should be more than adequate for local
+connections. If a timeout is given, though, it should be an integer
+greater than zero.
+
+The maximum number of times the server automatically attempts to
+reconnect to the lease database after connectivity has been lost may be
+specified:
+
+::
+
+ "Dhcp6": { "lease-database": { "max-reconnect-tries" : number-of-tries, ... }, ... }
+
+If the server is unable to reconnect to the database after making the
+maximum number of attempts, the server will exit. A value of 0 (the
+default) disables automatic recovery and the server will exit
+immediately upon detecting a loss of connectivity (MySQL and PostgreSQL
+only).
+
+The number of milliseconds the server waits between attempts to
+reconnect to the lease database after connectivity has been lost may
+also be specified:
+
+::
+
+ "Dhcp6": { "lease-database": { "reconnect-wait-time" : number-of-milliseconds, ... }, ... }
+
+The default value for MySQL and PostgreSQL is 0, which disables automatic
+recovery and causes the server to exit immediately upon detecting the
+loss of connectivity.
+
+::
+
+ "Dhcp6": { "lease-database": { "on-fail" : "stop-retry-exit", ... }, ... }
+
+The possible values are:
+
+- ``stop-retry-exit`` - disables the DHCP service while trying to automatically
+ recover lost connections. Shuts down the server on failure after exhausting
+ ``max-reconnect-tries``. This is the default value for the lease backend,
+ the host backend, and the configuration backend.
+
+- ``serve-retry-exit`` - continues the DHCP service while trying to
+ automatically recover lost connections. Shuts down the server on failure
+ after exhausting ``max-reconnect-tries``.
+
+- ``serve-retry-continue`` - continues the DHCP service and does not shut down
+ the server even if the recovery fails. This is the default value for forensic
+ logging.
+
+.. note::
+
+ Automatic reconnection to database backends is configured individually per
+ backend; this allows users to tailor the recovery parameters to each backend
+ they use. We suggest that users enable it either for all backends or none,
+ so behavior is consistent.
+
+ Losing connectivity to a backend for which reconnection is disabled results
+ (if configured) in the server shutting itself down. This includes cases when
+ the lease database backend and the hosts database backend are connected to
+ the same database instance.
+
+ It is highly recommended not to change the ``stop-retry-exit`` default
+ setting for the lease manager, as it is critical for the connection to be
+ active while processing DHCP traffic. Change this only if the server is used
+ exclusively as a configuration tool.
+
+::
+
+ "Dhcp6": { "lease-database": { "retry-on-startup" : true, ... }, ... }
+
+During server startup, the inability to connect to any of the configured
+backends is considered fatal only if ``retry-on-startup`` is set to ``false``
+(the default). A fatal error is logged and the server exits, based on the idea
+that the configuration should be valid at startup. Exiting to the operating
+system allows nanny scripts to detect the problem.
+If ``retry-on-startup`` is set to ``true``, the server will start reconnection
+attempts even at server startup or on reconfigure events, and will honor the
+action specified in the ``on-fail`` parameter.
+
+The host parameter is used by the MySQL and PostgreSQL backends.
+
+Finally, the credentials of the account under which the server will
+access the database should be set:
+
+::
+
+ "Dhcp6": {
+ "lease-database": {
+ "user": "user-name",
+ "password": "password",
+ ...
+ },
+ ...
+ }
+
+If there is no password to the account, set the password to the empty
+string ``""``. (This is the default.)
+
+.. _tuning-database-timeouts6:
+
+Tuning Database Timeouts
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+In rare cases, reading or writing to the database may hang. This can be
+caused by a temporary network issue, or by misconfiguration of the proxy
+server switching the connection between different database instances.
+These situations are rare, but users have reported
+that Kea sometimes hangs while performing database IO operations.
+Setting appropriate timeout values can mitigate such issues.
+
+MySQL exposes two distinct connection options to configure the read and
+write timeouts. Kea's corresponding ``read-timeout`` and ``write-timeout``
+configuration parameters specify the timeouts in seconds. For example:
+
+::
+
+ "Dhcp6": { "lease-database": { "read-timeout" : 10, "write-timeout": 20, ... }, ... }
+
+
+Setting these parameters to 0 is equivalent to not specifying them, and
+causes the Kea server to establish a connection to the database with the
+MySQL defaults. In this case, Kea waits indefinitely for the completion of
+the read and write operations.
+
+MySQL versions earlier than 5.6 do not support setting timeouts for
+read and write operations. Moreover, the ``read-timeout`` and ``write-timeout``
+parameters can only be specified for the MySQL backend; setting them for
+any other backend database type causes a configuration error.
+
+To set a timeout in seconds for PostgreSQL, use the ``tcp-user-timeout``
+parameter. For example:
+
+::
+
+ "Dhcp6": { "lease-database": { "tcp-user-timeout" : 10, ... }, ... }
+
+
+Specifying this parameter for other backend types causes a configuration
+error.
+
+.. note::
+
+ The timeouts described here are only effective for TCP connections.
+ Please note that the MySQL client library used by the Kea servers
+ typically connects to the database via a UNIX domain socket when the
+ ``host`` parameter is ``localhost``, but establishes a TCP connection
+ for ``127.0.0.1``.
+
+
+.. _hosts6-storage:
+
+Hosts Storage
+-------------
+
+Kea is also able to store information about host reservations in the
+database. The hosts database configuration uses the same syntax as the
+lease database. In fact, the Kea server opens independent connections for
+each purpose, be it lease or hosts information, which gives
+the most flexibility. Kea can keep leases and host reservations
+separately, but can also point to the same database. Currently the
+supported hosts database types are MySQL and PostgreSQL.
+
+The following configuration can be used to configure a
+connection to MySQL:
+
+::
+
+ "Dhcp6": {
+ "hosts-database": {
+ "type": "mysql",
+ "name": "kea",
+ "user": "kea",
+ "password": "secret123",
+ "host": "localhost",
+ "port": 3306
+ }
+ }
+
+Depending on the database configuration, many of the
+parameters may be optional.
+
+Please note that usage of hosts storage is optional. A user can define
+all host reservations in the configuration file, and that is the
+recommended way if the number of reservations is small. However, when
+the number of reservations grows, it is more convenient to use host
+storage. Please note that both storage methods (the configuration file and
+one of the supported databases) can be used together. If hosts are
+defined in both places, the definitions from the configuration file are
+checked first and external storage is checked later, if necessary.
+
+Host information can be placed in multiple stores. Operations
+are performed on the stores in the order they are defined in the
+configuration file, although this leads to a restriction in ordering
+in the case of a host reservation addition; read-only stores must be
+configured after a (required) read-write store, or the addition will
+fail.
+
+.. note::
+
+ Kea requires the database timezone to match the system timezone.
+ For more details, see :ref:`mysql-database-create` and
+ :ref:`pgsql-database-create`.
+
+.. _hosts-databases-configuration6:
+
+DHCPv6 Hosts Database Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Hosts database configuration is controlled through the
+``Dhcp6``/``hosts-database`` parameters. If enabled, the type of database must
+be set to ``mysql`` or ``postgresql``.
+
+::
+
+ "Dhcp6": { "hosts-database": { "type": "mysql", ... }, ... }
+
+Next, the name of the database to hold the reservations must be set;
+this is the name used when the lease database was created (see
+:ref:`supported-databases` for instructions on how to set up the
+desired database type):
+
+::
+
+ "Dhcp6": { "hosts-database": { "name": "database-name" , ... }, ... }
+
+If the database is located on a different system than the DHCPv6 server,
+the database host name must also be specified:
+
+::
+
+ "Dhcp6": { "hosts-database": { "host": remote-host-name, ... }, ... }
+
+Normally, the database is on the same machine as the DHCPv6 server.
+In this case, set the value to the empty string:
+
+::
+
+ "Dhcp6": { "hosts-database": { "host" : "", ... }, ... }
+
+Should the database use a port different than the default, it may be
+specified as well:
+
+::
+
+ "Dhcp6": { "hosts-database": { "port" : 12345, ... }, ... }
+
+The maximum number of times the server automatically attempts to
+reconnect to the host database after connectivity has been lost may be
+specified:
+
+::
+
+ "Dhcp6": { "hosts-database": { "max-reconnect-tries" : number-of-tries, ... }, ... }
+
+If the server is unable to reconnect to the database after making the
+maximum number of attempts, the server will exit. A value of 0 (the
+default) disables automatic recovery and the server will exit
+immediately upon detecting a loss of connectivity (MySQL and PostgreSQL
+only).
+
+The number of milliseconds the server waits between attempts to
+reconnect to the host database after connectivity has been lost may also
+be specified:
+
+::
+
+ "Dhcp6": { "hosts-database": { "reconnect-wait-time" : number-of-milliseconds, ... }, ... }
+
+The default value for MySQL and PostgreSQL is 0, which disables automatic
+recovery and causes the server to exit immediately upon detecting the
+loss of connectivity.
+
+::
+
+ "Dhcp6": { "hosts-database": { "on-fail" : "stop-retry-exit", ... }, ... }
+
+The possible values are:
+
+- ``stop-retry-exit`` - disables the DHCP service while trying to automatically
+ recover lost connections. Shuts down the server on failure after exhausting
+ ``max-reconnect-tries``. This is the default value for MySQL and PostgreSQL.
+
+- ``serve-retry-exit`` - continues the DHCP service while trying to automatically
+ recover lost connections. Shuts down the server on failure after exhausting
+ ``max-reconnect-tries``.
+
+- ``serve-retry-continue`` - continues the DHCP service and does not shut down the
+ server even if the recovery fails.
+
+.. note::
+
+ Automatic reconnection to database backends is configured individually per
+ backend. This allows users to tailor the recovery parameters to each backend
+ they use. We suggest that users enable it either for all backends or none,
+ so behavior is consistent.
+
+ Losing connectivity to a backend for which reconnection is disabled results
+ (if configured) in the server shutting itself down. This includes cases when
+ the lease database backend and the hosts database backend are connected to
+ the same database instance.
+
+::
+
+ "Dhcp6": { "hosts-database": { "retry-on-startup" : true, ... }, ... }
+
+During server startup, the inability to connect to any of the configured
+backends is considered fatal only if ``retry-on-startup`` is set to ``false``
+(the default). A fatal error is logged and the server exits, based on the idea
+that the configuration should be valid at startup. Exiting to the operating
+system allows nanny scripts to detect the problem.
+If ``retry-on-startup`` is set to ``true``, the server will start reconnection
+attempts even at server startup or on reconfigure events, and will honor the
+action specified in the ``on-fail`` parameter.
+Database connection retries are not attempted on startup if the
+:ischooklib:`libdhcp_limits.so` is loaded because the hook library requires a
+valid connection to the database to check if JSON format is supported and to
+recount class limits.
+
+Finally, the credentials of the account under which the server will
+access the database should be set:
+
+::
+
+ "Dhcp6": {
+ "hosts-database": {
+ "user": "user-name",
+ "password": "password",
+ ...
+ },
+ ...
+ }
+
+If there is no password to the account, set the password to the empty
+string ``""``. (This is the default.)
+
+The multiple-storage extension uses a similar syntax; a configuration is
+placed into a ``hosts-databases`` list instead of into a ``hosts-database``
+entry, as in:
+
+::
+
+ "Dhcp6": { "hosts-databases": [ { "type": "mysql", ... }, ... ], ... }
+
+If the same host is configured both in-file and in-database, Kea does not issue a warning,
+as it would if both were specified in the same data source.
+Instead, the host configured in-file has priority over the one configured
+in-database.
+
+.. _read-only-database-configuration6:
+
+Using Read-Only Databases for Host Reservations with DHCPv6
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In some deployments, the user whose name is specified in the
+database backend configuration may not have write privileges to the
+database. This is often required by the policy within a given network to
+secure the data from being unintentionally modified. In many cases
+administrators have deployed inventory databases, which contain
+substantially more information about the hosts than just the static
+reservations assigned to them. The inventory database can be used to
+create a view of a Kea hosts database and such a view is often
+read-only.
+
+Kea host-database backends operate with an implicit configuration to
+both read from and write to the database. If the user does not
+have write access to the host database, the backend will fail to start
+and the server will refuse to start (or reconfigure). However, if access
+to a read-only host database is required for retrieving reservations
+for clients and/or assigning specific addresses and options, it is
+possible to explicitly configure Kea to start in "read-only" mode. This
+is controlled by the ``readonly`` boolean parameter as follows:
+
+::
+
+ "Dhcp6": { "hosts-database": { "readonly": true, ... }, ... }
+
+Setting this parameter to ``false`` configures the database backend to
+operate in "read-write" mode, which is also the default configuration if
+the parameter is not specified.
+
+.. note::
+
+ The ``readonly`` parameter is only supported for MySQL and
+ PostgreSQL databases.
+
+
+Tuning Database Timeouts for Hosts Storage
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+See :ref:`tuning-database-timeouts6`.
+
+.. _dhcp6-interface-configuration:
+
+Interface Configuration
+-----------------------
+
+The DHCPv6 server must be configured to listen on specific network
+interfaces. The simplest network interface configuration tells the
+server to listen on all available interfaces:
+
+::
+
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ "*" ]
+ },
+ ...
+ }
+
+The asterisk plays the role of a wildcard and means "listen on all
+interfaces." However, it is usually a good idea to explicitly specify
+interface names:
+
+::
+
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ "eth1", "eth3" ]
+ },
+ ...
+ }
+
+
+It is possible to use an interface wildcard (*) concurrently
+with explicit interface names:
+
+::
+
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ "eth1", "eth3", "*" ]
+ },
+ ...
+ }
+
+This format should only be used when it is
+desired to temporarily override a list of interface names and listen on
+all interfaces.
+
+As with the DHCPv4 server, binding to specific addresses and disabling
+re-detection of interfaces are supported. But ``dhcp-socket-type`` is
+not supported, because DHCPv6 uses only UDP/IPv6 sockets. The following example
+shows how to disable interface detection:
+
+::
+
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ "eth1", "eth3" ],
+ "re-detect": false
+ },
+ ...
+ }
+
+
+The loopback interfaces (i.e. the ``lo`` or ``lo0`` interface) are not
+configured by default, unless explicitly mentioned in the
+configuration. Note that Kea requires a link-local address (which does
+not exist on all systems) or a specified unicast address, as in:
+
+::
+
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ "enp0s2/2001:db8::1234:abcd" ]
+ },
+ ...
+ }
+
+Kea binds the service sockets for each interface on startup. If another
+process is already using a port, then Kea logs the message and suppresses an
+error. DHCP service runs, but it is unavailable on some interfaces.
+
+The "service-sockets-require-all" option makes Kea require all sockets to
+be successfully bound. If any opening fails, Kea interrupts the
+initialization and exits with a non-zero status. (Default is false).
+
+::
+
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ "eth1", "eth3" ],
+ "service-sockets-require-all": true
+ },
+ ...
+ }
+
+Sometimes, immediate interruption isn't a good choice. The port can be
+unavailable only temporary. In this case, retrying the opening may resolve
+the problem. Kea provides two options to specify the retrying:
+``service-sockets-max-retries`` and ``service-sockets-retry-wait-time``.
+
+The first defines a maximal number of retries that Kea makes to open a socket.
+The zero value (default) means that the Kea doesn't retry the process.
+
+The second defines a wait time (in milliseconds) between attempts. The default
+value is 5000 (5 seconds).
+
+::
+
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ "eth1", "eth3" ],
+ "service-sockets-max-retries": 5,
+ "service-sockets-retry-wait-time": 5000
+ },
+ ...
+ }
+
+If "service-sockets-max-retries" is non-zero and "service-sockets-require-all"
+is false, then Kea retries the opening (if needed) but does not fail if any
+socket is still not opened.
+
+.. _ipv6-subnet-id:
+
+IPv6 Subnet Identifier
+----------------------
+
+The subnet identifier (subnet ID) is a unique number associated with a particular
+subnet. In principle, it is used to associate clients' leases with their
+respective subnets. The server configuration should contain unique and stable
+identifiers for all subnets. When a subnet identifier is not specified for a
+subnet, it is automatically assigned by the configuration mechanism. The identifiers
+are assigned starting at 1 and are monotonically increased for each subsequent
+subnet: 1, 2, 3, ....
+
+If there are multiple subnets configured with auto-generated identifiers
+and one of them is removed, the subnet identifiers may be renumbered.
+For example: if there are four subnets and the third is removed, the
+last subnet will be assigned the identifier that the third subnet had
+before removal. As a result, the leases stored in the lease database for
+subnet 3 are now associated with subnet 4, something that may have
+unexpected consequences. It is one of the reasons why auto-generated subnet
+identifiers are deprecated starting from Kea version 2.4.0.
+
+.. note::
+
+ The auto-generation of the subnet identifiers will be removed in a future
+ release. Starting from Kea 2.4.0, a subnet without an ``id`` entry
+ or with the zero value raises a warning at the configuration time.
+
+.. note::
+
+ Subnet IDs must be greater than zero and less than 4294967295.
+
+The following configuration assigns the specified subnet identifier
+to a newly configured subnet:
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "subnet": "2001:db8:1::/64",
+ "id": 1024,
+ ...
+ }
+ ]
+ }
+
+This identifier will not change for this subnet unless the ``id``
+parameter is removed or set to 0. The value of 0 forces auto-generation
+of the subnet identifier.
+
+.. _ipv6-subnet-prefix:
+
+IPv6 Subnet Prefix
+------------------
+
+The subnet prefix is the second way to identify a subnet. Kea can
+accept non-canonical subnet addresses; for instance,
+this configuration is accepted:
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "subnet": "2001:db8:1::1/64",
+ ...
+ }
+ ]
+ }
+
+This works even if there is another subnet with the "2001:db8:1::/64" prefix;
+only the textual form of subnets are compared to avoid duplicates.
+
+.. note::
+
+ Abuse of this feature can lead to incorrect subnet selection
+ (see :ref:`dhcp6-config-subnets`).
+
+.. _dhcp6-unicast:
+
+Unicast Traffic Support
+-----------------------
+
+When the DHCPv6 server starts, by default it listens to the DHCP traffic
+sent to multicast address ff02::1:2 on each interface that it is
+configured to listen on (see :ref:`dhcp6-interface-configuration`). In some cases it is
+useful to configure a server to handle incoming traffic sent to global
+unicast addresses as well; the most common reason for this is to have
+relays send their traffic to the server directly. To configure the
+server to listen on a specific unicast address, add a slash (/) after the interface name,
+followed by the global unicast
+address on which the server should listen. The server will listen to this
+address in addition to normal link-local binding and listening on the
+ff02::1:2 address. The sample configuration below shows how to listen on
+2001:db8::1 (a global address) configured on the ``eth1`` interface.
+
+::
+
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ "eth1/2001:db8::1" ]
+ },
+ "option-data": [
+ {
+ "name": "unicast",
+ "data": "2001:db8::1"
+ } ],
+ ...
+ }
+
+
+This configuration will cause the server to listen on ``eth1`` on the
+link-local address, the multicast group (ff02::1:2), and 2001:db8::1.
+
+Usually, unicast support is associated with a server unicast option which
+allows clients to send unicast messages to the server. The example above
+includes a server unicast option specification which causes the
+client to send messages to the specified unicast address.
+
+It is possible to mix interface names, wildcards, and interface
+names/addresses in the list of interfaces. It is not possible, however,
+to specify more than one unicast address on a given interface.
+
+Care should be taken to specify proper unicast addresses, as the server
+will attempt to bind to the addresses specified without any additional
+checks. This approach was selected intentionally, to allow the software to
+communicate over uncommon addresses if so desired.
+
+.. _dhcp6-address-config:
+
+Configuration of IPv6 Address Pools
+-----------------------------------
+
+The main role of a DHCPv6 server is address assignment. For this, the
+server must be configured with at least one subnet and one pool of
+dynamic addresses to be managed. For example, assume that the server is
+connected to a network segment that uses the 2001:db8:1::/64 prefix. The
+administrator of that network decides that addresses from the range
+2001:db8:1::1 to 2001:db8:1::ffff are going to be managed by the DHCPv6
+server. Such a configuration can be achieved in the following way:
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "subnet": "2001:db8:1::/64",
+ "pools": [
+ {
+ "pool": "2001:db8:1::1-2001:db8:1::ffff"
+ }
+ ],
+ ...
+ }
+ ]
+ }
+
+Note that ``subnet`` is defined as a simple string, but the ``pools``
+parameter is actually a list of pools; for this reason, the pool
+definition is enclosed in square brackets, even though only one range of
+addresses is specified.
+
+Each ``pool`` is a structure that contains the parameters that describe
+a single pool. Currently there is only one parameter, ``pool``, which
+gives the range of addresses in the pool.
+
+It is possible to define more than one pool in a subnet; continuing the
+previous example, further assume that 2001:db8:1:0:5::/80 should also be
+managed by the server. It could be written as 2001:db8:1:0:5:: to
+2001:db8:1::5:ffff:ffff:ffff, but typing so many ``f`` characters is cumbersome.
+The pool can be expressed more simply as 2001:db8:1:0:5::/80. Both formats are
+supported by ``Dhcp6`` and they can be mixed in the pool list. For example,
+the following pools could be defined:
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "subnet": "2001:db8:1::/64",
+ "pools": [
+ { "pool": "2001:db8:1::1-2001:db8:1::ffff" },
+ { "pool": "2001:db8:1:05::/80" }
+ ],
+ ...
+ }
+ ]
+ }
+
+White space in pool definitions is ignored, so spaces before and after
+the hyphen are optional. They can be used to improve readability.
+
+The number of pools is not limited, but for performance reasons it is
+recommended to use as few as possible.
+
+The server may be configured to serve more than one subnet. To add a
+second subnet, use a command similar to the following:
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "pools": [
+ { "pool": "2001:db8:1::1-2001:db8:1::ffff" }
+ ]
+ },
+ {
+ "id": 2,
+ "subnet": "2001:db8:2::/64",
+ "pools": [
+ { "pool": "2001:db8:2::/64" }
+ ]
+ },
+ ...
+ ]
+ }
+
+In this example, we allow the server to dynamically assign all addresses
+available in the whole subnet. Although rather wasteful, it is certainly
+a valid configuration to dedicate the whole /64 subnet for that purpose.
+Note that the Kea server does not preallocate the leases, so there is no
+danger in using gigantic address pools.
+
+When configuring a DHCPv6 server using prefix/length notation, please
+pay attention to the boundary values. When specifying that the server
+can use a given pool, it is also able to allocate the first
+(typically a network address) address from that pool. For example, for
+pool 2001:db8:2::/64, the 2001:db8:2:: address may be assigned as well.
+To avoid this, use the ``min-max`` notation.
+
+.. _dhcp6-prefix-config:
+
+Subnet and Prefix Delegation Pools
+----------------------------------
+
+Subnets may also be configured to delegate prefixes, as defined in `RFC
+8415 <https://tools.ietf.org/html/rfc8415>`__, section 6.3. A subnet may
+have one or more prefix delegation pools. Each pool has a prefixed
+address, which is specified as a prefix (``prefix``) and a prefix length
+(``prefix-len``), as well as a delegated prefix length
+(``delegated-len``). The delegated length must not be shorter than
+(i.e. it must be numerically greater than or equal to) the prefix length.
+If both the delegated and prefix lengths are equal, the server will be
+able to delegate only one prefix. The delegated prefix does not have to
+match the subnet prefix.
+
+Below is a sample subnet configuration which enables prefix delegation
+for the subnet:
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:d8b:1::/64",
+ "pd-pools": [
+ {
+ "prefix": "3000:1::",
+ "prefix-len": 64,
+ "delegated-len": 96
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+.. _pd-exclude-option:
+
+Prefix Exclude Option
+---------------------
+
+For each delegated prefix, the delegating router may choose to exclude a
+single prefix out of the delegated prefix as specified in `RFC
+6603 <https://tools.ietf.org/html/rfc6603>`__. The requesting router must
+not assign the excluded prefix to any of its downstream interfaces.
+The excluded prefix is intended to be used on a link through which the delegating router
+exchanges DHCPv6 messages with the requesting router. The configuration
+example below demonstrates how to specify an excluded prefix within a
+prefix pool definition. The excluded prefix
+``2001:db8:1:8000:cafe:80::/72`` will be sent to a requesting router which
+includes the Prefix Exclude option in the Option Request option (ORO),
+and which is delegated a prefix from this pool.
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/48",
+ "pd-pools": [
+ {
+ "prefix": "2001:db8:1:8000::",
+ "prefix-len": 56,
+ "delegated-len": 64,
+ "excluded-prefix": "2001:db8:1:8000:cafe:80::",
+ "excluded-prefix-len": 72
+ }
+ ]
+ }
+ ]
+ }
+
+.. note::
+
+ Here are some liberties and limits to the values that subnets and pools can
+ take in Kea configurations that are out of the ordinary:
+
+ +-------------------------------------------------------------------------------+---------+------------------------------------------------------------------------------------+
+ | Kea configuration case | Allowed | Comment |
+ +===============================================================================+=========+====================================================================================+
+ | Overlapping subnets | Yes | Administrator consideration needs to be given to how clients are matched to |
+ | | | these subnets. |
+ +-------------------------------------------------------------------------------+---------+------------------------------------------------------------------------------------+
+ | Overlapping address pools in one subnet | No | Startup error: DHCP6_PARSER_FAIL |
+ +-------------------------------------------------------------------------------+---------+------------------------------------------------------------------------------------+
+ | Overlapping address pools in different subnets | Yes | Specifying the same address pool in different subnets can be used as an equivalent |
+ | | | of the global address pool. In that case, the server can assign addresses from the |
+ | | | same range regardless of the client's subnet. If an address from such a pool is |
+ | | | assigned to a client in one subnet, the same address will be renewed for this |
+ | | | client if it moves to another subnet. Another client in a different subnet will |
+ | | | not be assigned an address already assigned to the client in any of the subnets. |
+ +-------------------------------------------------------------------------------+---------+------------------------------------------------------------------------------------+
+ | Address pools that are outside the subnet they are configured under | No | Startup error: DHCP6_PARSER_FAIL |
+ +-------------------------------------------------------------------------------+---------+------------------------------------------------------------------------------------+
+ | Overlapping prefix delegation pools in one subnet | No | Startup error: DHCP6_PARSER_FAIL |
+ +-------------------------------------------------------------------------------+---------+------------------------------------------------------------------------------------+
+ | Overlapping prefix delegation pools in different subnets | Yes | Specifying the same prefix delegation pool in different subnets can be used as an |
+ | | | equivalent of the global pool. In that case, the server can delegate the same |
+ | | | prefixes regardless of the client's subnet. If a prefix from such a pool is |
+ | | | delegated to a client in one subnet, the same prefix will be renewed for this |
+ | | | client if it moves to another subnet. Another client in a different subnet will |
+ | | | not be delegated a prefix already delegated to the client in any of the subnets. |
+ +-------------------------------------------------------------------------------+---------+------------------------------------------------------------------------------------+
+ | Prefix delegation pools not matching the subnet prefix | Yes | It is common in many deployments to configure the prefix delegation pools not |
+ | | | matching the subnet prefix, e.g. a prefix pool of 3000::/96 within the |
+ | | | 2001:db8:1::/64 subnet. Such use cases are supported by the Kea DHCPv6 server. |
+ +-------------------------------------------------------------------------------+---------+------------------------------------------------------------------------------------+
+
+.. _dhcp6-std-options:
+
+Standard DHCPv6 Options
+-----------------------
+
+One of the major features of the DHCPv6 server is the ability to provide
+configuration options to clients. Although there are several options
+that require special behavior, most options are sent by the server only
+if the client explicitly requests them. The following example shows how
+to configure the addresses of DNS servers, one of the most frequently used options.
+Options specified in this way are considered global and apply to all configured subnets.
+
+::
+
+ "Dhcp6": {
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "code": 23,
+ "space": "dhcp6",
+ "csv-format": true,
+ "data": "2001:db8::cafe, 2001:db8::babe"
+ },
+ ...
+ ]
+ }
+
+The ``option-data`` line creates a new entry in the option-data table.
+This table contains information on all global options that the server is
+supposed to configure in all subnets. The ``name`` line specifies the
+option name. (For a complete list of currently supported names, see
+:ref:`dhcp6-std-options-list`.) The next line specifies the
+option code, which must match one of the values from that list. The line
+beginning with ``space`` specifies the option space, which must always
+be set to ``dhcp6`` as these are standard DHCPv6 options. For other name
+spaces, including custom option spaces, see :ref:`dhcp6-option-spaces`. The following line
+specifies the format in which the data will be entered; use of CSV
+(comma-separated values) is recommended. Finally, the ``data`` line
+gives the actual value to be sent to clients. The data parameter is specified as
+normal text, with values separated by commas if more than one value is
+allowed.
+
+Options can also be configured as hexadecimal values. If ``csv-format`` is
+set to ``false``, the option data must be specified as a hexadecimal string.
+The following commands configure the ``dns-servers`` option for all subnets
+with the addresses 2001:db8:1::cafe and 2001:db8:1::babe.
+
+::
+
+ "Dhcp6": {
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "code": 23,
+ "space": "dhcp6",
+ "csv-format": false,
+ "data": "20 01 0D B8 00 01 00 00 00 00 00 00 00 00 CA FE \
+ 20 01 0D B8 00 01 00 00 00 00 00 00 00 00 BA BE"
+ },
+ ...
+ ]
+ }
+
+.. note::
+
+ The value for the setting of the ``data`` element is split across two
+ lines in this example for clarity; when entering the command, the
+ whole string should be entered on the same line.
+
+Kea supports the following formats when specifying hexadecimal data:
+
+- ``Delimited octets`` - one or more octets separated by either colons or
+ spaces (":" or " "). While each octet may contain one or two digits,
+ we strongly recommend always using two digits. Valid examples are
+ "ab:cd:ef" and "ab cd ef".
+
+- ``String of digits`` - a continuous string of hexadecimal digits with
+ or without a "0x" prefix. Valid examples are "0xabcdef" and "abcdef".
+
+Care should be taken to use proper encoding when using hexadecimal
+format; Kea's ability to validate data correctness in hexadecimal is
+limited.
+
+It is also possible to specify data for binary options as
+a single-quoted text string within double quotes, as shown (note that
+``csv-format`` must be set to ``false``):
+
+::
+
+ "Dhcp6": {
+ "option-data": [
+ {
+ "name": "subscriber-id",
+ "code": 38,
+ "space": "dhcp6",
+ "csv-format": false,
+ "data": "'convert this text to binary'"
+ },
+ ...
+ ],
+ ...
+ }
+
+Most of the parameters in the ``option-data`` structure are optional and
+can be omitted in some circumstances, as discussed in :ref:`dhcp6-option-data-defaults`.
+Only one of ``name`` or ``code``
+is required; it is not necessary to specify both. Space has a default value
+of ``dhcp6``, so this can be skipped as well if a regular (not
+encapsulated) DHCPv6 option is defined. Finally, ``csv-format`` defaults to ``true``, so it
+too can be skipped, unless the option value is specified as
+hexstring. Therefore, the above example can be simplified to:
+
+::
+
+ "Dhcp6": {
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8::cafe, 2001:db8::babe"
+ },
+ ...
+ ]
+ }
+
+
+Defined options are added to the response when the client requests them,
+as well as any options required by a protocol. An administrator can also
+specify that an option is always sent, even if a client did not
+specifically request it. To enforce the addition of a particular option,
+set the ``always-send`` flag to ``true``, as in:
+
+::
+
+ "Dhcp6": {
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8::cafe, 2001:db8::babe",
+ "always-send": true
+ },
+ ...
+ ]
+ }
+
+
+The effect is the same as if the client added the option code in the
+Option Request Option (or its equivalent for vendor options), as in:
+
+::
+
+ "Dhcp6": {
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8::cafe, 2001:db8::babe",
+ "always-send": true
+ },
+ ...
+ ],
+ "subnet6": [
+ {
+ "subnet": "2001:db8:1::/64",
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8:1::cafe, 2001:db8:1::babe"
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+
+In the example above, the ``dns-servers`` option respects the global
+``always-send`` flag and is always added to responses, but for subnet
+``2001:db8:1::/64``, the value is taken from the subnet-level option data
+specification.
+
+Contrary to ``always-send``, if the ``never-send`` flag is set to
+``true`` for a particular option, the server does not add it to the response.
+The effect is the same as if the client removed the option code in the
+Option Request Option (or its equivalent for vendor options):
+
+::
+
+ "Dhcp6": {
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8::cafe, 2001:db8::babe"
+ },
+ ...
+ ],
+ "subnet6": [
+ {
+ "subnet": "2001:db8:1::/64",
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "never-send": true
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+In the example above, the ``dns-server`` option is never added to responses
+on subnet ``2001:db8:1::/64``. ``never-send`` has precedence over
+``always-send``, so if both are ``true`` the option is not added.
+
+.. note::
+
+ The ``always-send`` and ``never-send`` flags are sticky, meaning
+ they do not follow the usual configuration inheritance rules.
+ Instead, if they are enabled at least once along the configuration
+ inheritance chain, they are applied - even if they are
+ disabled in other places which would normally receive a higher priority.
+ For instance, if one of the flags is enabled in the global scope,
+ but disabled at the subnet level, it is enabled,
+ disregarding the subnet-level setting.
+
+.. note::
+
+ The ``never-send`` flag is less powerful than :ischooklib:`libdhcp_flex_option.so`;
+ for instance, it has no effect on options managed by the server itself.
+ Both ``always-send`` and ``never-send`` have no effect on options
+ which cannot be requested, for instance from a custom space.
+
+It is possible to override options on a per-subnet basis. If clients
+connected to most subnets are expected to get the same values of
+a given option, administrators should use global options; it is possible to override
+specific values for a small number of subnets. On the other hand, if
+different values are used in each subnet, it does not make sense to specify
+global option values; rather, only subnet-specific ones should be set.
+
+The following commands override the global ``dns-servers`` option for a
+particular subnet, setting a single DNS server with address
+2001:db8:1::3.
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "code": 23,
+ "space": "dhcp6",
+ "csv-format": true,
+ "data": "2001:db8:1::3"
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+In some cases it is useful to associate some options with an address or
+prefix pool from which a client is assigned a lease. Pool-specific
+option values override subnet-specific and global option values. If the
+client is assigned multiple leases from different pools, the server
+assigns options from all pools from which the leases have been obtained.
+However, if the particular option is specified in multiple pools from
+which the client obtains the leases, only one instance of this option
+is handed out to the client. The server's administrator must not
+try to prioritize assignment of pool-specific options by trying to order
+pool declarations in the server configuration.
+
+The following configuration snippet demonstrates how to specify the
+``dns-servers`` option, which will be assigned to a client only if the client
+obtains an address from the given pool:
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "pools": [
+ {
+ "pool": "2001:db8:1::100-2001:db8:1::300",
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8:1::10"
+ }
+ ]
+ }
+ ]
+ },
+ ...
+ ],
+ ...
+ }
+
+Options can also be specified in class or host-reservation scope. The
+current Kea options precedence order is (from most important to least): host
+reservation, pool, subnet, shared network, class, global.
+
+When a data field is a string and that string contains the comma (``,``;
+U+002C) character, the comma must be escaped with two backslashes (``\\,``;
+U+005C). This double escape is required because both the routine
+splitting of CSV data into fields and JSON use the same escape character; a
+single escape (``\,``) would make the JSON invalid. For example, the string
+"EST5EDT4,M3.2.0/02:00,M11.1.0/02:00" must be represented as:
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "pools": [
+ {
+ "option-data": [
+ {
+ "name": "new-posix-timezone",
+ "data": "EST5EDT4\\,M3.2.0/02:00\\,M11.1.0/02:00"
+ }
+ ]
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+Some options are designated as arrays, which means that more than one
+value is allowed. For example, the option ``dns-servers``
+allows the specification of more than one IPv6 address, enabling clients
+to obtain the addresses of multiple DNS servers.
+
+:ref:`dhcp6-custom-options` describes the
+configuration syntax to create custom option definitions (formats).
+Creation of custom definitions for standard options is generally not
+permitted, even if the definition being created matches the actual
+option format defined in the RFCs. However, there is an exception to this rule
+for standard options for which Kea currently does not provide a
+definition. To use such options, a server administrator must
+create a definition as described in :ref:`dhcp6-custom-options` in the ``dhcp6`` option space. This
+definition should match the option format described in the relevant RFC,
+but the configuration mechanism allows any option format as there is
+currently no way to validate it.
+
+The currently supported standard DHCPv6 options are listed in
+the table below. "Name" and "Code" are the
+values that should be used as a name/code in the option-data structures.
+"Type" designates the format of the data; the meanings of the various
+types are given in :ref:`dhcp-types`.
+
+.. _dhcp6-std-options-list:
+
+.. table:: List of standard DHCPv6 options configurable by an administrator
+
+ +--------------------------+-----------------+-----------------+-----------------+
+ | Name | Code | Type | Array? |
+ +==========================+=================+=================+=================+
+ | preference | 7 | uint8 | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | unicast | 12 | ipv6-address | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | sip-server-dns | 21 | fqdn | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | sip-server-addr | 22 | ipv6-address | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | dns-servers | 23 | ipv6-address | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | domain-search | 24 | fqdn | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | nis-servers | 27 | ipv6-address | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | nisp-servers | 28 | ipv6-address | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | nis-domain-name | 29 | fqdn | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | nisp-domain-name | 30 | fqdn | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | sntp-servers | 31 | ipv6-address | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | information-refresh-time | 32 | uint32 | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | bcmcs-server-dns | 33 | fqdn | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | bcmcs-server-addr | 34 | ipv6-address | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | geoconf-civic | 36 | record (uint8, | false |
+ | | | uint16, binary) | |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | remote-id | 37 | record (uint32, | false |
+ | | | binary) | |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | subscriber-id | 38 | binary | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | client-fqdn | 39 | record (uint8, | false |
+ | | | fqdn) | |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | pana-agent | 40 | ipv6-address | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | new-posix-timezone | 41 | string | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | new-tzdb-timezone | 42 | string | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | ero | 43 | uint16 | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | lq-query (1) | 44 | record (uint8, | false |
+ | | | ipv6-address) | |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | client-data (1) | 45 | empty | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | clt-time (1) | 46 | uint32 | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | lq-relay-data (1) | 47 | record | false |
+ | | | (ipv6-address, | |
+ | | | binary) | |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | lq-client-link (1) | 48 | ipv6-address | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | v6-lost | 51 | fqdn | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | capwap-ac-v6 | 52 | ipv6-address | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | relay-id | 53 | binary | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | v6-access-domain | 57 | fqdn | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | sip-ua-cs-list | 58 | fqdn | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | bootfile-url | 59 | string | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | bootfile-param | 60 | tuple | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | client-arch-type | 61 | uint16 | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | nii | 62 | record (uint8, | false |
+ | | | uint8, uint8) | |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | aftr-name | 64 | fqdn | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | erp-local-domain-name | 65 | fqdn | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | rsoo | 66 | empty | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | pd-exclude | 67 | binary | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | rdnss-selection | 74 | record | true |
+ | | | (ipv6-address, | |
+ | | | uint8, fqdn) | |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | client-linklayer-addr | 79 | binary | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | link-address | 80 | ipv6-address | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | solmax-rt | 82 | uint32 | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | inf-max-rt | 83 | uint32 | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | dhcp4o6-server-addr | 88 | ipv6-address | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | s46-rule | 89 | record (uint8, | false |
+ | | | uint8, uint8, | |
+ | | | ipv4-address, | |
+ | | | ipv6-prefix) | |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | s46-br | 90 | ipv6-address | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | s46-dmr | 91 | ipv6-prefix | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | s46-v4v6bind | 92 | record | false |
+ | | | (ipv4-address, | |
+ | | | ipv6-prefix) | |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | s46-portparams | 93 | record(uint8, | false |
+ | | | psid) | |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | s46-cont-mape | 94 | empty | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | s46-cont-mapt | 95 | empty | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | s46-cont-lw | 96 | empty | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | v6-captive-portal | 103 | string | false |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | v6-sztp-redirect | 136 | tuple | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | ipv6-address-andsf | 143 | ipv6-address | true |
+ +--------------------------+-----------------+-----------------+-----------------+
+ | v6-dnr | 144 | record (uint16, | false |
+ | | | uint16, fqdn, | |
+ | | | binary) | |
+ +--------------------------+-----------------+-----------------+-----------------+
+
+Options marked with (1) have option definitions, but the logic behind
+them is not implemented. That means that, technically, Kea knows how to
+parse them in incoming messages or how to send them if configured to do
+so, but not what to do with them. Since the related RFCs require certain
+processing, the support for those options is non-functional. However, it
+may be useful in some limited lab testing; hence the definition formats
+are listed here.
+
+Some options are more complex to configure than others. In particular, the Softwire46 family of options
+and DNR are discussed in separate sections below.
+
+Kea supports more options than those listed above. The following list is mostly useful for readers who
+want to understand whether Kea is able to support certain options. The following options are
+returned by the Kea engine itself and in general should not be configured manually.
+
+.. table:: List of standard DHCPv6 options managed by Kea on its own and not directly configurable by an administrator
+
+ +--------------+------+------------------------------------------------------------------------+
+ | Name | Code | Description |
+ +==============+======+========================================================================+
+ | client-id | 1 | Sent by the client; Kea uses it to distinguish between clients. |
+ +--------------+------+------------------------------------------------------------------------+
+ | server-id | 2 | Sent by clients to request action from a specific server and by the |
+ | | | server to identify itself. See :ref:`dhcp6-serverid` for details. |
+ +--------------+------+------------------------------------------------------------------------+
+ | ia-na | 3 | A container option that conveys IPv6 addresses (``iaddr`` options). Kea|
+ | | | receives and sends those options using its allocation engine. |
+ +--------------+------+------------------------------------------------------------------------+
+ | ia-ta | 4 | Conveys temporary addresses. Deprecated feature, not supported. |
+ +--------------+------+------------------------------------------------------------------------+
+ | iaaddr | 5 | Conveys addresses with lifetimes in ``ia-na`` and ``ia-ta`` options. |
+ +--------------+------+------------------------------------------------------------------------+
+ | oro | 6 | ORO (or Option Request Option) is used by clients to request a list |
+ | | | of options they are interested in. Kea supports it and sends the |
+ | | | requested options back if configured with required options. |
+ +--------------+------+------------------------------------------------------------------------+
+ | elapsed-time | 8 | Sent by clients to identify how long they have been trying to obtain a |
+ | | | configuration. Kea uses high values sent by clients as an indicator |
+ | | | that something is wrong; this is one of the aspects used in HA to |
+ | | | determine if the partner is healthy or not. |
+ +--------------+------+------------------------------------------------------------------------+
+ | relay-msg | 9 | Used by relays to encapsulate the original client message. Kea uses it |
+ | | | when sending back relayed responses to the relay agent. |
+ +--------------+------+------------------------------------------------------------------------+
+ | auth | 11 | Used to pass authentication information between clients and server. The|
+ | | | support for this option is very limited. |
+ +--------------+------+------------------------------------------------------------------------+
+ | status-code | 13 | An option that the server can attach in case of various failures, such |
+ | | | as running out of addresses or not being configured to assign prefixes.|
+ +--------------+------+------------------------------------------------------------------------+
+ | rapid-commit | 14 | Used to signal the client's willingness to support ``rapid-commit`` and|
+ | | | the server's acceptance for this configuration. See |
+ | | | :ref:`dhcp6-rapid-commit` for details. |
+ +--------------+------+------------------------------------------------------------------------+
+ | user-class | 15 | Sent by the client to self-identify the device type. Kea |
+ | | | can use this for client classification. |
+ +--------------+------+------------------------------------------------------------------------+
+ | vendor-class | 16 | Similar to ``user-class``, but vendor-specific. |
+ +--------------+------+------------------------------------------------------------------------+
+ | vendor-opts | 17 | A vendor-specific container that is used by both the client and the |
+ | | | server to exchange vendor-specific options. The logic behind those |
+ | | | options varies between vendors. Vendor options are explained in |
+ | | | :ref:`dhcp6-vendor-opts`. |
+ +--------------+------+------------------------------------------------------------------------+
+ | interface-id | 18 | May be inserted by the relay agent to identify the interface that the |
+ | | | original client message was received on. Kea may be told to use this |
+ | | | information to select specific subnets. Also, if specified, Kea |
+ | | | echoes this option back, so the relay will know which interface to use |
+ | | | to reach the client. |
+ +--------------+------+------------------------------------------------------------------------+
+ | ia-pd | 25 | A container for conveying Prefix Delegations (PDs)) that are being |
+ | | | delegated to clients. See :ref:`dhcp6-prefix-config` for details. |
+ +--------------+------+------------------------------------------------------------------------+
+ | iaprefix | 26 | Conveys the IPv6 prefix in the ``ia-pd`` option. See |
+ | | | :ref:`dhcp6-prefix-config` for details. |
+ +--------------+------+------------------------------------------------------------------------+
+
+.. _s46-options:
+
+Common Softwire46 Options
+-------------------------
+
+Softwire46 options are involved in IPv4-over-IPv6 provisioning by means
+of tunneling or translation, as specified in `RFC
+7598 <https://tools.ietf.org/html/rfc7598>`__. The following sections
+provide configuration examples of these options.
+
+.. _s46-containers:
+
+Softwire46 Container Options
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Softwire46 (S46) container options group rules and optional port parameters for a
+specified domain. There are three container options specified in the
+"dhcp6" (top-level) option space: the MAP-E Container option, the MAP-T
+Container option, and the S46 Lightweight 4over6 Container option. These
+options only contain the encapsulated options specified below; they do not
+include any data fields.
+
+To configure the server to send a specific container option along with
+all encapsulated options, the container option must be included in the
+server configuration as shown below:
+
+::
+
+ "Dhcp6": {
+ "option-data": [
+ {
+ "name": "s46-cont-mape"
+ } ],
+ ...
+ }
+
+This configuration will cause the server to include the MAP-E Container
+option to the client. Use ``s46-cont-mapt`` or ``s46-cont-lw`` for the MAP-T
+Container and S46 Lightweight 4over6 Container options, respectively.
+
+All remaining Softwire46 options described below are included in one of
+the container options. Thus, they must be included in appropriate
+option spaces by selecting a ``space`` name, which specifies the
+option where they are supposed to be included.
+
+S46 Rule Option
+~~~~~~~~~~~~~~~
+
+The S46 Rule option is used to convey the Basic Mapping Rule (BMR)
+and Forwarding Mapping Rule (FMR).
+
+::
+
+ {
+ "space": "s46-cont-mape-options",
+ "name": "s46-rule",
+ "data": "128, 0, 24, 192.0.2.0, 2001:db8:1::/64"
+ }
+
+Another possible ``space`` value is ``s46-cont-mapt-options``.
+
+The S46 Rule option conveys a number of parameters:
+
+- ``flags`` - an unsigned 8-bit integer, with currently only the
+ most-significant bit specified. It denotes whether the rule can be
+ used for forwarding (128) or not (0).
+
+- ``ea-len`` - an 8-bit-long Embedded Address length. Allowed values
+ range from 0 to 48.
+
+- ``IPv4 prefix length`` - an 8-bit-long expression of the prefix length of
+ the Rule IPv4 prefix specified in the ``ipv4-prefix`` field. Allowed
+ values range from 0 to 32.
+
+- ``IPv4 prefix`` - a fixed-length 32-bit field that specifies the IPv4
+ prefix for the S46 rule. The bits in the prefix after
+ a specific number of bits (defined in ``prefix4-len``) are reserved, and MUST
+ be initialized to zero by the sender and ignored by the receiver.
+
+- ``IPv6 prefix`` - a field in prefix/length notation that specifies the IPv6
+ domain prefix for the S46 rule. The field is padded on the right with
+ zero bits up to the nearest octet boundary, when ``prefix6-len`` is not
+ evenly divisible by 8.
+
+S46 BR Option
+~~~~~~~~~~~~~
+
+The S46 BR option is used to convey the IPv6 address of the Border
+Relay. This option is mandatory in the MAP-E Container option and is not
+permitted in the MAP-T and S46 Lightweight 4over6 Container options.
+
+.. code-block:: json
+
+ {
+ "space": "s46-cont-mape-options",
+ "name": "s46-br",
+ "data": "2001:db8:cafe::1"
+ }
+
+Another possible ``space`` value is ``s46-cont-lw-options``.
+
+S46 DMR Option
+~~~~~~~~~~~~~~
+
+The S46 DMR option is used to convey values for the Default Mapping Rule
+(DMR). This option is mandatory in the MAP-T container option and is not
+permitted in the MAP-E and S46 Lightweight 4over6 Container options.
+
+.. code-block:: json
+
+ {
+ "space": "s46-cont-mapt-options",
+ "name": "s46-dmr",
+ "data": "2001:db8:cafe::/64"
+ }
+
+This option must not be included in other containers.
+
+S46 IPv4/IPv6 Address Binding Option
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The S46 IPv4/IPv6 Address Binding option may be used to specify the full
+or shared IPv4 address of the Customer Edge (CE). The IPv6 prefix field
+is used by the CE to identify the correct prefix to use for the tunnel
+source.
+
+::
+
+ {
+ "space": "s46-cont-lw",
+ "name": "s46-v4v6bind",
+ "data": "192.0.2.3, 2001:db8:1:cafe::/64"
+ }
+
+This option must not be included in other containers.
+
+S46 Port Parameters
+~~~~~~~~~~~~~~~~~~~
+
+The S46 Port Parameters option specifies optional port-set information
+that may be provided to CEs.
+
+.. code-block:: json
+
+ {
+ "space": "s46-rule-options",
+ "name": "s46-portparams",
+ "data": "2, 3/4"
+ }
+
+Another possible ``space`` value is ``s46-v4v6bind``, to include this option
+in the S46 IPv4/IPv6 Address Binding option.
+
+Note that the second value in the example above specifies the PSID and
+PSID-length fields in the format of PSID/PSID length. This is equivalent
+to the values of ``PSID-len=4`` and ``PSID=12288`` conveyed in the S46 Port
+Parameters option.
+
+.. _dnr6-options:
+
+DNR (Discovery of Network-designated Resolvers) Options for DHCPv6
+------------------------------------------------------------------
+
+One of the more recently added options is the Discovery of
+Network-designated Resolvers or DNR option,
+introduced in `RFC 9463 <https://www.rfc-editor.org/rfc/rfc9463>`__. The goal of that RFC is
+to provide a way to communicate location of DNS resolvers available over means other than
+the classic DNS over UDP port 53. At the time of this writing, the supported technologies
+are DoT (DNS-over-TLS), DoH (DNS-over-HTTPS), and DoQ (DNS-over-QUIC), but the option was
+designed to be extensible to accommodate other protocols in the future.
+
+DNR option may be configured using convenient notation. Comma delimited fields must be provided in the following order:
+
+- Service Priority (mandatory),
+- ADN FQDN (mandatory),
+- IP address(es) (optional - if more than one - they must be space-separated)
+- SvcParams as a set of key=value pairs (optional - if more than one - they must be space-separated;
+ to provide more than one alpn-id separate them with double backslash escaped comma like in the
+ example below).
+
+Let's imagine an example that we want to convey a DoT server operating at ``dot1.example.org``
+(which resolves to two IPv6 addresses: ``2001:db8::1`` and ``2001:db8::2``) on a non-standard port 8530.
+An example option that would convey this information looks as follows:
+
+::
+
+ {
+ "name": "v6-dnr", // name of the option
+
+ // The following fields should be specified:
+ // - service priority (unsigned 16 bit integer)
+ // - authentication-domain-name (fqdn of the encrypted resolver)
+ // - a list of one or more IPv6 addresses
+ // - list of parameters in key=value format, space separated; any comma
+ // characters in this field must be escaped with double backslash
+ "data": "100, dot1.example.org., 2001:db8::1 2001:db8::2, alpn=dot port=8530"
+ }
+
+The above option will be encoded on-wire as follows:
+
+::
+
+ 00 64 - service priority (100 in hex as unsigned 16 bit integer)
+ 00 12 - length of the Authentication Domain Name (name of the resolver) FQDN (18 in hex as unsigned 16 bit integer)
+ 04 64 6f 74 31 07 65 78 61 6d 70 6c 65 03 6f 72 67 00 - 18 octets of the ADN FQDN
+ 00 20 - 32 octets is the length of the following two IPv6 addresses
+ 20 01 0d b8 00 00 00 00 00 00 00 00 00 00 00 01 - 2001:db8::1
+ 20 01 0d b8 00 00 00 00 00 00 00 00 00 00 00 02 - 2001:db8::2
+ 00 01 - SvsParams begin - this is alpn SvcParamKey
+ 00 04 - length of the alpn SvcParamValue field (4 octets)
+ 03 - length of the following alpn-id coded on one octet
+ 64 6f 74 - "dot" - value of the alpn
+ 00 03 - this is port SvcParamKey
+ 00 02 - length of the SvcParamValue field is 2 octets
+ 21 52 - the actual value is 0x2152 or 8530 in decimal
+
+The following example shows how to configure more than one ``ALPN`` protocol in Service Parameters.
+The example specifies a resolver known as ``resolver.example`` that supports:
+
+- DoT on default port 853
+- DoQ on default port 853
+- DoH at ``https://resolver.example/q{?dns}``
+
+::
+
+ {
+ "name": "v6-dnr", // name of the option
+
+ // Note the double backslash escaped commas in alpn-id list.
+ "data": "150, resolver.example., 2001:db8::1 2001:db8::2, alpn=dot\\,doq\\,h2\\,h3 dohpath=/q{?dns}"
+ }
+
+The above option will be encoded on-wire as follows:
+
+::
+
+ 00 96 - service priority (150 in hex as unsigned 16 bit integer)
+ 00 12 - length of the Authentication Domain Name (name of the resolver) FQDN (18 in hex as unsigned 16 bit integer)
+ 08 72 65 73 6f 6c 76 65 72 07 65 78 61 6d 70 6c 65 00 - 18 octets of the ADN FQDN
+ 00 20 - 32 octets is the length of the following two IPv6 addresses
+ 20 01 0d b8 00 00 00 00 00 00 00 00 00 00 00 01 - 2001:db8::1
+ 20 01 0d b8 00 00 00 00 00 00 00 00 00 00 00 02 - 2001:db8::2
+ 00 01 - SvsParams begin - this is alpn SvcParamKey
+ 00 0e - length of the alpn SvcParamValue field (14 octets)
+ 03 - length of the following alpn-id coded on one octet
+ 64 6f 74 - "dot" - value of the alpn
+ 03 - length of the following alpn-id coded on one octet
+ 64 6f 71 - "doq" - value of the alpn
+ 02 - length of the following alpn-id coded on one octet
+ 68 32 - "h2" - value of the alpn "HTTP/2 over TLS"
+ 02 - length of the following alpn-id coded on one octet
+ 68 33 - "h3" - value of the alpn "HTTP/3"
+ 00 07 - this is dohpath SvcParamKey
+ 00 08 - length of the SvcParamValue field is 8 octets
+ 2f 71 7b 3f 64 6e 73 7d - "/q{?dns}" dohpath
+
+
+.. note::
+
+ Note that whenever "comma" characters need to be used not as the delimiters, they must be escaped with
+ double backslash (``\\,``). E.g. one must use escaped commas when configuring more than one ``ALPN``
+ protocol to separate them.
+
+The `RFC 9463 <https://www.rfc-editor.org/rfc/rfc9463#name-option-format>`__ Section 4.1 is encouraging to include
+at least the ``ALPN`` (Application-Layer Protocol Negotiation) SvcParam, as it will be required in most cases.
+It defines the protocol how the encrypted resolver could be reached. The most common values are
+``dot``, ``doq``, ``h2`` (meaning HTTP/2.0 over TLS, used in DoH).
+
+As per `RFC 9461 <https://www.rfc-editor.org/rfc/rfc9461.html#name-new-svcparamkey-dohpath>`__ Section 5:
+
+If the ``alpn`` SvcParam indicates support for HTTP, ``dohpath`` MUST be present. The URI Template MUST contain
+a "dns" variable. For example, when advertising DoH resolver available at
+``https://doh1.example.org/query{?dns}``, the ``dohpath`` should be set to relative URI ``/query{?dns}``.
+
+A reader interested in configuring this option is encouraged to read the following materials:
+
+- A very nice set of examples is available in Section 7 of `RFC 9461
+ <https://www.rfc-editor.org/rfc/rfc9461#name-examples>`__.
+- List of all currently defined service parameters is maintained on `IANA registry
+ <https://www.iana.org/assignments/dns-svcb/dns-svcb.xhtml>`__. This specifies records that can be
+ stored in the svcParams field of the DNR option.
+- List of currently allowed protocols in the ALPN parameter is maintained on `another IANA registry
+ <https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids>`__.
+
+- `RFC 9463 <https://www.rfc-editor.org/rfc/rfc9463>`__ which provides option definitions. In terms of SvcParams, it states
+ that at `alpn` and `port` must be supported, and `dohpath` (used for DoH) is recommended to be supported.
+- Section 2.2 of `RFC 9460 <https://www.rfc-editor.org/rfc/rfc9460>`__, which defines the on-wire format for SvcParams.
+- Sections 7.1, 7.2 of `RFC 9460 <https://www.rfc-editor.org/rfc/rfc9460>`__, which defines the on-wire format for alpn and port.
+- Section 5 of `RFC 9461 <https://www.rfc-editor.org/rfc/rfc9461#name-new-svcparamkey-dohpath>`__, which defines
+ on-wire format for `dohpath`.
+
+Kea currently supports the following service parameters:
+
+ +-----------------+------+------------------------------------------------------------------------+
+ | Name | Code | Description |
+ +=================+======+========================================================================+
+ | alpn | 1 | Specifies comma separated protocol types (DoT, DoH, etc.) |
+ +-----------------+------+------------------------------------------------------------------------+
+ | port | 3 | Unsigned 16 bit integer. Indicated non-standard TCP or UDP port. |
+ +-----------------+------+------------------------------------------------------------------------+
+ | dohpath | 7 | Mandatory for DoH. Contains URL path for the DoT resolver. |
+ +-----------------+------+------------------------------------------------------------------------+
+
+Other currently defined service parameters: mandatory (0), no-default-alpn (2), ipv4hint (4), ech (5),
+ipv6hint (6), and ohttp (8) are not usable in the DNR option.
+
+Further examples are provided in Kea sources in ``all-options.json`` file
+in the ``doc/examples/kea6`` directory. The DHCPv4 option is almost equivalent, and is described
+in :ref:`dnr4-options`.
+
+
+.. _dhcp6-custom-options:
+
+Custom DHCPv6 Options
+---------------------
+
+Kea supports custom (non-standard) DHCPv6 options.
+Let's say that we want to define a new DHCPv6 option called ``foo``, which
+will have code 100 and will convey a single, unsigned, 32-bit
+integer value. Such an option can be defined by putting the following entry
+in the configuration file:
+
+::
+
+ "Dhcp6": {
+ "option-def": [
+ {
+ "name": "foo",
+ "code": 100,
+ "type": "uint32",
+ "array": false,
+ "record-types": "",
+ "space": "dhcp6",
+ "encapsulate": ""
+ },
+ ...
+ ],
+ ...
+ }
+
+The ``false`` value of the ``array`` parameter determines that the option
+does NOT comprise an array of ``uint32`` values but is, instead, a single
+value. Two other parameters have been left blank: ``record-types`` and
+``encapsulate``. The former specifies the comma-separated list of option
+data fields, if the option comprises a record of data fields. The
+``record-types`` value should be non-empty if ``type`` is set to
+``record``; otherwise it must be left blank. The latter parameter
+specifies the name of the option space being encapsulated by the
+particular option. If the particular option does not encapsulate any
+option space, the parameter should be left blank. Note that the ``option-def``
+configuration statement only defines the format of an option and does
+not set its value(s).
+
+The ``name``, ``code``, and ``type`` parameters are required; all others
+are optional. The ``array`` parameter default value is ``false``. The
+``record-types`` and ``encapsulate`` parameters default values are blank
+(``""``). The default ``space`` is ``dhcp6``.
+
+Once the new option format is defined, its value is set in the same way
+as for a standard option. For example, the following commands set a
+global value that applies to all subnets.
+
+::
+
+ "Dhcp6": {
+ "option-data": [
+ {
+ "name": "foo",
+ "code": 100,
+ "space": "dhcp6",
+ "csv-format": true,
+ "data": "12345"
+ },
+ ...
+ ],
+ ...
+ }
+
+New options can take more complex forms than the simple use of primitives
+(uint8, string, ipv6-address, etc.); it is possible to define an option
+comprising a number of existing primitives.
+
+For example, say we want to define a new option that will consist of
+an IPv6 address, followed by an unsigned 16-bit integer, followed by a
+boolean value, followed by a text string. Such an option could be
+defined in the following way:
+
+::
+
+ "Dhcp6": {
+ "option-def": [
+ {
+ "name": "bar",
+ "code": 101,
+ "space": "dhcp6",
+ "type": "record",
+ "array": false,
+ "record-types": "ipv6-address, uint16, boolean, string",
+ "encapsulate": ""
+ },
+ ...
+ ],
+ ...
+ }
+
+The ``type`` parameter is set to ``"record"`` to indicate that the option
+contains multiple values of different types. These types are given as a
+comma-separated list in the ``record-types`` field and should be ones
+from those listed in :ref:`dhcp-types`.
+
+The values of the options are set in an ``option-data`` statement as
+follows:
+
+::
+
+ "Dhcp6": {
+ "option-data": [
+ {
+ "name": "bar",
+ "space": "dhcp6",
+ "code": 101,
+ "csv-format": true,
+ "data": "2001:db8:1::10, 123, false, Hello World"
+ }
+ ],
+ ...
+ }
+
+The ``csv-format`` parameter is set to ``true`` to indicate that the ``data``
+field comprises a comma-separated list of values. The values in ``data`` must
+correspond to the types set in the ``record-types`` field of the option
+definition.
+
+When ``array`` is set to ``true`` and ``type`` is set to ``"record"``, the
+last field is an array, i.e. it can contain more than one value, as in:
+
+::
+
+ "Dhcp6": {
+ "option-def": [
+ {
+ "name": "bar",
+ "code": 101,
+ "space": "dhcp6",
+ "type": "record",
+ "array": true,
+ "record-types": "ipv6-address, uint16",
+ "encapsulate": ""
+ },
+ ...
+ ],
+ ...
+ }
+
+The new option content is one IPv6 address followed by one or more 16-bit
+unsigned integers.
+
+.. note::
+
+ In general, boolean values are specified as ``true`` or ``false``,
+ without quotes. Some specific boolean parameters may also accept
+ ``"true"``, ``"false"``, ``0``, ``1``, ``"0"``, and ``"1"``.
+
+.. _dhcp6-vendor-opts:
+
+DHCPv6 Vendor-Specific Options
+------------------------------
+
+Vendor options in DHCPv6 are carried in the Vendor-Specific
+Information option (code 17). The idea behind option 17
+is that each vendor has its own unique set of options with their own custom
+formats. The vendor is identified by a 32-bit unsigned integer called
+``enterprise-number`` or ``vendor-id``.
+
+The standard spaces defined in Kea and their options are:
+
+- ``vendor-2495``: Internet Systems Consortium, Inc. for 4o6 options:
+
++-------------+--------------------+------------------------------------------------------------------------+
+| option code | option name | option description |
++=============+====================+========================================================================+
+| 60000 | 4o6-interface | the name of the 4o6 server's client-facing interface |
++-------------+--------------------+------------------------------------------------------------------------+
+| 60001 | 4o6-source-address | the address that the 4o6 server uses to send packets to the client |
++-------------+--------------------+------------------------------------------------------------------------+
+| 60002 | 4o6-source-port | the port that the 4o6 server opens to send packets to the client |
++-------------+--------------------+------------------------------------------------------------------------+
+
+- ``vendor-4491``: Cable Television Laboratories, Inc. for DOCSIS3 options:
+
++-------------+--------------------+------------------------------------------------------------------------+
+| option code | option name | option description |
++=============+====================+========================================================================+
+| 1 | oro | ORO (or Option Request Option) is used by clients to request a list of |
+| | | options they are interested in. |
++-------------+--------------------+------------------------------------------------------------------------+
+| 2 | tftp-servers | a list of IPv4 addresses of TFTP servers to be used by the cable modem |
++-------------+--------------------+------------------------------------------------------------------------+
+
+The following examples show how to
+define an option ``"foo"`` with code 1 that consists of an IPv6 address,
+an unsigned 16-bit integer, and a string. The ``"foo"`` option is
+conveyed in a Vendor-Specific Information option, which comprises a
+single uint32 value that is set to ``12345``. The sub-option ``"foo"``
+follows the data field holding this value.
+
+The first step is to define the format of the option:
+
+::
+
+ "Dhcp6": {
+ "option-def": [
+ {
+ "name": "foo",
+ "code": 1,
+ "space": "vendor-12345",
+ "type": "record",
+ "array": false,
+ "record-types": "ipv6-address, uint16, string",
+ "encapsulate": ""
+ }
+ ],
+ ...
+ }
+
+Note that the option space is set to ``"vendor-12345"``.
+Once the option format is defined, the next step is to define actual values
+for that option:
+
+::
+
+ "Dhcp6": {
+ "option-data": [
+ {
+ "name": "foo",
+ "space": "vendor-12345",
+ "data": "2001:db8:1::10, 123, Hello World"
+ },
+ ...
+ ],
+ ...
+ }
+
+We should also define a value (``"enterprise-number"``) for the
+Vendor-Specific Information option, to convey the option ``foo``.
+
+::
+
+ "Dhcp6": {
+ "option-data": [
+ {
+ "name": "vendor-opts",
+ "data": "12345"
+ },
+ ...
+ ],
+ ...
+ }
+
+Alternatively, the option can be specified using its code.
+
+::
+
+ "Dhcp6": {
+ "option-data": [
+ {
+ "code": 17,
+ "data": "12345"
+ },
+ ...
+ ],
+ ...
+ }
+
+A common configuration is to set the ``always-send`` flag to ``true``, so the
+vendor option is sent even when the client did not specify it in the query.
+
+This is also how :iscman:`kea-dhcp6` can be configured to send multiple vendor options
+from different vendors, along with each of their specific enterprise number.
+If these options need to be sent by the server regardless of whether the client
+specified any enterprise number, ``"always-send": true`` must be configured
+for the suboptions that will be included in the Vendor-Specific Information option (code 17).
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "option-data": [
+ {
+ "always-send": true,
+ "data": "tagged",
+ "name": "tag",
+ "space": "vendor-2234"
+ },
+ {
+ "always-send": true,
+ "data": "https://example.com:1234/path",
+ "name": "url",
+ "space": "vendor-3561"
+ }
+ ],
+ "option-def": [
+ {
+ "code": 22,
+ "name": "tag",
+ "space": "vendor-2234",
+ "type": "string"
+ },
+ {
+ "code": 11,
+ "name": "url",
+ "space": "vendor-3561",
+ "type": "string"
+ }
+ ]
+ }
+ }
+
+.. note::
+
+ The :iscman:`kea-dhcp6` server is able to recognize multiple Vendor Class
+ options (code 16) with different enterprise numbers in the client requests
+ and to send multiple Vendor-Specific Information options (code 17) in the
+ responses, one for each vendor.
+
+.. _dhcp6-option-spaces:
+
+Nested DHCPv6 Options (Custom Option Spaces)
+--------------------------------------------
+
+It is sometimes useful to define a completely new option space, such as
+when a user creates a new option to convey sub-options that
+use a separate numbering scheme, such as sub-options with codes 1
+and 2. Those option codes conflict with standard DHCPv6 options, so a
+separate option space must be defined.
+
+Note that the creation of a new option space is not required when
+defining sub-options for a standard option, because one is created by
+default if the standard option is meant to convey any sub-options (see
+:ref:`dhcp6-vendor-opts`).
+
+If we want a DHCPv6 option called ``container`` with code 102,
+that conveys two sub-options with codes 1 and 2, we first need to
+define the new sub-options:
+
+::
+
+ "Dhcp6": {
+ "option-def": [
+ {
+ "name": "subopt1",
+ "code": 1,
+ "space": "isc",
+ "type": "ipv6-address",
+ "record-types": "",
+ "array": false,
+ "encapsulate": ""
+ },
+ {
+ "name": "subopt2",
+ "code": 2,
+ "space": "isc",
+ "type": "string",
+ "record-types": "",
+ "array": false,
+ "encapsulate": ""
+ }
+ ],
+ ...
+ }
+
+Note that we have defined the options to belong to a new option space
+(in this case, ``"isc"``).
+
+The next step is to define a regular DHCPv6 option with the desired code
+and specify that it should include options from the new option space:
+
+::
+
+ "Dhcp6": {
+ "option-def": [
+ {
+ "name": "container",
+ "code": 102,
+ "space": "dhcp6",
+ "type": "empty",
+ "array": false,
+ "record-types": "",
+ "encapsulate": "isc"
+ },
+ ...
+ ],
+ ...
+ }
+
+The name of the option space in which the sub-options are defined is set
+in the ``encapsulate`` field. The ``type`` field is set to ``"empty"``, to
+indicate that this option does not carry any data other than
+sub-options.
+
+Finally, we can set values for the new options:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "option-data": [
+ {
+ "name": "subopt1",
+ "code": 1,
+ "space": "isc",
+ "data": "2001:db8::abcd"
+ },
+ {
+ "name": "subopt2",
+ "code": 2,
+ "space": "isc",
+ "data": "Hello world"
+ },
+ {
+ "name": "container",
+ "code": 102,
+ "space": "dhcp6"
+ }
+ ]
+ }
+ }
+
+It is possible to create an option which carries some data in
+addition to the sub-options defined in the encapsulated option space.
+For example, if the ``container`` option from the previous example were
+required to carry a uint16 value as well as the sub-options, the
+``type`` value would have to be set to ``"uint16"`` in the option
+definition. (Such an option would then have the following data
+structure: DHCP header, uint16 value, sub-options.) The value specified
+with the ``data`` parameter — which should be a valid integer enclosed
+in quotes, e.g. ``"123"`` — would then be assigned to the ``uint16`` field in
+the ``container`` option.
+
+.. _dhcp6-option-data-defaults:
+
+Unspecified Parameters for DHCPv6 Option Configuration
+------------------------------------------------------
+
+In many cases it is not required to specify all parameters for an option
+configuration, and the default values can be used. However, it is
+important to understand the implications of not specifying some of them,
+as it may result in configuration errors. The list below explains the
+behavior of the server when a particular parameter is not explicitly
+specified:
+
+- ``name`` - the server requires either an option name or an option code to
+ identify an option. If this parameter is unspecified, the option code
+ must be specified.
+
+- ``code`` - the server requires either an option name or an option code to
+ identify an option; this parameter may be left unspecified if the
+ ``name`` parameter is specified. However, this also requires that the
+ particular option have a definition (either as a standard option or
+ an administrator-created definition for the option using an
+ ``option-def`` structure), as the option definition associates an
+ option with a particular name. It is possible to configure an option
+ for which there is no definition (unspecified option format).
+ Configuration of such options requires the use of the option code.
+
+- ``space`` - if the option space is unspecified it defaults to
+ ``dhcp6``, which is an option space holding standard DHCPv6 options.
+
+- ``data`` - if the option data is unspecified it defaults to an empty
+ value. The empty value is mostly used for the options which have no
+ payload (boolean options), but it is legal to specify empty values
+ for some options which carry variable-length data and for which the
+ specification allows a length of 0. For such options, the data
+ parameter may be omitted in the configuration.
+
+- ``csv-format`` - if this value is not specified, the server
+ assumes that the option data is specified as a list of comma-separated
+ values to be assigned to individual fields of the DHCP option.
+
+.. _dhcp6-t1-t2-times:
+
+Controlling the Values Sent for T1 and T2 Times
+-----------------------------------------------
+
+According to RFC 8415, section 21.4, the recommended T1 and T2 values
+are 50% and 80% of the preferred
+lease time, respectively. Kea can be configured to send values that are
+specified explicitly or that are calculated as percentages of the
+preferred lease time. The server's behavior is determined by a combination
+of configuration parameters, of which T1 and T2 are only two.
+
+The lease's preferred and valid lifetimes are expressed as triplets with
+minimum, default, and maximum values using configuration entries:
+
+- ``min-preferred-lifetime`` - specifies the minimum preferred lifetime (optional).
+
+- ``preferred-lifetime`` - specifies the default preferred lifetime.
+
+- ``max-preferred-lifetime`` - specifies the maximum preferred lifetime (optional).
+
+- ``min-valid-lifetime`` - specifies the minimum valid lifetime (optional).
+
+- ``valid-lifetime`` - specifies the default valid lifetime.
+
+- ``max-valid-lifetime`` - specifies the maximum valid lifetime (optional).
+
+Since Kea 1.9.11, these values may be specified within client classes.
+
+When the client does not specify lifetimes, the default is used.
+A specified lifetime - using the IAADDR or IAPREFIX sub-option with
+non-zero values - uses these values when they are between the configured
+minimum and maximum bounds. Values outside the bounds are rounded up or down as
+needed.
+
+.. note::
+
+ As of Kea 2.3.8, if the preferred-lifetime has not been explicitly specified
+ or the specified value is larger than the value of valid-lifetime, the server
+ will use the value given by 0.625 * valid-lifetime.
+
+To send specific fixed values, use the following two parameters:
+
+- ``renew-timer`` - specifies the value of T1 in seconds.
+
+- ``rebind-timer`` - specifies the value of T2 in seconds.
+
+Any value greater than or equal to zero may be specified for T2.
+T1, if specified, must be less than T2. This flexibility allows
+a use case where administrators want to suppress client renewals and
+rebinds by deferring them beyond the lifespan of the lease. This should
+cause the lease to expire, rather than get renewed by clients. If T1 is
+specified as larger than T2, T1 is silently set to zero in the outbound IA.
+
+In the great majority of cases, the values should follow this rule: T1 < T2 <
+preferred lifetime < valid lifetime. Alternatively, both T1 and T2
+values can be configured to 0, which is a signal to DHCPv6 clients that
+they may renew at their own discretion. However, there are known broken
+client implementations in use that will start renewing immediately.
+Administrators who plan to use T1=T2=0 values should test first and make sure
+their clients behave rationally.
+
+In some rare cases there may be a need to disable a client's ability to
+renew addresses. This is undesired from a protocol perspective and should
+be avoided if possible. However, if necessary, administrators can
+configure the T1 and T2 values to be equal or greater to the valid
+lifetime. Be advised that this will cause clients to occasionally
+lose their addresses, which is generally perceived as poor service.
+However, there may be some rare business cases when this is desired
+(e.g. when it is desirable to intentionally break long-lasting connections).
+
+Calculation of the values is controlled by the following three parameters:
+
+- ``calculate-tee-times`` - when ``true``, T1 and T2 are calculated as
+ percentages of the valid lease time. It defaults to ``true``.
+
+- ``t1-percent`` - the percentage of the valid lease time to use for
+ T1. It is expressed as a real number between 0.0 and 1.0 and must be
+ less than ``t2-percent``. The default value is 0.5, per RFC 8415.
+
+- ``t2-percent`` - the percentage of the valid lease time to use for
+ T2. It is expressed as a real number between 0.0 and 1.0 and must be
+ greater than ``t1-percent``. The default value is 0.8 per RFC 8415.
+
+.. note::
+
+ If both explicit values are specified and
+ ``calculate-tee-times`` is ``true``, the server will use the explicit values.
+ Administrators with a setup where some subnets or shared-networks
+ use explicit values and some use calculated values must
+ not define the explicit values at any level higher than where they
+ will be used. Inheriting them from too high a scope, such as
+ global, will cause them to have values at every level underneath
+ (both shared-networks and subnets), effectively disabling calculated
+ values.
+
+.. _dhcp6-config-subnets:
+
+IPv6 Subnet Selection
+---------------------
+
+The DHCPv6 server may receive requests from local (connected to the same
+subnet as the server) and remote (connected via relays) clients. As the
+server may have many subnet configurations defined, it must select an
+appropriate subnet for a given request.
+
+In IPv4, the server can determine which of the configured subnets are
+local, as there is a reasonable expectation that the server will have a
+(global) IPv4 address configured on the interface. That assumption is not
+true in IPv6; the DHCPv6 server must be able to operate while only using
+link-local addresses. Therefore, an optional ``interface`` parameter is
+available within a subnet definition to designate that a given subnet is
+local, i.e. reachable directly over the specified interface. For
+example, a server that is intended to serve a local subnet over eth0
+may be configured as follows:
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:beef::/48",
+ "pools": [
+ {
+ "pool": "2001:db8:beef::/48"
+ }
+ ],
+ "interface": "eth0"
+ }
+ ],
+ ...
+ }
+
+.. _dhcp6-rapid-commit:
+
+Rapid Commit
+------------
+
+The Rapid Commit option, described in `RFC
+8415 <https://tools.ietf.org/html/rfc8415>`__, is supported by the Kea
+DHCPv6 server. However, support is disabled by default. It can be
+enabled on a per-subnet basis using the ``rapid-commit`` parameter as
+shown below:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:beef::/48",
+ "rapid-commit": true,
+ "pools": [
+ {
+ "pool": "2001:db8:beef::1-2001:db8:beef::10"
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+This setting only affects the subnet for which ``rapid-commit`` is
+set to ``true``. For clients connected to other subnets, the server
+ignores the Rapid Commit option sent by the client and follows the
+4-way exchange procedure, i.e. responds with an Advertise for a Solicit
+containing a Rapid Commit option.
+
+.. _dhcp6-relays:
+
+DHCPv6 Relays
+-------------
+
+A DHCPv6 server with multiple subnets defined must select the
+appropriate subnet when it receives a request from a client. For clients
+connected via relays, two mechanisms are used:
+
+The first uses the ``linkaddr`` field in the ``RELAY_FORW`` message. The name of
+this field is somewhat misleading in that it does not contain a
+link-layer address; instead, it holds an address (typically a global
+address) that is used to identify a link. The DHCPv6 server checks to
+see whether the address belongs to a defined subnet and, if it does,
+that subnet is selected for the client's request.
+
+The second mechanism is based on ``interface-id`` options. While forwarding
+a client's message, relays may insert an ``interface-id`` option into the
+message that identifies the interface on the relay that received the
+message. (Some relays allow configuration of that parameter, but it is
+sometimes hard-coded and may range from the very simple [e.g. "vlan100"]
+to the very cryptic; one example seen on real hardware was
+"ISAM144|299|ipv6|nt:vp:1:110".) The server can use this information to
+select the appropriate subnet. The information is also returned to the
+relay, which then knows the interface to use to transmit the response to
+the client. For this to work successfully, the relay interface IDs must
+be unique within the network and the server configuration must match
+those values.
+
+When configuring the DHCPv6 server, two
+similarly named parameters can be configured for a subnet:
+
+- ``interface`` - defines which local network interface can be used to
+ access a given subnet.
+
+- ``interface-id`` - specifies the content of the ``interface-id`` option
+ used by relays to identify the interface on the relay to which the
+ response packet is sent.
+
+The two are mutually exclusive; a subnet cannot be reachable both
+locally (direct traffic) and via relays (remote traffic). Specifying
+both is a configuration error and the DHCPv6 server will refuse such a
+configuration.
+
+The following example configuration shows how to specify an ``interface-id``
+with a value of "vlan123":
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:beef::/48",
+ "pools": [
+ {
+ "pool": "2001:db8:beef::/48"
+ }
+ ],
+ "interface-id": "vlan123"
+ }
+ ],
+ ...
+ }
+
+.. _dhcp6-rsoo:
+
+Relay-Supplied Options
+----------------------
+
+`RFC 6422 <https://tools.ietf.org/html/rfc6422>`__ defines a mechanism
+called Relay-Supplied DHCP Options. In certain cases relay agents are
+the only entities that may have specific information, and they can
+insert options when relaying messages from the client to the server. The
+server then does certain checks and copies those options to the
+response sent to the client.
+
+There are certain conditions that must be met for the option to be
+included. First, the server must not provide the option itself; in other
+words, if both relay and server provide an option, the server always
+takes precedence. Second, the option must be RSOO-enabled. (RSOO is the
+"Relay Supplied Options option.") IANA maintains a list of RSOO-enabled
+options
+`here <https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#options-relay-supplied>`__.
+However, there may be cases when system administrators want to echo
+other options. Kea can be instructed to treat other options as
+RSOO-enabled; for example, to mark options 110, 120, and 130 as
+RSOO-enabled, the following syntax should be used:
+
+::
+
+ "Dhcp6": {
+ "relay-supplied-options": [ "110", "120", "130" ],
+ ...
+ }
+
+At this time, only option 65 is RSOO-enabled by IANA. This option
+will always be treated as RSOO-enabled, so there is no need to explicitly mark
+it. When enabling standard options, it is also possible to use their
+names rather than their option code, e.g. use ``dns-servers`` instead of
+``23``. See ref:`dhcp6-std-options-list` for the names. In
+certain cases this may also work for custom options, but due to the
+nature of the parser code this may be unreliable and should be avoided.
+
+.. _dhcp6-client-classifier:
+
+Client Classification in DHCPv6
+-------------------------------
+
+The DHCPv6 server includes support for client classification. For a
+deeper discussion of the classification process, see :ref:`classify`.
+
+In certain cases it is useful to configure the server to differentiate
+between DHCP client types and treat them accordingly. Client
+classification can be used to modify the behavior of almost any part of
+DHCP message processing. Kea currently offers
+three mechanisms that take advantage of client classification in DHCPv6:
+subnet selection, address pool selection, and DHCP options assignment.
+
+Kea can be instructed to limit access to given subnets based on class
+information. This is particularly useful for cases where two types of
+devices share the same link and are expected to be served from two
+different subnets. The primary use case for such a scenario is cable
+networks, where there are two classes of devices: the cable modem
+itself, which should be handed a lease from subnet A; and all other
+devices behind the modem, which should get leases from subnet B. That
+segregation is essential to prevent overly curious end-users from playing
+with their cable modems. For details on how to set up class restrictions
+on subnets, see :ref:`classification-subnets`.
+
+When subnets belong to a shared network, the classification applies to
+subnet selection but not to pools; that is, a pool in a subnet limited to a
+particular class can still be used by clients which do not belong to the
+class, if the pool they are expected to use is exhausted. The limit
+on access based on class information is also available at the
+address/prefix pool level within a subnet: see :ref:`classification-pools`.
+This is useful when segregating clients belonging to the same
+subnet into different address ranges.
+
+In a similar way, a pool can be constrained to serve only known clients,
+i.e. clients which have a reservation, using the built-in ``KNOWN`` or
+``UNKNOWN`` classes. Addresses can be assigned to registered clients
+without giving a different address per reservation: for instance, when
+there are not enough available addresses. The determination whether
+there is a reservation for a given client is made after a subnet is
+selected, so it is not possible to use ``KNOWN``/``UNKNOWN`` classes to select a
+shared network or a subnet.
+
+The process of classification is conducted in five steps. The first step
+is to assess an incoming packet and assign it to zero or more classes.
+The second step is to choose a subnet, possibly based on the class
+information. When the incoming packet is in the special class ``DROP``,
+it is dropped and a debug message logged.
+The next step is to evaluate class expressions depending on the built-in
+``KNOWN``/``UNKNOWN`` classes after host reservation lookup, using them for
+pool/pd-pool selection and assigning classes from host reservations. The
+list of required classes is then built and each class of the list has
+its expression evaluated; when it returns ``true``, the packet is added as
+a member of the class. The last step is to assign options, again possibly
+based on the class information. More complete and detailed information
+is available in :ref:`classify`.
+
+There are two main methods of classification. The first is automatic and
+relies on examining the values in the vendor class options or the
+existence of a host reservation. Information from these options is
+extracted, and a class name is constructed from it and added to the
+class list for the packet. The second method specifies an expression that is
+evaluated for each packet. If the result is ``true``, the packet is a
+member of the class.
+
+.. note::
+
+ The new ``early-global-reservations-lookup`` global parameter flag
+ enables a lookup for global reservations before the subnet selection
+ phase. This lookup is similar to the general lookup described above
+ with two differences:
+
+ - the lookup is limited to global host reservations
+
+ - the ``UNKNOWN`` class is never set
+
+.. note::
+
+ Care should be taken with client classification, as it is easy for
+ clients that do not meet class criteria to be denied all service.
+
+Defining and Using Custom Classes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The following example shows how to configure a class using an expression
+and a subnet using that class. This configuration defines the class
+named ``Client_enterprise``. 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 address
+from 2001:db8:1::0 to 2001:db8:1::FFFF and the addresses of their DNS
+servers set to 2001:db8:0::1 and 2001:db8:2::1.
+
+::
+
+ "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": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "pools": [ { "pool": "2001:db8:1::-2001:db8:1::ffff" } ],
+ "client-class": "Client_enterprise"
+ }
+ ],
+ ...
+ }
+
+This example shows a configuration using an automatically generated
+``VENDOR_CLASS_`` class. The administrator of the network has decided that
+addresses in the range 2001:db8:1::1 to 2001:db8:1::ffff are to be
+managed by the DHCPv6 server and that only clients belonging to the
+eRouter1.0 client class are allowed to use that pool.
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "pools": [
+ {
+ "pool": "2001:db8:1::-2001:db8:1::ffff"
+ }
+ ],
+ "client-class": "VENDOR_CLASS_eRouter1.0"
+ }
+ ],
+ ...
+ }
+
+.. _dhcp6-required-class:
+
+Required Classification
+~~~~~~~~~~~~~~~~~~~~~~~
+
+In some cases it is useful to limit the scope of a class to a
+shared network, subnet, or pool. There are two parameters which are used
+to limit the scope of the class by instructing the server to evaluate test
+expressions when required.
+
+The first one is the per-class ``only-if-required`` flag, which is ``false``
+by default. When it is set to ``true``, the test expression of the class
+is not evaluated at the reception of the incoming packet but later, and
+only if the class evaluation is required.
+
+The second is ``require-client-classes``, which takes a list of class
+names and is valid in shared-network, subnet, and pool scope. Classes in
+these lists are marked as required and evaluated after selection of this
+specific shared network/subnet/pool and before output-option processing.
+
+In this example, a class is assigned to the incoming packet when the
+specified subnet is used:
+
+::
+
+ "Dhcp6": {
+ "client-classes": [
+ {
+ "name": "Client_foo",
+ "test": "member('ALL')",
+ "only-if-required": true
+ },
+ ...
+ ],
+ "subnet6": [
+ {
+ "subnet": "2001:db8:1::/64",
+ "pools": [
+ {
+ "pool": "2001:db8:1::-2001:db8:1::ffff"
+ }
+ ],
+ "require-client-classes": [ "Client_foo" ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+Required evaluation can be used to express complex dependencies like
+subnet membership. It can also be used to reverse the
+precedence; if ``option-data`` is set in a subnet, it takes precedence
+over ``option-data`` in a class. If ``option-data`` is moved to a
+required class and required in the subnet, a class evaluated earlier
+may take precedence.
+
+Required evaluation is also available at shared-network and pool/pd-pool
+levels. The order in which required classes are considered is:
+shared-network, subnet, and (pd-)pool, i.e. in the reverse order from the
+way in which ``option-data`` is processed.
+
+.. _dhcp6-ddns-config:
+
+DDNS for DHCPv6
+---------------
+
+As mentioned earlier, :iscman:`kea-dhcp6` can be configured to generate requests
+to the DHCP-DDNS server, :iscman:`kea-dhcp-ddns`, (referred to herein as "D2") to
+update DNS entries. These requests are known as NameChangeRequests or
+NCRs. Each NCR contains the following information:
+
+1. Whether it is a request to add (update) or remove DNS entries.
+
+2. Whether the change requests forward DNS updates (AAAA records), reverse
+ DNS updates (PTR records), or both.
+
+3. The Fully Qualified Domain Name (FQDN), lease address, and DHCID
+ (information identifying the client associated with the FQDN).
+
+DDNS-related parameters are split into two groups:
+
+1. Connectivity Parameters
+
+ These are parameters which specify where and how :iscman:`kea-dhcp6` connects to
+ and communicates with D2. These parameters can only be specified
+ within the top-level ``dhcp-ddns`` section in the :iscman:`kea-dhcp6`
+ configuration. The connectivity parameters are listed below:
+
+ - ``enable-updates``
+ - ``server-ip``
+ - ``server-port``
+ - ``sender-ip``
+ - ``sender-port``
+ - ``max-queue-size``
+ - ``ncr-protocol``
+ - ``ncr-format"``
+
+2. Behavioral Parameters
+
+ These parameters influence behavior such as how client host names and
+ FQDN options are handled. They have been moved out of the ``dhcp-ddns``
+ section so that they may be specified at the global, shared-network,
+ and/or subnet levels. Furthermore, they are inherited downward from global to
+ shared-network to subnet. In other words, if a parameter is not specified at
+ a given level, the value for that level comes from the level above it.
+ The behavioral parameters are as follows:
+
+ - ``ddns-send-updates``
+ - ``ddns-override-no-update``
+ - ``ddns-override-client-update``
+ - ``ddns-replace-client-name"``
+ - ``ddns-generated-prefix``
+ - ``ddns-qualifying-suffix``
+ - ``ddns-update-on-renew``
+ - ``ddns-conflict-resolution-mode``
+ - ``ddns-ttl-percent``
+ - ``hostname-char-set``
+ - ``hostname-char-replacement``
+
+.. note::
+
+ For backward compatibility, configuration parsing still recognizes
+ the original behavioral parameters specified in ``dhcp-ddns``,
+ by translating the parameter into its global equivalent. If a
+ parameter is specified both globally and in ``dhcp-ddns``, the latter
+ value is ignored. In either case, a log is emitted explaining
+ what has occurred. Specifying these values within ``dhcp-ddns`` is
+ deprecated and support for it will be removed.
+
+The default configuration and values would appear as follows:
+
+::
+
+ "Dhcp6": {
+ "dhcp-ddns": {
+ // Connectivity parameters
+ "enable-updates": false,
+ "server-ip": "127.0.0.1",
+ "server-port":53001,
+ "sender-ip":"",
+ "sender-port":0,
+ "max-queue-size":1024,
+ "ncr-protocol":"UDP",
+ "ncr-format":"JSON"
+ },
+
+ // Behavioral parameters (global)
+ "ddns-send-updates": true,
+ "ddns-override-no-update": false,
+ "ddns-override-client-update": false,
+ "ddns-replace-client-name": "never",
+ "ddns-generated-prefix": "myhost",
+ "ddns-qualifying-suffix": "",
+ "ddns-update-on-renew": false,
+ "ddns-conflict-resolution-mode": "check-with-dhcid",
+ "hostname-char-set": "",
+ "hostname-char-replacement": "",
+ ...
+ }
+
+There are two parameters which determine if :iscman:`kea-dhcp6`
+can generate DDNS requests to D2: the existing ``dhcp-ddns:enable-updates``
+parameter, which now only controls whether :iscman:`kea-dhcp6` connects to D2;
+and the new behavioral parameter, ``ddns-send-updates``, which determines
+whether DDNS updates are enabled at a given level (i.e. global, shared-network,
+or subnet). The following table shows how the two parameters function
+together:
+
+.. table:: Enabling and disabling DDNS updates
+
+ +-----------------+--------------------+-------------------------------------+
+ | dhcp-ddns: | Global | Outcome |
+ | enable-updates | ddns-send-updates | |
+ +=================+====================+=====================================+
+ | false (default) | false | no updates at any scope |
+ +-----------------+--------------------+-------------------------------------+
+ | false | true (default) | no updates at any scope |
+ +-----------------+--------------------+-------------------------------------+
+ | true | false | updates only at scopes with |
+ | | | a local value of ``true`` for |
+ | | | ``ddns-enable-updates`` |
+ +-----------------+--------------------+-------------------------------------+
+ | true | true | updates at all scopes except those |
+ | | | with a local value of ``false`` |
+ | | | for ``ddns-enable-updates`` |
+ +-----------------+--------------------+-------------------------------------+
+
+Kea 1.9.1 added two new parameters; the first is ``ddns-update-on-renew``.
+Normally, when leases are renewed, the server only updates DNS if the DNS
+information for the lease (e.g. FQDN, DNS update direction flags) has changed.
+Setting ``ddns-update-on-renew`` to ``true`` instructs the server to always update
+the DNS information when a lease is renewed, even if its DNS information has not
+changed. This allows Kea to "self-heal" if it was previously unable
+to add DNS entries or they were somehow lost by the DNS server.
+
+.. note::
+
+ Setting ``ddns-update-on-renew`` to ``true`` may impact performance, especially
+ for servers with numerous clients that renew often.
+
+The second parameter added in Kea 1.9.1 is ``ddns-use-conflict-resolution``. This
+boolean parameter was passed through to D2 and enabled or disabled conflict resolution
+as described in `RFC 4703 <https://tools.ietf.org/html/rfc4703>`__. Beginning with
+Kea 2.5.0, it is deprecated and replaced by ``ddns-conflict-resolution-mode`` which
+offers four modes of conflict resolution-related behavior:
+
+ - ``check-with-dhcid`` - The default mode, it instructs D2 to carry out RFC
+ 4703-compliant conflict resolution. Existing DNS entries may only be
+ overwritten if they have a DHCID record and it matches the client's DHCID.
+ This is equivalent to ``ddns-use-conflict-resolution``: true;
+
+ - ``no-check-with-dhcid`` - Existing DNS entries may be overwritten by any
+ client, whether or not those entries include a DHCID record. The new entries
+ will include a DHCID record for the client to whom they belong.
+ This is equivalent to ``ddns-use-conflict-resolution``: false;
+
+ - ``check-exists-with-dhcid`` - Existing DNS entries may only be overwritten
+ if they have a DHCID record. The DHCID record need not match the client's DHCID.
+ This mode provides a way to protect static DNS entries (those that do not have
+ a DHCID record) while allowing dynamic entries (those that do have a DHCID
+ record) to be overwritten by any client. This behavior was not supported
+ prior to Kea 2.4.0.
+
+ - ``no-check-without-dhcid`` - Existing DNS entries may be overwritten by
+ any client. New entries will not include DHCID records. This behavior was
+ not supported prior to Kea 2.4.0.
+
+.. note::
+
+ For backward compatibility, ddns-use-conflict-resolution is still accepted in
+ JSON configuration. The server will replace the value internally, with the
+ ``ddns-conflict-resolution-mode`` and an appropriate value: `
+ `check-with-dhcid`` for ``true`` and ``no-check-with-dhcid`` for ``false``.
+
+.. note::
+
+ Setting ``ddns-conflict-resolution-mode`` to any value other than
+ ``check-with-dhcid`` disables the one or more overwrite safeguards
+ that the rules of conflict resolution (from
+ `RFC 4703 <https://tools.ietf.org/html/rfc4703>`__) are intended to
+ prevent. This means that existing entries for an FQDN or an
+ IP address made for Client-A can be deleted or replaced by entries
+ for Client-B. Furthermore, there are two scenarios by which entries
+ for multiple clients for the same key (e.g. FQDN or IP) can be created.
+
+ 1. Client-B uses the same FQDN as Client-A but a different IP address.
+ In this case, the forward DNS entries (AAAA and DHCID RRs) for
+ Client-A will be deleted as they match the FQDN and new entries for
+ Client-B will be added. The reverse DNS entries (PTR and DHCID RRs)
+ for Client-A, however, will not be deleted as they belong to a different
+ IP address, while new entries for Client-B will still be added.
+
+ 2. Client-B uses the same IP address as Client-A but a different FQDN.
+ In this case the reverse DNS entries (PTR and DHCID RRs) for Client-A
+ will be deleted as they match the IP address, and new entries for
+ Client-B will be added. The forward DNS entries (AAAA and DHCID RRs)
+ for Client-A, however, will not be deleted, as they belong to a different
+ FQDN, while new entries for Client-B will still be added.
+
+ Disabling conflict resolution should be done only after careful review of
+ specific use cases. The best way to avoid unwanted DNS entries is to
+ always ensure lease changes are processed through Kea, whether they are
+ released, expire, or are deleted via the :isccmd:`lease6-del` command, prior to
+ reassigning either FQDNs or IP addresses. Doing so causes :iscman:`kea-dhcp6`
+ to generate DNS removal requests to D2.
+
+The DNS entries Kea creates contain a value for TTL (time to live).
+The :iscman:`kea-dhcp6` server calculates that value based on
+`RFC 4702, Section 5 <https://tools.ietf.org/html/rfc4702#section-5>`__,
+which suggests that the TTL value be 1/3 of the lease's lifetime, with
+a minimum value of 10 minutes.
+
+The parameter ``ddns-ttl-percent``, when specified,
+causes the TTL to be calculated as a simple percentage of the lease's
+lifetime, using the parameter's value as the percentage. It is specified
+as a decimal percent (e.g. .25, .75, 1.00) and may be specified at the
+global, shared-network, and subnet levels. By default it is unspecified.
+
+.. _dhcpv6-d2-io-config:
+
+DHCP-DDNS Server Connectivity
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For NCRs to reach the D2 server, :iscman:`kea-dhcp6` must be able to communicate
+with it. :iscman:`kea-dhcp6` uses the following configuration parameters to
+control this communication:
+
+- ``enable-updates`` - Enables connectivity to :iscman:`kea-dhcp-ddns` such that DDNS
+ updates can be constructed and sent.
+ It must be ``true`` for NCRs to be generated and sent to D2.
+ It defaults to ``false``.
+
+- ``server-ip`` - This is the IP address on which D2 listens for requests. The
+ default is the local loopback interface at address 127.0.0.1.
+ Either an IPv4 or IPv6 address may be specified.
+
+- ``server-port`` - This is the port on which D2 listens for requests. The default
+ value is ``53001``.
+
+- ``sender-ip`` - This is the IP address which :iscman:`kea-dhcp6` uses to send requests to
+ D2. The default value is blank, which instructs :iscman:`kea-dhcp6` to select a
+ suitable address.
+
+- ``sender-port`` - This is the port which :iscman:`kea-dhcp6` uses to send requests to D2.
+ The default value of ``0`` instructs :iscman:`kea-dhcp6` to select a suitable port.
+
+- ``max-queue-size`` - This is the maximum number of requests allowed to queue
+ while waiting to be sent to D2. This value guards against requests
+ accumulating uncontrollably if they are being generated faster than
+ they can be delivered. If the number of requests queued for
+ transmission reaches this value, DDNS updating is turned off
+ until the queue backlog has been sufficiently reduced. The intent is
+ to allow the :iscman:`kea-dhcp4` server to continue lease operations without
+ running the risk that its memory usage grows without limit. The
+ default value is ``1024``.
+
+- ``ncr-protocol`` - This specifies the socket protocol to use when sending requests to
+ D2. Currently only UDP is supported.
+
+- ``ncr-format`` - This specifies the packet format to use when sending requests to D2.
+ Currently only JSON format is supported.
+
+By default, :iscman:`kea-dhcp-ddns` is assumed to be running on the same machine
+as :iscman:`kea-dhcp6`, and all of the default values mentioned above should be
+sufficient. If, however, D2 has been configured to listen on a different
+address or port, these values must be altered accordingly. For example, if
+D2 has been configured to listen on 2001:db8::5 port 900, the following
+configuration is required:
+
+::
+
+ "Dhcp6": {
+ "dhcp-ddns": {
+ "server-ip": "2001:db8::5",
+ "server-port": 900,
+ ...
+ },
+ ...
+ }
+
+.. _dhcpv6-d2-rules-config:
+
+When Does the :iscman:`kea-dhcp6` Server Generate a DDNS Request?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :iscman:`kea-dhcp6` server follows the behavior prescribed for DHCP servers in
+`RFC 4704 <https://tools.ietf.org/html/rfc4704>`__. It is important to keep
+in mind that :iscman:`kea-dhcp6` makes the initial decision of when and what to
+update and forwards that information to D2 in the form of NCRs. Carrying
+out the actual DNS updates and dealing with such things as conflict
+resolution are within the purview of D2 itself
+(see :ref:`dhcp-ddns-server`). This section describes when :iscman:`kea-dhcp6`
+generates NCRs and the configuration parameters that can be used to
+influence this decision. It assumes that both the connectivity parameter
+``enable-updates`` and the behavioral parameter ``ddns-send-updates``,
+are ``true``.
+
+.. note::
+
+ Currently the interface between :iscman:`kea-dhcp6` and D2 only supports
+ requests which update DNS entries for a single IP address. If a lease
+ grants more than one address, :iscman:`kea-dhcp6` creates the DDNS update
+ request for only the first of these addresses.
+
+In general, :iscman:`kea-dhcp6` generates DDNS update requests when:
+
+1. A new lease is granted in response to a DHCPREQUEST;
+
+2. An existing lease is renewed but the FQDN associated with it has
+ changed; or
+
+3. An existing lease is released in response to a DHCPRELEASE.
+
+In the second case, lease renewal, two DDNS requests are issued: one
+request to remove entries for the previous FQDN, and a second request to
+add entries for the new FQDN. In the third case, a lease release - a
+single DDNS request - to remove its entries will be made.
+
+As for the first case, the decisions involved when granting a new lease are
+more complex. When a new lease is granted, :iscman:`kea-dhcp6` generates a
+DDNS update request only if the DHCPREQUEST contains the FQDN option
+(code 39).
+By default, :iscman:`kea-dhcp6` respects the FQDN N and S flags
+specified by the client as shown in the following table:
+
+.. table:: Default FQDN flag behavior
+
+ +------------+-----------------+-----------------+-------------+
+ | Client | Client Intent | Server Response | Server |
+ | Flags:N-S | | | Flags:N-S-O |
+ +============+=================+=================+=============+
+ | 0-0 | Client wants to | Server | 1-0-0 |
+ | | do forward | generates | |
+ | | updates, server | reverse-only | |
+ | | should do | request | |
+ | | reverse updates | | |
+ +------------+-----------------+-----------------+-------------+
+ | 0-1 | Server should | Server | 0-1-0 |
+ | | do both forward | generates | |
+ | | and reverse | request to | |
+ | | updates | update both | |
+ | | | directions | |
+ +------------+-----------------+-----------------+-------------+
+ | 1-0 | Client wants no | Server does not | 1-0-0 |
+ | | updates done | generate a | |
+ | | | request | |
+ +------------+-----------------+-----------------+-------------+
+
+The first row in the table above represents "client delegation." Here
+the DHCP client states that it intends to do the forward DNS updates and
+the server should do the reverse updates. By default, :iscman:`kea-dhcp6`
+honors the client's wishes and generates a DDNS request to the D2 server
+to update only reverse DNS data. The parameter
+``ddns-override-client-update`` can be used to instruct the server to
+override client delegation requests. When this parameter is ``true``,
+:iscman:`kea-dhcp6` disregards requests for client delegation and generates a
+DDNS request to update both forward and reverse DNS data. In this case,
+the N-S-O flags in the server's response to the client will be 0-1-1
+respectively.
+
+(Note that the flag combination N=1, S=1 is prohibited according to `RFC
+4702 <https://tools.ietf.org/html/rfc4702>`__. If such a combination is
+received from the client, the packet will be dropped by :iscman:`kea-dhcp6`.)
+
+To override client delegation, set the following values in the
+configuration file:
+
+::
+
+ "Dhcp6": {
+ "ddns-override-client-update": true,
+ ...
+ }
+
+The third row in the table above describes the case in which the client
+requests that no DNS updates be done. The parameter
+``ddns-override-no-update`` can be used to instruct the server to disregard
+the client's wishes. When this parameter is ``true``, :iscman:`kea-dhcp6`
+generates DDNS update requests to :iscman:`kea-dhcp-ddns` even if the client
+requests that no updates be done. The N-S-O flags in the server's response to
+the client will be 0-1-1.
+
+To override client delegation, issue the following commands:
+
+::
+
+ "Dhcp6": {
+ "ddns-override-no-update": true,
+ ...
+ }
+
+The :iscman:`kea-dhcp6` server always generates DDNS update requests if the
+client request only contains the Host Name option. In addition, it includes
+an FQDN option in the response to the client with the FQDN N-S-O flags
+set to 0-1-0, respectively. The domain name portion of the FQDN option
+is the name submitted to D2 in the DDNS update request.
+
+.. _dhcpv6-fqdn-name-generation:
+
+:iscman:`kea-dhcp6` Name Generation for DDNS Update Requests
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Each NameChangeRequest must of course include the fully qualified domain
+name whose DNS entries are to be affected. :iscman:`kea-dhcp6` can be configured
+to supply a portion or all of that name, based on what it receives
+from the client in the DHCPREQUEST.
+
+The default rules for constructing the FQDN that will be used for DNS
+entries are:
+
+1. If the DHCPREQUEST contains the client FQDN option, take the
+ candidate name from there.
+
+2. If the candidate name is a partial (i.e. unqualified) name, then add
+ a configurable suffix to the name and use the result as the FQDN.
+
+3. If the candidate name provided is empty, generate an FQDN using a
+ configurable prefix and suffix.
+
+4. If the client provides neither option, then take no DNS action.
+
+These rules can be amended by setting the ``ddns-replace-client-name``
+parameter, which provides the following modes of behavior:
+
+- ``never`` - use the name the client sent. If the client sent no name,
+ do not generate one. This is the default mode.
+
+- ``always`` - replace the name the client sent. If the client sent no
+ name, generate one for the client.
+
+- ``when-present`` - replace the name the client sent. If the client
+ sent no name, do not generate one.
+
+- ``when-not-present`` - use the name the client sent. If the client
+ sent no name, generate one for the client.
+
+.. note::
+
+ In early versions of Kea, this parameter was a boolean and permitted only
+ values of ``true`` and ``false``. Boolean values have been deprecated
+ and are no longer accepted. Administrators currently using booleans
+ must replace them with the desired mode name. A value of ``true``
+ maps to ``when-present``, while ``false`` maps to ``never``.
+
+For example, to instruct :iscman:`kea-dhcp6` to always generate the FQDN for a
+client, set the parameter ``ddns-replace-client-name`` to ``always`` as
+follows:
+
+::
+
+ "Dhcp6": {
+ "ddns-replace-client-name": "always",
+ ...
+ }
+
+The prefix used in the generation of an FQDN is specified by the
+``ddns-generated-prefix`` parameter. The default value is "myhost". To alter
+its value, simply set it to the desired string:
+
+::
+
+ "Dhcp6": {
+ "ddns-generated-prefix": "another.host",
+ ...
+ }
+
+The suffix used when generating an FQDN, or when qualifying a partial
+name, is specified by the ``ddns-qualifying-suffix`` parameter. It is
+strongly recommended that the user supply a value for the qualifying
+suffix when DDNS updates are enabled. For obvious reasons, we cannot
+supply a meaningful default.
+
+::
+
+ "Dhcp6": {
+ "ddns-qualifying-suffix": "foo.example.org",
+ ...
+ }
+
+When qualifying a partial name, :iscman:`kea-dhcp6` constructs the name in the
+format:
+
+``[candidate-name].[ddns-qualifying-suffix].``
+
+where ``candidate-name`` is the partial name supplied in the DHCPREQUEST.
+For example, if the FQDN domain name value is "some-computer" and the
+``ddns-qualifying-suffix`` is "example.com", the generated FQDN is:
+
+``some-computer.example.com.``
+
+When generating the entire name, :iscman:`kea-dhcp6` constructs the name in
+the format:
+
+``[ddns-generated-prefix]-[address-text].[ddns-qualifying-suffix].``
+
+where ``address-text`` is simply the lease IP address converted to a
+hyphenated string. For example, if the lease address is 3001:1::70E, the
+qualifying suffix is "example.com", and the default value is used for
+``ddns-generated-prefix``, the generated FQDN is:
+
+``myhost-3001-1--70E.example.com.``
+
+.. _dhcp6-host-name-sanitization:
+
+Sanitizing Client FQDN Names
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some DHCP clients may provide values in the name component of the FQDN
+option (option code 39) that contain undesirable
+characters. It is possible to configure :iscman:`kea-dhcp6` to sanitize these
+values. The most typical use case is ensuring that only characters that
+are permitted by RFC 1035 be included: A-Z, a-z, 0-9, and "-". This may be
+accomplished with the following two parameters:
+
+- ``hostname-char-set`` - a regular expression describing the invalid
+ character set. This can be any valid, regular expression using POSIX
+ extended expression syntax. Embedded nulls (0x00) are always
+ considered an invalid character to be replaced (or omitted).
+ The default is ``"[^A-Za-z0-9.-]"``. This matches any character that is not
+ a letter, digit, dot, hyphen, or null.
+
+- ``hostname-char-replacement`` - a string of zero or more characters
+ with which to replace each invalid character in the host name. An empty
+ string causes invalid characters to be OMITTED rather than replaced.
+ The default is ``""``.
+
+The following configuration replaces anything other than a letter,
+digit, dot, or hyphen with the letter "x":
+::
+
+ "Dhcp6": {
+ "hostname-char-set": "[^A-Za-z0-9.-]",
+ "hostname-char-replacement": "x",
+ ...
+ }
+
+Thus, a client-supplied value of "myhost-$[123.org" would become
+"myhost-xx123.org". Sanitizing is performed only on the portion of the
+name supplied by the client, and it is performed before applying a
+qualifying suffix (if one is defined and needed).
+
+.. note::
+
+ Name sanitizing is meant to catch the more common cases of invalid
+ characters through a relatively simple character-replacement scheme.
+ It is difficult to devise a scheme that works well in all cases.
+ Administrators who find they have clients with odd corner cases of
+ character combinations that cannot be readily handled with this
+ mechanism should consider writing a hook that can carry out
+ sufficiently complex logic to address their needs.
+
+ Make sure that the dot, "." is considered a valid character by the
+ ``hostname-char-set`` expression, such as this: ``"[^A-Za-z0-9.-]"``.
+ When scrubbing FQDNs, dots are treated as delimiters and used to separate
+ the option value into individual domain labels that are scrubbed and
+ then re-assembled.
+
+ If clients are sending values that differ only by characters
+ considered as invalid by the ``hostname-char-set``, be aware that
+ scrubbing them will yield identical values. In such cases, DDNS
+ conflict rules will permit only one of them to register the name.
+
+ Finally, given the latitude clients have in the values they send, it
+ is virtually impossible to guarantee that a combination of these two
+ parameters will always yield a name that is valid for use in DNS. For
+ example, using an empty value for ``hostname-char-replacement`` could
+ yield an empty domain label within a name, if that label consists
+ only of invalid characters.
+
+.. note::
+
+ It is possible to specify ``hostname-char-set``
+ and/or ``hostname-char-replacement`` at the global scope. This allows
+ host names to be sanitized without requiring a ``dhcp-ddns`` entry. When
+ a ``hostname-char`` parameter is defined at both the global scope and
+ in a ``dhcp-ddns`` entry, the second (local) value is used.
+
+ For the ability to generate host names procedurally, based on an expression, and
+ for the ability to skip DDNS updates on a per-client basis, or fine-tuning various
+ DNS update aspects, the :iscman:`kea-dhcp6` can load the premium hook library
+ `libdhcp_ddns_tuning.so` which is available from ISC. Please refer to
+ :ref:`hooks-ddns-tuning` documentation for the configuration options.
+
+.. _dhcp6-dhcp4o6-config:
+
+DHCPv4-over-DHCPv6: DHCPv6 Side
+-------------------------------
+
+The support of DHCPv4-over-DHCPv6 transport is described in `RFC
+7341 <https://tools.ietf.org/html/rfc7341>`__ and is implemented using
+cooperating DHCPv4 and DHCPv6 servers. This section is about the
+configuration of the DHCPv6 side (the DHCPv4 side is described in
+:ref:`dhcp4-dhcp4o6-config`).
+
+.. note::
+
+ DHCPv4-over-DHCPv6 support is experimental and the details of the
+ inter-process communication may change; for instance, the
+ support of port relay (RFC 8357) introduced an incompatible change.
+ Both the DHCPv4 and DHCPv6 sides should be running the same version of Kea.
+
+There is only one specific parameter for the DHCPv6 side:
+``dhcp4o6-port``, which specifies the first of the two consecutive ports
+of the UDP sockets used for the communication between the DHCPv6 and
+DHCPv4 servers. The DHCPv6 server is bound to ::1 on ``port`` and
+connected to ::1 on ``port`` + 1.
+
+Two other configuration entries are generally required: unicast traffic
+support (see :ref:`dhcp6-unicast`) and the DHCP 4o6
+server address option (name "dhcp4o6-server-addr", code 88).
+
+ISC tested the following configuration:
+
+::
+
+ {
+
+ # DHCPv6 conf
+ "Dhcp6": {
+
+ "interfaces-config": {
+ "interfaces": [ "eno33554984/2001:db8:1:1::1" ]
+ },
+
+ "lease-database": {
+ "type": "memfile",
+ "name": "leases6"
+ },
+
+ "preferred-lifetime": 3000,
+ "valid-lifetime": 4000,
+ "renew-timer": 1000,
+ "rebind-timer": 2000,
+
+ "subnet6": [ {
+ "id": 1,
+ "subnet": "2001:db8:1:1::/64",
+ "interface": "eno33554984",
+ "pools": [ { "pool": "2001:db8:1:1::1:0/112" } ]
+ } ],
+
+ "dhcp4o6-port": 6767,
+
+ "option-data": [ {
+ "name": "dhcp4o6-server-addr",
+ "code": 88,
+ "space": "dhcp6",
+ "csv-format": true,
+ "data": "2001:db8:1:1::1"
+ } ],
+
+
+ "loggers": [ {
+ "name": "kea-dhcp6",
+ "output-options": [ {
+ "output": "/tmp/kea-dhcp6.log"
+ } ],
+ "severity": "DEBUG",
+ "debuglevel": 0
+ } ]
+ }
+
+ }
+
+.. note::
+
+ Relayed DHCPv4-QUERY DHCPv6 messages are not supported.
+
+.. _sanity-checks6:
+
+Sanity Checks in DHCPv6
+-----------------------
+
+An important aspect of a well-running DHCP system is an assurance that
+the data remains consistent; however, in some cases it may be convenient
+to tolerate certain inconsistent data. For example, a network
+administrator who temporarily removes a subnet from a configuration
+would not want all the leases associated with it to disappear from the
+lease database. Kea has a mechanism to implement sanity checks for situations
+like this.
+
+Kea supports a configuration scope called ``sanity-checks``.
+A parameter, called ``lease-checks``,
+governs the verification carried out when a new lease is loaded from a
+lease file. This mechanism permits Kea to attempt to correct inconsistent data.
+
+Every subnet has a ``subnet-id`` value; this is how Kea internally
+identifies subnets. Each lease has a ``subnet-id`` parameter as well, which
+identifies the subnet it belongs to. However, if the configuration has
+changed, it is possible that a lease could exist with a ``subnet-id`` but
+without any subnet that matches it. Also, it is possible that the
+subnet's configuration has changed and the ``subnet-id`` now belongs to a
+subnet that does not match the lease.
+
+Kea's corrective algorithm first
+checks to see if there is a subnet with the ``subnet-id`` specified by the
+lease. If there is, it verifies whether the lease belongs to that
+subnet. If not, depending on the ``lease-checks`` setting, the lease is
+discarded, a warning is displayed, or a new subnet is selected for the
+lease that matches it topologically.
+
+Since delegated prefixes do not have to belong to a subnet in which
+they are offered, there is no way to implement such a mechanism for IPv6
+prefixes. As such, the mechanism works for IPv6 addresses only.
+
+There are five levels which are supported:
+
+- ``none`` - do no special checks; accept the lease as is.
+
+- ``warn`` - if problems are detected display a warning, but
+ accept the lease data anyway. This is the default value.
+
+- ``fix`` - if a data inconsistency is discovered, try to
+ correct it. If the correction is not successful, insert the incorrect data
+ anyway.
+
+- ``fix-del`` - if a data inconsistency is discovered, try to
+ correct it. If the correction is not successful, reject the lease.
+ This setting ensures the data's correctness, but some
+ incorrect data may be lost. Use with care.
+
+- ``del`` - if any inconsistency is
+ detected, reject the lease. This is the strictest mode; use with care.
+
+This feature is currently implemented for the memfile backend. The
+sanity check applies to the lease database in memory, not to the lease file,
+i.e. inconsistent leases will stay in the lease file.
+
+An example configuration that sets this parameter looks as follows:
+
+::
+
+ "Dhcp6": {
+ "sanity-checks": {
+ "lease-checks": "fix-del"
+ },
+ ...
+ }
+
+.. _store-extended-info-v6:
+
+Storing Extended Lease Information
+----------------------------------
+To support such features as DHCPv6 Reconfigure
+(`RFC 3315 <https://tools.ietf.org/html/rfc3315>`__) and Leasequery
+(`RFC 5007 <https://tools.ietf.org/html/rfc5007>`__),
+additional information must be stored with each lease. Because the amount
+of information stored for each lease has ramifications in terms of
+performance and system resource consumption, storage of this additional
+information is configurable through the ``store-extended-info`` parameter.
+It defaults to ``false`` and may be set at the global, shared-network, and
+subnet levels.
+
+::
+
+ "Dhcp6": {
+ "store-extended-info": true,
+ ...
+ }
+
+When set to ``true``, information relevant to the DHCPv6 query (e.g. REQUEST, RENEW,
+or REBIND) asking for the lease is added into the lease's ``user-context`` as a
+map element labeled "ISC". Currently, the information contained in the map
+is a list of relays, one for each relay message layer that encloses the
+client query. The lease's
+``user-context`` for a two-hop query might look something like this (shown
+pretty-printed for clarity):
+
+::
+
+ {
+ "ISC": {
+ "relay-info": [
+ {
+ "hop": 3,
+ "link": "2001:db8::1",
+ "peer": "2001:db8::2"
+ },
+ {
+ "hop": 2,
+ "link": "2001:db8::3",
+ "options": "0x00C800080102030405060708",
+ "peer": "2001:db8::4"
+ },
+ {
+ "hop": 1,
+ "link": "2001:db8::5",
+ "options": "0x00250006010203040506003500086464646464646464",
+ "remote-id": "010203040506",
+ "relay-id": "6464646464646464"
+ }
+ ]
+ }
+ }
+
+.. note::
+
+ Prior to Kea version 2.3.2, this entry was named ``relays``; remote and relay
+ identifier options were not decoded.
+
+.. note::
+
+ It is possible that other hook libraries are already using
+ ``user-context``. Enabling ``store-extended-info`` should not interfere with
+ any other ``user-context`` content, as long as it does not also use an element
+ labeled "ISC". In other words, ``user-context`` is intended to be a flexible
+ container serving multiple purposes. As long as no other purpose also
+ writes an "ISC" element to ``user-context`` there should not be a conflict.
+
+Extended lease information is also subject to configurable sanity checking.
+The parameter in the ``sanity-checks`` scope is named ``extended-info-checks``
+and supports these levels:
+
+- ``none`` - do no check nor upgrade. This level should be used only when
+ extended info is not used at all or when no badly formatted extended
+ info, including using the old format, is expected.
+
+- ``fix`` - fix some common inconsistencies and upgrade extended info
+ using the old format to the new one. It is the default level and is
+ convenient when the Leasequery hook library is not loaded.
+
+- ``strict`` - fix all inconsistencies which have an impact on the (Bulk)
+ Leasequery hook library.
+
+- ``pedantic`` - enforce full conformance to the format produced by the
+ Kea code; for instance, no extra entries are allowed with the exception
+ of ``comment``.
+
+.. note::
+
+ This feature is currently implemented only for the memfile
+ backend. The sanity check applies to the lease database in memory,
+ not to the lease file, i.e. inconsistent leases stay in the lease
+ file.
+
+.. _dhcp6-multi-threading-settings:
+
+Multi-Threading Settings
+------------------------
+
+The Kea server can be configured to process packets in parallel using multiple
+threads. These settings can be found under the ``multi-threading`` structure and are
+represented by:
+
+- ``enable-multi-threading`` - use multiple threads to process packets in
+ parallel. The default is ``true``.
+
+- ``thread-pool-size`` - specify the number of threads to process packets in
+ parallel. It may be set to ``0`` (auto-detect), or any positive number that
+ explicitly sets the thread count. The default is ``0``.
+
+- ``packet-queue-size`` - specify the size of the queue used by the thread
+ pool to process packets. It may be set to ``0`` (unlimited), or any positive
+ number that explicitly sets the queue size. The default is ``64``.
+
+An example configuration that sets these parameters looks as follows:
+
+::
+
+ "Dhcp6": {
+ "multi-threading": {
+ "enable-multi-threading": true,
+ "thread-pool-size": 4,
+ "packet-queue-size": 16
+ },
+ ...
+ }
+
+Multi-Threading Settings With Different Database Backends
+---------------------------------------------------------
+
+The Kea DHCPv6 server is benchmarked by ISC to determine which settings
+give the best performance. Although this section describes our results, they are merely
+recommendations and are very dependent on the particular hardware used
+for benchmarking. We strongly advise that administrators run their own performance benchmarks.
+
+A full report of performance results for the latest stable Kea version can be found
+`here <https://reports.kea.isc.org/>`_.
+This includes hardware and benchmark scenario descriptions, as well as
+current results.
+
+After enabling multi-threading, the number of threads is set by the ``thread-pool-size``
+parameter. Results from our experiments show that the best settings for
+:iscman:`kea-dhcp6` are:
+
+- ``thread-pool-size``: 4 when using ``memfile`` for storing leases.
+
+- ``thread-pool-size``: 12 or more when using ``mysql`` for storing leases.
+
+- ``thread-pool-size``: 6 when using ``postgresql``.
+
+Another very important parameter is ``packet-queue-size``; in our benchmarks we
+used it as a multiplier of ``thread-pool-size``. The actual setting strongly depends
+on ``thread-pool-size``.
+
+We saw the best results in our benchmarks with the following settings:
+
+- ``packet-queue-size``: 150 * ``thread-pool-size`` when using ``memfile`` for
+ storing leases; in our case it was 150 * 4 = 600. This means that at any given
+ time, up to 600 packets could be queued.
+
+- ``packet-queue-size``: 200 * ``thread-pool-size`` when using ``mysql`` for
+ storing leases; in our case it was 200 * 12 = 2400. This means that up to
+ 2400 packets could be queued.
+
+- ``packet-queue-size``: 11 * ``thread-pool-size`` when using ``postgresql`` for
+ storing leases; in our case it was 11 * 6 = 66.
+
+Lease Caching
+-------------
+
+Clients that attempt multiple renewals in a short period can cause the server to update
+and write to the database frequently, resulting in a performance impact
+on the server. The cache parameters instruct the DHCP server to avoid
+updating leases too frequently, thus avoiding this behavior. Instead,
+the server assigns the same lease (i.e. reuses it) with no
+modifications except for CLTT (Client Last Transmission Time), which
+does not require disk operations.
+
+The two parameters are the ``cache-threshold`` double and the
+``cache-max-age`` integer; they have no default setting, i.e. the lease caching
+feature must be explicitly enabled. These parameters can be configured
+at the global, shared-network, and subnet levels. The subnet level has
+the precedence over the shared-network level, while the global level is used
+as a last resort. For example:
+
+::
+
+ {
+ "subnet6": [
+ {
+ "subnet": "2001:db8:1:1::/64",
+ "pools": [ { "pool": "2001:db8:1:1::1:0/112" } ],
+ "cache-threshold": .25,
+ "cache-max-age": 600,
+ "valid-lifetime": 2000,
+ ...
+ }
+ ],
+ ...
+ }
+
+When an already-assigned lease can fulfill a client query:
+
+ - any important change, e.g. for DDNS parameter, hostname, or
+ preferred or valid lifetime reduction, makes the lease not reusable.
+
+ - lease age, i.e. the difference between the creation or last modification
+ time and the current time, is computed (elapsed duration).
+
+ - if ``cache-max-age`` is explicitly configured, it is compared with the lease age;
+ leases that are too old are not reusable. This means that the value 0
+ for ``cache-max-age`` disables the lease cache feature.
+
+ - if ``cache-threshold`` is explicitly configured and is between 0.0 and 1.0,
+ it expresses the percentage of the lease valid lifetime which is
+ allowed for the lease age. Values below and including 0.0 and
+ values greater than 1.0 disable the lease cache feature.
+
+In our example, a lease with a valid lifetime of 2000 seconds can be
+reused if it was committed less than 500 seconds ago. With a lifetime
+of 3000 seconds, a maximum age of 600 seconds applies.
+
+In outbound client responses (e.g. DHCPV6_REPLY messages), the used
+preferred and valid lifetimes are the reusable values, i.e. the
+expiration dates do not change.
+
+.. _host-reservation-v6:
+
+Host Reservations in DHCPv6
+===========================
+
+There are many cases where it is useful to provide a configuration on a
+per-host basis. The most obvious one is to reserve a specific, static
+IPv6 address or/and prefix for exclusive use by a given client (host);
+the returning client receives the same address and/or prefix every time,
+and other clients will never get that address. Host
+reservations are also convenient when a host has specific requirements,
+e.g. a printer that needs additional DHCP options or a cable modem that
+needs specific parameters. Yet another possible use case is to define
+unique names for hosts.
+
+There may be cases when a new reservation has been made for a
+client for an address or prefix currently in use by another client. We
+call this situation a "conflict." These conflicts get resolved
+automatically over time, as described in subsequent sections. Once a
+conflict is resolved, the correct client will receive the reserved
+configuration when it renews.
+
+Host reservations are defined as parameters for each subnet. Each host
+must be identified by either DUID or its hardware/MAC address; see
+:ref:`mac-in-dhcpv6` for details. There
+is an optional ``reservations`` array in the ``subnet6`` structure; each
+element in that array is a structure that holds information about reservations for a
+single host. In particular, the structure has an identifier that
+uniquely identifies a host. In the DHCPv6 context, the identifier is
+usually a DUID, but it can also be a hardware or MAC address. One or more
+addresses or prefixes may also be specified, and it is possible to
+specify a hostname and DHCPv6 options for a given host.
+
+.. note::
+
+ The reserved address must be within the subnet.
+ This does not apply to reserved prefixes.
+
+The following example shows how to reserve addresses and prefixes for
+specific hosts:
+
+::
+
+ {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/48",
+ "pools": [ { "pool": "2001:db8:1::/80" } ],
+ "pd-pools": [
+ {
+ "prefix": "2001:db8:1:8000::",
+ "prefix-len": 56,
+ "delegated-len": 64
+ }
+ ],
+ "reservations": [
+ {
+ "duid": "01:02:03:04:05:0A:0B:0C:0D:0E",
+ "ip-addresses": [ "2001:db8:1::100" ]
+ },
+ {
+ "hw-address": "00:01:02:03:04:05",
+ "ip-addresses": [ "2001:db8:1::101", "2001:db8:1::102" ]
+ },
+ {
+ "duid": "01:02:03:04:05:06:07:08:09:0A",
+ "ip-addresses": [ "2001:db8:1::103" ],
+ "prefixes": [ "2001:db8:2:abcd::/64" ],
+ "hostname": "foo.example.com"
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+This example includes reservations for three different clients. The
+first reservation is for the address 2001:db8:1::100, for a client using
+DUID 01:02:03:04:05:0A:0B:0C:0D:0E. The second reservation is for two
+addresses, 2001:db8:1::101 and 2001:db8:1::102, for a client using MAC
+address 00:01:02:03:04:05. Lastly, address 2001:db8:1::103 and prefix
+2001:db8:2:abcd::/64 are reserved for a client using DUID
+01:02:03:04:05:06:07:08:09:0A. The last reservation also assigns a
+hostname to this client.
+
+DHCPv6 allows a single client to lease multiple addresses and
+multiple prefixes at the same time. Therefore ``ip-addresses`` and
+``prefixes`` are plural and are actually arrays. When the client sends
+multiple IA options (IA_NA or IA_PD), each reserved address or prefix is
+assigned to an individual IA of the appropriate type. If the number of
+IAs of a specific type is lower than the number of reservations of that
+type, the number of reserved addresses or prefixes assigned to the
+client is equal to the number of IA_NAs or IA_PDs sent by the client;
+that is, some reserved addresses or prefixes are not assigned. However,
+they still remain reserved for this client and the server will not
+assign them to any other client. If the number of IAs of a specific type
+sent by the client is greater than the number of reserved addresses or
+prefixes, the server will try to assign all reserved addresses or
+prefixes to the individual IAs and dynamically allocate addresses or
+prefixes to the remaining IAs. If the server cannot assign a reserved
+address or prefix because it is in use, the server will select the next
+reserved address or prefix and try to assign it to the client. If the
+server subsequently finds that there are no more reservations that can
+be assigned to the client at that moment, the server will try to assign
+leases dynamically.
+
+Making a reservation for a mobile host that may visit multiple subnets
+requires a separate host definition in each subnet that host is expected to
+visit. It is not possible to define multiple host definitions with the
+same hardware address in a single subnet. Multiple host definitions with
+the same hardware address are valid if each is in a different subnet.
+The reservation for a given host should include only one identifier,
+either DUID or hardware address; defining both for the same host is
+considered a configuration error.
+
+Adding host reservations incurs a performance penalty. In principle,
+when a server that does not support host reservation responds to a
+query, it needs to check whether there is a lease for a given address
+being considered for allocation or renewal. The server that does
+support host reservation has to perform additional checks: not only
+whether the address is currently used (i.e., if there is a lease for
+it), but also whether the address could be used by someone else (i.e.,
+if there is a reservation for it). That additional check incurs extra
+overhead.
+
+.. _reservation6-types:
+
+Address/Prefix Reservation Types
+--------------------------------
+
+In a typical Kea scenario there is an IPv6 subnet defined, with a certain
+part of it dedicated for dynamic address allocation by the DHCPv6
+server. There may be an additional address space defined for prefix
+delegation. Those dynamic parts are referred to as dynamic pools,
+address and prefix pools, or simply pools. In principle, a host
+reservation can reserve any address or prefix that belongs to the
+subnet. The reservations that specify addresses that belong to
+configured pools are called "in-pool reservations." In contrast, those
+that do not belong to dynamic pools are called "out-of-pool
+reservations." There is no formal difference in the reservation syntax
+and both reservation types are handled uniformly.
+
+Kea supports global host reservations. These are reservations that are
+specified at the global level within the configuration and that do not
+belong to any specific subnet. Kea still matches inbound client
+packets to a subnet as before, but when the subnet's reservation mode is
+set to "global", Kea looks for host reservations only among the
+global reservations defined. Typically, such reservations would be used
+to reserve hostnames for clients which may move from one subnet to
+another.
+
+.. note::
+
+ Global reservations, while useful in certain circumstances, have aspects
+ that must be given due consideration when using them. Please see
+ :ref:`reservation6-conflict` for more details.
+
+.. note::
+
+ Since Kea 1.9.1, reservation mode has been replaced by three
+ boolean flags, ``reservations-global``, ``reservations-in-subnet``
+ and ``reservations-out-of-pool``, which allow the configuration of
+ host reservations both globally and in a subnet. In such cases a subnet
+ host reservation has preference over a global reservation
+ when both exist for the same client.
+
+.. _reservation6-conflict:
+
+Conflicts in DHCPv6 Reservations
+--------------------------------
+
+As reservations and lease information are stored separately, conflicts
+may arise. Consider the following series of events: the server has
+configured the dynamic pool of addresses from the range of 2001:db8::10
+to 2001:db8::20. Host A requests an address and gets 2001:db8::10. Now
+the system administrator decides to reserve address 2001:db8::10 for
+Host B. In general, reserving an address that is currently assigned to
+someone else is not recommended, but there are valid use cases where
+such an operation is warranted.
+
+The server now has a conflict to resolve. If Host B boots up and
+requests an address, the server cannot immediately assign the reserved
+address 2001:db8::10. A naive approach would to be immediately remove
+the lease for Host A and create a new one for Host B. That would not
+solve the problem, though, because as soon as Host B gets the address,
+it will detect that the address is already in use (by Host
+A) and will send a DHCPDECLINE message. Therefore, in this situation,
+the server has to temporarily assign a different address from the
+dynamic pool (not matching what has been reserved) to Host B.
+
+When Host A renews its address, the server will discover that the
+address being renewed is now reserved for someone else - Host B.
+The server will remove the lease for 2001:db8::10, select a
+new address, and create a new lease for it. It will send two addresses
+in its response: the old address, with the lifetime set to 0 to explicitly
+indicate that it is no longer valid; and the new address, with a
+non-zero lifetime. When Host B tries to renew its temporarily assigned address,
+the server will detect that the existing lease does not match the
+reservation, so it will release the current address Host B has and will
+create a new lease matching the reservation. As before, the server will
+send two addresses: the temporarily assigned one with a zero lifetime,
+and the new one that matches the reservation with the proper lifetime set.
+
+This recovery will succeed, even if other hosts attempt to get the
+reserved address. If Host C requests the address 2001:db8::10 after the
+reservation is made, the server will propose a different address.
+
+This recovery mechanism allows the server to fully recover from a case
+where reservations conflict with existing leases; however, this procedure
+takes roughly as long as the value set for ``renew-timer``. The
+best way to avoid such a recovery is not to define new reservations that
+conflict with existing leases. Another recommendation is to use
+out-of-pool reservations; if the reserved address does not belong to a
+pool, there is no way that other clients can get it.
+
+.. note::
+
+ The conflict-resolution mechanism does not work for global
+ reservations. Although the global address reservations feature may be useful
+ in certain settings, it is generally recommended not to use
+ global reservations for addresses. Administrators who do choose
+ to use global reservations must manually ensure that the reserved
+ addresses are not in dynamic pools.
+
+.. _reservation6-hostname:
+
+Reserving a Hostname
+--------------------
+
+When the reservation for a client includes the ``hostname``, the server
+assigns this hostname to the client and sends it back in the Client
+FQDN option, if the client included the Client FQDN option in its message
+to the server. The reserved hostname always takes precedence over the
+hostname supplied by the client (via the FQDN option) or the autogenerated
+(from the IPv6 address) hostname.
+
+The server qualifies the reserved hostname with the value of the
+``ddns-qualifying-suffix`` parameter. For example, the following subnet
+configuration:
+
+::
+
+ {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/48",
+ "pools": [ { "pool": "2001:db8:1::/80" } ],
+ "ddns-qualifying-suffix": "example.isc.org.",
+ "reservations": [
+ {
+ "duid": "01:02:03:04:05:0A:0B:0C:0D:0E",
+ "ip-addresses": [ "2001:db8:1::100" ],
+ "hostname": "alice-laptop"
+ }
+ ]
+ }
+ ],
+ "dhcp-ddns": {
+ "enable-updates": true
+ },
+ ...
+ }
+
+will result the "alice-laptop.example.isc.org." hostname being assigned to
+the client using the DUID "01:02:03:04:05:0A:0B:0C:0D:0E". If the
+``ddns-qualifying-suffix`` is not specified, the default (empty) value will
+be used, and in this case the value specified as a ``hostname`` will be
+treated as a fully qualified name. Thus, by leaving the
+``ddns-qualifying-suffix`` empty it is possible to qualify hostnames for
+different clients with different domain names:
+
+.. code-block:: json
+
+ {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/48",
+ "pools": [ { "pool": "2001:db8:1::/80" } ],
+ "reservations": [
+ {
+ "duid": "01:02:03:04:05:0A:0B:0C:0D:0E",
+ "ip-addresses": [ "2001:db8:1::100" ],
+ "hostname": "mark-desktop.example.org."
+ }
+ ]
+ }
+ ],
+ "dhcp-ddns": {
+ "enable-updates": true
+ }
+ }
+
+The above example results in the assignment of the
+"mark-desktop.example.org." hostname to the client using the DUID
+"01:02:03:04:05:0A:0B:0C:0D:0E".
+
+.. _reservation6-options:
+
+Including Specific DHCPv6 Options in Reservations
+-------------------------------------------------
+
+Kea offers the ability to specify options on a per-host basis. These
+options follow the same rules as any other options. These can be
+standard options (see :ref:`dhcp6-std-options`),
+custom options (see :ref:`dhcp6-custom-options`),
+or vendor-specific options (see :ref:`dhcp6-vendor-opts`). The following
+example demonstrates how standard options can be defined.
+
+::
+
+ {
+ "reservations": [
+ {
+ "duid": "01:02:03:05:06:07:08",
+ "ip-addresses": [ "2001:db8:1::2" ],
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "3000:1::234"
+ },
+ {
+ "name": "nis-servers",
+ "data": "3000:1::234"
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+Vendor-specific options can be reserved in a similar manner:
+
+::
+
+ {
+ "reservations": [
+ {
+ "duid": "aa:bb:cc:dd:ee:ff",
+ "ip-addresses": [ "2001:db8::1" ],
+ "option-data": [
+ {
+ "name": "vendor-opts",
+ "data": 4491
+ },
+ {
+ "name": "tftp-servers",
+ "space": "vendor-4491",
+ "data": "3000:1::234"
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+Options defined at the host level have the highest priority. In other words,
+if there are options defined with the same type on global, subnet,
+class, and host levels, the host-specific values are used.
+
+.. _reservation6-client-classes:
+
+Reserving Client Classes in DHCPv6
+----------------------------------
+
+:ref:`classification-using-expressions` explains how to configure
+the server to assign classes to a client, based on the content of the
+options that this client sends to the server. Host reservation
+mechanisms also allow for the static assignment of classes to clients.
+The definitions of these classes are placed in the Kea configuration file or
+a database. The following configuration snippet shows how to specify that
+a client belongs to the classes ``reserved-class1`` and ``reserved-class2``. Those
+classes are associated with specific options sent to the clients which belong
+to them.
+
+::
+
+ {
+ "client-classes": [
+ {
+ "name": "reserved-class1",
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8:1::50"
+ }
+ ]
+ },
+ {
+ "name": "reserved-class2",
+ "option-data": [
+ {
+ "name": "nis-servers",
+ "data": "2001:db8:1::100"
+ }
+ ]
+ }
+ ],
+ "subnet6": [
+ {
+ "id": 1,
+ "pools": [ { "pool": "2001:db8:1::/64" } ],
+ "subnet": "2001:db8:1::/48",
+ "reservations": [
+ {
+ "duid": "01:02:03:04:05:06:07:08",
+
+ "client-classes": [ "reserved-class1", "reserved-class2" ]
+
+ }
+ ]
+ } ]
+ }
+
+In some cases the host reservations can be used in conjunction with client
+classes specified within the Kea configuration. In particular, when a
+host reservation exists for a client within a given subnet, the "KNOWN"
+built-in class is assigned to the client. Conversely, when there is no
+static assignment for the client, the "UNKNOWN" class is assigned to the
+client. Class expressions within the Kea configuration file can
+refer to "KNOWN" or "UNKNOWN" classes using the "member" operator.
+For example:
+
+::
+
+ {
+ "client-classes": [
+ {
+ "name": "dependent-class",
+ "test": "member('KNOWN')",
+ "only-if-required": true
+ }
+ ]
+ }
+
+The ``only-if-required`` parameter is needed here to force
+evaluation of the class after the lease has been allocated and thus the
+reserved class has been also assigned.
+
+.. note::
+
+ The classes specified in non-global host reservations
+ are assigned to the processed packet after all classes with the
+ ``only-if-required`` parameter set to ``false`` have been evaluated.
+ This means that these classes must not depend on the
+ statically assigned classes from the host reservations. If
+ such a dependency is needed, the ``only-if-required`` must
+ be set to ``true`` for the dependent classes. Such classes are
+ evaluated after the static classes have been assigned to the packet.
+ This, however, imposes additional configuration overhead, because
+ all classes marked as ``only-if-required`` must be listed in the
+ ``require-client-classes`` list for every subnet where they are used.
+
+.. note::
+
+ Client classes specified within the Kea configuration file may
+ depend on the classes specified within the global host reservations.
+ In such a case the ``only-if-required`` parameter is not needed.
+ Refer to the :ref:`pool-selection-with-class-reservations6` and
+ :ref:`subnet-selection-with-class-reservations6`
+ for specific use cases.
+
+.. _reservations6-mysql-pgsql:
+
+Storing Host Reservations in MySQL or PostgreSQL
+------------------------------------------------
+
+Kea can store host reservations in MySQL or PostgreSQL.
+See :ref:`hosts6-storage` for information on how to
+configure Kea to use reservations stored in MySQL or PostgreSQL.
+Kea provides a dedicated hook for managing reservations in a
+database; section :ref:`hooks-host-cmds` provides detailed information.
+The `Kea wiki
+<https://gitlab.isc.org/isc-projects/kea/wikis/designs/commands#23-host-reservations-hr-management>`__
+provides some examples of how to conduct common host reservation
+operations.
+
+.. note::
+
+ In Kea, the maximum length of an option specified per-host is
+ arbitrarily set to 4096 bytes.
+
+.. _reservations6-tuning:
+
+Fine-Tuning DHCPv6 Host Reservation
+-----------------------------------
+
+The host reservation capability introduces additional restrictions for
+the allocation engine (the component of Kea that selects an address for
+a client) during lease selection and renewal. In particular, three major
+checks are necessary. First, when selecting a new lease, it is not
+sufficient for a candidate lease to simply not be in use by another DHCP
+client; it also must not be reserved for another client. Similarly, when
+renewing a lease, an additional check must be performed to see whether
+the address being renewed is reserved for another client. Finally, when
+a host renews an address or a prefix, the server must check whether
+there is a reservation for this host, which would mean the existing (dynamically
+allocated) address should be revoked and the reserved one be used
+instead.
+
+Some of those checks may be unnecessary in certain deployments, and not
+performing them may improve performance. The Kea server provides the
+``reservation-mode`` configuration parameter to select the types of
+reservations allowed for a particular subnet. Each reservation type has
+different constraints for the checks to be performed by the server when
+allocating or renewing a lease for the client. Allowed values are:
+
+- ``all`` - enables both in-pool and out-of-pool host reservation
+ types. This setting is the default value, and is the safest and most
+ flexible. However, as all checks are conducted, it is also the slowest.
+ It does not check against global reservations.
+
+- ``out-of-pool`` - allows only out-of-pool host reservations. With
+ this setting in place, the server assumes that all host
+ reservations are for addresses that do not belong to the dynamic
+ pool. Therefore, it can skip the reservation checks when dealing with
+ in-pool addresses, thus improving performance. Do not use this mode
+ if any reservations use in-pool addresses. Caution is advised
+ when using this setting; Kea does not sanity-check the reservations
+ against ``reservation-mode`` and misconfiguration may cause problems.
+
+- ``global`` - allows only global host reservations. With this setting
+ in place, the server searches for reservations for a client only
+ among the defined global reservations. If an address is specified,
+ the server skips the reservation checks carried out in
+ other modes, thus improving performance. Caution is advised when
+ using this setting; Kea does not sanity-check the reservations when
+ ``global`` is set, and misconfiguration may cause problems.
+
+- ``disabled`` - host reservation support is disabled. As there are no
+ reservations, the server skips all checks. Any reservations
+ defined are completely ignored. As checks are skipped, the
+ server may operate faster in this mode.
+
+Since Kea 1.9.1, the ``reservation-mode`` parameter is replaced by the
+``reservations-global``, ``reservations-in-subnet`` and
+``reservations-out-of-pool`` flags.
+The flags can be activated independently and can produce various combinations,
+some of them being unsupported by the deprecated ``reservation-mode``.
+
+The ``reservation-mode`` parameter can be specified at:
+
+- global level: ``.Dhcp6["reservation-mode"]`` (lowest priority: gets overridden
+ by all others)
+
+- subnet level: ``.Dhcp6.subnet6[]["reservation-mode"]`` (low priority)
+
+- shared-network level: ``.Dhcp6["shared-networks"][]["reservation-mode"]``
+ (high priority)
+
+- shared-network subnet-level:
+ ``.Dhcp6["shared-networks"][].subnet6[]["reservation-mode"]`` (highest
+ priority: overrides all others)
+
+To decide which ``"reservation-mode"`` to choose, the
+following decision diagram may be useful:
+
+::
+
+ O
+ |
+ v
+ +-----------------------------+------------------------------+
+ | Is per-host configuration needed, such as |
+ | reserving specific addresses, |
+ | assigning specific options or |
+ | assigning packets to specific classes on per-device basis? |
+ +-+-----------------+----------------------------------------+
+ | |
+ no| yes|
+ | | +--------------------------------------+
+ | | | For all given hosts, |
+ +--> "disabled" +-->+ can the reserved resources |
+ | be used in all configured subnets? |
+ +--------+---------------------------+-+
+ | |
+ +----------------------------+ |no |yes
+ | Is | | |
+ | at least one reservation +<--+ "global" <--+
+ | used to reserve addresses |
+ | or prefixes? |
+ +-+------------------------+-+
+ | |
+ no| yes| +---------------------------+
+ | | | Is high leases-per-second |
+ +--> "out-of-pool" +-->+ performance or efficient |
+ ^ | resource usage |
+ | | (CPU ticks, RAM usage, |
+ | | database roundtrips) |
+ | | important to your setup? |
+ | +-+----------------+--------+
+ | | |
+ | yes| no|
+ | | |
+ | +-------------+ |
+ | | |
+ | | +----------------------+ |
+ | | | Can it be guaranteed | |
+ | +-->+ that the reserved | |
+ | | addresses/prefixes | |
+ | | aren't part of the | |
+ | | pools configured | |
+ | | in the respective | |
+ | | subnet? | |
+ | +-+------------------+-+ |
+ | | | |
+ | yes| no| |
+ | | | V
+ +----------------+ +--> "all"
+
+An example configuration that disables reservations looks as follows:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "pools": [
+ {
+ "pool": "2001:db8:1::-2001:db8:1::100"
+ }
+ ],
+ "reservation-mode": "disabled",
+ "subnet": "2001:db8:1::/64"
+ }
+ ]
+ }
+ }
+
+An example configuration using global reservations is shown below:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "reservation-mode": "global",
+ "reservations": [
+ {
+ "duid": "00:03:00:01:11:22:33:44:55:66",
+ "hostname": "host-one"
+ },
+ {
+ "duid": "00:03:00:01:99:88:77:66:55:44",
+ "hostname": "host-two"
+ }
+ ],
+ "subnet6": [
+ {
+ "id": 1,
+ "pools": [
+ {
+ "pool": "2001:db8:1::-2001:db8:1::100"
+ }
+ ],
+ "subnet": "2001:db8:1::/64"
+ }
+ ]
+ }
+ }
+
+The meaning of the reservation flags are:
+
+- ``reservations-global``: fetch global reservations.
+
+- ``reservations-in-subnet``: fetch subnet reservations. For a shared network
+ this includes all subnet members of the shared network.
+
+- ``reservations-out-of-pool``: this makes sense only when the
+ ``reservations-in-subnet`` flag is ``true``. When ``reservations-out-of-pool``
+ is ``true``, the server assumes that all host reservations are for addresses
+ that do not belong to the dynamic pool. Therefore, it can skip the reservation
+ checks when dealing with in-pool addresses, thus improving performance.
+ The server will not assign reserved addresses that are inside the dynamic
+ pools to the respective clients. This also means that the addresses matching
+ the respective reservations from inside the dynamic pools (if any) can be
+ dynamically assigned to any client.
+
+The ``disabled`` value from the deprecated ``reservation-mode`` corresponds to:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "reservations-global": false,
+ "reservations-in-subnet": false
+ }
+ }
+
+The ``global`` value from the deprecated ``reservation-mode`` corresponds to:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "reservations-global": true,
+ "reservations-in-subnet": false
+ }
+ }
+
+The ``out-of-pool`` value from the deprecated ``reservation-mode`` corresponds to:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "reservations-global": false,
+ "reservations-in-subnet": true,
+ "reservations-out-of-pool": true
+ }
+ }
+
+And the ``all`` value from the deprecated ``reservation-mode`` corresponds to:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "reservations-global": false,
+ "reservations-in-subnet": true,
+ "reservations-out-of-pool": false
+ }
+ }
+
+To activate both ``global`` and ``all``, the following combination can be used:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "reservations-global": true,
+ "reservations-in-subnet": true,
+ "reservations-out-of-pool": false
+ }
+ }
+
+To activate both ``global`` and ``out-of-pool``, the following combination can
+be used:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "reservations-global": true,
+ "reservations-in-subnet": true,
+ "reservations-out-of-pool": true
+ }
+ }
+
+Enabling ``out-of-pool`` and disabling ``in-subnet`` at the same time
+is not recommended because ``out-of-pool`` applies to host reservations in a
+subnet, which are fetched only when the ``in-subnet`` flag is ``true``.
+
+The parameter can be specified at the global, subnet, and shared-network
+levels.
+
+An example configuration that disables reservations looks as follows:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "reservations-global": false,
+ "reservations-in-subnet": false,
+ "subnet": "2001:db8:1::/64",
+ "id": 1
+ }
+ ]
+ }
+ }
+
+An example configuration using global reservations is shown below:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "reservations": [
+ {
+ "duid": "00:03:00:01:11:22:33:44:55:66",
+ "hostname": "host-one"
+ },
+ {
+ "duid": "00:03:00:01:99:88:77:66:55:44",
+ "hostname": "host-two"
+ }
+ ],
+ "reservations-global": true,
+ "reservations-in-subnet": false,
+ "subnet6": [
+ {
+ "pools": [
+ {
+ "pool": "2001:db8:1::-2001:db8:1::100"
+ }
+ ],
+ "subnet": "2001:db8:1::/64",
+ "id": 1
+ }
+ ]
+ }
+ }
+
+For more details regarding global reservations, see :ref:`global-reservations6`.
+
+Another aspect of host reservations is the different types of
+identifiers. Kea currently supports two types of identifiers in DHCPv6:
+hardware address and DUID. This is beneficial from a usability
+perspective; however, there is one drawback. For each incoming packet
+Kea has to extract each identifier type and then query the database
+to see if there is a reservation by this particular identifier. If
+nothing is found, the next identifier is extracted and the next query is
+issued. This process continues until either a reservation is found or
+all identifier types have been checked. Over time, with an increasing
+number of supported identifier types, Kea would become slower and
+slower.
+
+To address this problem, a parameter called
+``host-reservation-identifiers`` is available. It takes a list of
+identifier types as a parameter. Kea checks only those identifier
+types enumerated in ``host-reservation-identifiers``. From a performance
+perspective, the number of identifier types should be kept to a minimum,
+ideally one. If the deployment uses several reservation types, please
+enumerate them from most- to least-frequently used, as this increases
+the chances of Kea finding the reservation using the fewest queries. An
+example of a ``host-reservation-identifiers`` configuration looks as follows:
+
+::
+
+ {
+ "host-reservation-identifiers": [ "duid", "hw-address" ],
+ "subnet6": [
+ {
+ "subnet": "2001:db8:1::/64",
+ ...
+ }
+ ],
+ ...
+ }
+
+If not specified, the default value is:
+
+::
+
+ "host-reservation-identifiers": [ "hw-address", "duid" ]
+
+.. note::
+
+ As soon as a host reservation is found the search is stopped so
+ when a client has two host reservations using different enabled
+ identifier types the first is always returned and the second
+ ignored. In other words, this is usually a configuration mistake.
+ In rare cases when having two reservations for the same host makes sense,
+ you can control which of those will be used by ordering the list of
+ identifier types in `host-reservation-identifiers`.
+
+
+.. _global-reservations6:
+
+Global Reservations in DHCPv6
+-----------------------------
+
+In some deployments, such as mobile, clients can roam within the network
+and certain parameters must be specified regardless of the client's
+current location. To meet such a need, Kea offers a global reservation
+mechanism. The idea behind it is that regular host
+reservations are tied to specific subnets, by using a specific
+subnet ID. Kea can specify a global reservation that can be used in
+every subnet that has global reservations enabled.
+
+This feature can be used to assign certain parameters, such as hostname
+or other dedicated, host-specific options. It can also be used to assign
+addresses or prefixes.
+
+An address assigned via global host reservation must be feasible for the
+subnet the server selects for the client. In other words, the address must
+lie within the subnet; otherwise, it is ignored and the server will
+attempt to dynamically allocate an address. If the selected subnet
+belongs to a shared network, the server checks for feasibility against
+the subnet's siblings, selecting the first in-range subnet. If no such
+subnet exists, the server falls back to dynamically allocating the address.
+This does not apply to globally reserved prefixes.
+
+.. note::
+
+ Prior to release 2.3.5, the server did not perform feasibility checks on
+ globally reserved addresses, which allowed the server to be configured to
+ hand out nonsensical leases for arbitrary address values. Later versions
+ of Kea perform these checks.
+
+To use global host reservations, a configuration similar to the
+following can be used:
+
+::
+
+ "Dhcp6": {
+ # This specifies global reservations.
+ # They will apply to all subnets that
+ # have global reservations enabled.
+
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:ff",
+ "hostname": "hw-host-dynamic"
+ },
+ {
+ "hw-address": "01:02:03:04:05:06",
+ "hostname": "hw-host-fixed",
+
+ # Use of IP addresses in global reservations is risky.
+ # If used outside of matching subnet, such as 3001::/64,
+ # it will result in a broken configuration being handed
+ # to the client.
+ "ip-address": "2001:db8:ff::77"
+ },
+ {
+ "duid": "01:02:03:04:05",
+ "hostname": "duid-host"
+ }
+ ],
+ "valid-lifetime": 600,
+ "subnet4": [ {
+ "subnet": "2001:db8:1::/64",
+ # It is replaced by the "reservations-global",
+ # "reservations-in-subnet", and "reservations-out-of-pool"
+ # parameters.
+ # "reservation-mode": "global",
+ # Specify if the server should look up global reservations.
+ "reservations-global": true,
+ # Specify if the server should look up in-subnet reservations.
+ "reservations-in-subnet": false,
+ # Specify if the server can assume that all reserved addresses
+ # are out-of-pool. It can be ignored because "reservations-in-subnet"
+ # is false.
+ # "reservations-out-of-pool": false,
+ "pools": [ { "pool": "2001:db8:1::-2001:db8:1::100" } ]
+ } ]
+ }
+
+When using database backends, the global host reservations are
+distinguished from regular reservations by using a ``subnet-id`` value of
+0.
+
+.. _pool-selection-with-class-reservations6:
+
+Pool Selection with Client Class Reservations
+---------------------------------------------
+
+Client classes can be specified both in the Kea configuration file and/or
+via host reservations. The classes specified in the Kea configuration file are
+evaluated immediately after receiving the DHCP packet and therefore can be
+used to influence subnet selection using the ``client-class`` parameter
+specified in the subnet scope. The classes specified within the host
+reservations are fetched and assigned to the packet after the server has
+already selected a subnet for the client. This means that the client
+class specified within a host reservation cannot be used to influence
+subnet assignment for this client, unless the subnet belongs to a
+shared network. If the subnet belongs to a shared network, the server may
+dynamically change the subnet assignment while trying to allocate a lease.
+If the subnet does not belong to a shared network, once selected, the subnet
+is not changed once selected.
+
+If the subnet does not belong to a shared network, it is possible to
+use host reservation-based client classification to select an address pool
+within the subnet as follows:
+
+::
+
+ "Dhcp6": {
+ "client-classes": [
+ {
+ "name": "reserved_class"
+ },
+ {
+ "name": "unreserved_class",
+ "test": "not member('reserved_class')"
+ }
+ ],
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:fe",
+ "client-classes": [ "reserved_class" ]
+ }
+ ],
+ "pools": [
+ {
+ "pool": "2001:db8:1::10-2001:db8:1::20",
+ "client-class": "reserved_class"
+ },
+ {
+ "pool": "2001:db8:1::30-2001:db8:1::40",
+ "client-class": "unreserved_class"
+ }
+ ]
+ }
+ ]
+ }
+
+The ``reserved_class`` is declared without the ``test`` parameter because
+it may be only assigned to the client via host reservation mechanism. The
+second class, ``unreserved_class``, is assigned to clients which do not
+belong to the ``reserved_class``. The first pool within the subnet is only
+used for clients having a reservation for the ``reserved_class``. The
+second pool is used for clients not having such a reservation. The
+configuration snippet includes one host reservation which causes the client
+with the MAC address aa:bb:cc:dd:ee:fe to be assigned to the
+``reserved_class``. Thus, this client will be given an IP address from the
+first address pool.
+
+.. _subnet-selection-with-class-reservations6:
+
+Subnet Selection with Client Class Reservations
+-----------------------------------------------
+
+There is one specific use case when subnet selection may be influenced by
+client classes specified within host reservations: when the
+client belongs to a shared network. In such a case it is possible to use
+classification to select a subnet within this shared network. Consider the
+following example:
+
+::
+
+ "Dhcp6": {
+ "client-classes": [
+ {
+ "name": "reserved_class"
+ },
+ {
+ "name": "unreserved_class",
+ "test": "not member('reserved_class')"
+ }
+ ],
+ "reservations": [
+ {
+ "hw-address": "aa:bb:cc:dd:ee:fe",
+ "client-classes": [ "reserved_class" ]
+ }
+ ],
+ # It is replaced by the "reservations-global",
+ # "reservations-in-subnet", and "reservations-out-of-pool" parameters.
+ # Specify if the server should look up global reservations.
+ "reservations-global": true,
+ # Specify if the server should look up in-subnet reservations.
+ "reservations-in-subnet": false,
+ # Specify if the server can assume that all reserved addresses
+ # are out-of-pool. It can be ignored because "reservations-in-subnet"
+ # is false, but if specified, it is inherited by "shared-networks"
+ # and "subnet6" levels.
+ # "reservations-out-of-pool": false,
+ "shared-networks": [
+ {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "pools": [
+ {
+ "pool": "2001:db8:1::10-2001:db8:1::20",
+ "client-class": "reserved_class"
+ }
+ ]
+ },
+ {
+ "id": 2,
+ "subnet": "2001:db8:2::/64",
+ "pools": [
+ {
+ "pool": "2001:db8:2::10-2001:db8:2::20",
+ "client-class": "unreserved_class"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+
+This is similar to the example described in the
+:ref:`pool-selection-with-class-reservations6`. This time, however, there
+are two subnets, each of which has a pool associated with a different
+class. The clients that do not have a reservation for the ``reserved_class``
+are assigned an address from the subnet 2001:db8:2::/64. Clients with
+a reservation for the ``reserved_class`` are assigned an address from
+the subnet 2001:db8:1::/64. The subnets must belong to the same shared network.
+In addition, the reservation for the client class must be specified at the
+global scope (global reservation) and ``reservations-global`` must be
+set to ``true``.
+
+In the example above, the ``client-class`` could also be specified at the
+subnet level rather than the pool level, and would yield the same effect.
+
+.. _multiple-reservations-same-ip6:
+
+Multiple Reservations for the Same IP
+-------------------------------------
+
+Host reservations were designed to preclude the creation of multiple
+reservations for the same IP address or delegated prefix within a
+particular subnet, to avoid having two different clients
+compete for the same lease. When using the default settings, the server
+returns a configuration error when it finds two or more reservations for
+the same lease within a subnet in the Kea configuration file.
+:ischooklib:`libdhcp_host_cmds.so` returns an error in response to the
+:isccmd:`reservation-add` command when it detects that the reservation exists
+in the database for the lease for which the new reservation is being added.
+
+Similar to DHCPv4 (see :ref:`multiple-reservations-same-ip4`), the DHCPv6
+server can also be configured to allow the creation of multiple reservations
+for the same IPv6 address and/or delegated prefix in a given subnet. This
+is supported since Kea release 1.9.1 as an optional mode of operation
+enabled with the ``ip-reservations-unique`` global parameter.
+
+The ``ip-reservations-unique`` is a boolean parameter that defaults to
+``true``, which forbids the specification of more than one reservation
+for the same lease in a given subnet. Setting this parameter to ``false``
+allows such reservations to be created both in the Kea configuration
+file and in the host database backend, via :ischooklib:`libdhcp_host_cmds.so`.
+
+Setting ``ip-reservations-unique`` to ``false`` when using memfile, MySQL or PostgreSQL is supported.
+This setting is not supported when using Host Cache (see :ref:`hooks-host-cache`), and the RADIUS backend
+(see :ref:`hooks-radius`). These reservation backends simply do not support multiple reservations for the
+same IP. If either of these hooks are loaded and ``ip-reservations-unique`` is set to ``false``, then a
+configuration error will be emitted and the server will fail to start.
+
+.. note::
+
+ When ``ip-reservations-unique`` is set to ``true`` (the default value),
+ the server ensures that IP reservations are unique for a subnet within
+ a single host backend and/or Kea configuration file. It does not
+ guarantee that the reservations are unique across multiple backends.
+ On server startup, only IP reservations defined in the Kea configuration
+ file are checked for uniqueness.
+
+
+The following is an example configuration with two reservations for
+the same IPv6 address but different MAC addresses:
+
+::
+
+ "Dhcp6": {
+ "ip-reservations-unique": false,
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "reservations": [
+ {
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "ip-address": "2001:db8:1::11"
+ },
+ {
+ "hw-address": "2a:2b:2c:2d:2e:2f",
+ "ip-address": "2001:db8:1::11"
+ }
+ ]
+ }
+ ]
+ }
+
+It is possible to control the ``ip-reservations-unique`` parameter via the
+:ref:`dhcp6-cb`. If the new setting of this parameter conflicts with
+the currently used backends (i.e. backends do not support the new setting),
+the new setting is ignored and a warning log message is generated.
+The backends continue to use the default setting, expecting that
+IP reservations are unique within each subnet. To allow the
+creation of non-unique IP reservations, the administrator must remove
+the backends which lack support for them from the configuration file.
+
+Administrators must be careful when they have been using multiple
+reservations for the same IP address and/or delegated prefix and later
+decide to return to the default mode in which this is no longer allowed.
+They must make sure that at most one reservation for
+a given IP address or delegated prefix exists within a subnet, prior
+to switching back to the default mode. If such duplicates are left in
+the configuration file, the server reports a configuration error.
+Leaving such reservations in the host databases does not cause
+configuration errors but may lead to lease allocation errors during
+the server's operation, when it unexpectedly finds multiple reservations
+for the same IP address or delegated prefix.
+
+.. note::
+
+ Currently the Kea server does not verify whether multiple reservations for
+ the same IP address and/or delegated prefix exist in
+ MySQL and/or PostgreSQL) host databases when ``ip-reservations-unique``
+ is updated from ``false`` to ``true``. This may cause issues with
+ lease allocations. The administrator must ensure that there is at
+ most one reservation for each IP address and/or delegated prefix
+ within each subnet, prior to the configuration update.
+
+The ``reservations-lookup-first`` is a boolean parameter which controls whether
+host reservations lookup should be performed before lease lookup. This parameter
+has effect only when multi-threading is disabled. When multi-threading is
+enabled, host reservations lookup is always performed first to avoid lease-lookup
+resource locking. The ``reservations-lookup-first`` parameter defaults to ``false``
+when multi-threading is disabled.
+
+.. _host_reservations_as_basic_access_control6:
+
+Host Reservations as Basic Access Control
+-----------------------------------------
+
+Starting with Kea 2.3.5, it is possible to define a host reservation that
+contains just an identifier, without any address, options, or values. In some
+deployments this is useful, as the hosts that have a reservation belong to
+the KNOWN class while others do not. This can be used as a basic access control
+mechanism.
+
+The following example demonstrates this concept. It indicates a single IPv6 subnet
+and all clients will get an address from it. However, only known clients (those that
+have reservations) will get their default DNS server configured. Empty reservations
+i.e. reservations that only have the identification criterion, can be
+specifically useful in this regard of making the clients known.
+
+::
+
+ "Dhcp6": {
+ "client-classes": [
+ {
+ "name": "KNOWN",
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8::1"
+ }
+ ]
+ }
+ ],
+ "reservations": [
+ // Clients on this list will be added to the KNOWN class.
+ { "duid": "01:02:03:04:05:0A:0B:0C:0D:0E" },
+ { "duid": "02:03:04:05:0A:0B:0C:0D:0E:0F" }
+ ],
+ "reservations-in-subnet": true,
+
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/48",
+ "pools": [
+ {
+ "pool": "2001:db8:1:1::/64"
+ }
+ ]
+ }
+ ]
+ }
+
+This concept can be extended further. A good real-life scenario might be a
+situation where some customers of an ISP have not paid their bills. A new class can be
+defined to use an alternative default DNS server that, instead of giving access
+to the Internet, redirects those customers to a captive portal urging them to bring
+their accounts up to date.
+
+::
+
+ "Dhcp6": {
+ "client-classes": [
+ {
+ "name": "blocked",
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8::2"
+ }
+ ]
+ }
+ ],
+ "reservations": [
+ // Clients on this list will be added to the KNOWN class. Some
+ // will also be added to the blocked class.
+ { "duid": "01:02:03:04:05:0A:0B:0C:0D:0E",
+ "client-classes": [ "blocked" ] },
+ { "duid": "02:03:04:05:0A:0B:0C:0D:0E:0F" }
+ ],
+ "reservations-in-subnet": true,
+
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/48",
+ "pools": [
+ {
+ "pool": "2001:db8:1:1::/64"
+ }
+ ],
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8::1"
+ }
+ ]
+ }
+ ]
+ }
+
+.. _shared-network6:
+
+Shared Networks in DHCPv6
+=========================
+
+DHCP servers use subnet information in two ways. It is used to
+both determine the point of attachment, i.e. where the client is
+connected to the network, and to
+group information pertaining to a specific location in the network.
+Sometimes it is useful to have more than one
+logical IP subnet being deployed on the same physical link.
+Understanding that two or more subnets are used on the same link requires
+additional logic in the DHCP server. This capability is called "shared
+networks" in Kea, and sometimes also
+"shared subnets"; in Microsoft's nomenclature it is called
+"multinet."
+
+There are many cases where the shared networks feature is useful; here we explain
+just a handful of the most common ones. The first and by far most common
+use case is an existing IPv4 network that has grown and
+is running out of available
+address space. This is less common in IPv6, but shared networks
+are still useful: for example, with the exhaustion of IPv6-
+delegated prefixes within a subnet, or the desire to
+experiment with an addressing scheme. With the advent of IPv6 deployment
+and a vast address space, many organizations split the address space
+into subnets, deploy it, and then after a while discover that they want
+to split it differently. In the transition period, they want both the old
+and new addressing to be available: thus the need for more than one
+subnet on the same physical link.
+
+Finally, the case of cable networks is directly applicable in IPv6.
+There are two types of devices in cable networks: cable modems and the
+end-user devices behind them. It is a common practice to use different
+subnets for cable modems to prevent users from tinkering with them. In
+this case, the distinction is based on the type of device, rather than
+on address-space exhaustion.
+
+A client connected to a shared network may be assigned a lease (address
+or prefix) from any of the pools defined within the subnets belonging to
+the shared network. Internally, the server selects one of the subnets
+belonging to a shared network and tries to allocate a lease from this
+subnet. If the server is unable to allocate a lease from the selected
+subnet (e.g., due to pool exhaustion), it uses another subnet from
+the same shared network and tries to allocate a lease from this subnet.
+The server typically allocates all leases
+available in a given subnet before it starts allocating leases from
+other subnets belonging to the same shared network. However, in certain
+situations the client can be allocated a lease from another subnet
+before the pools in the first subnet get exhausted; this sometimes occurs
+when the client provides a hint that belongs to another subnet, or the client has
+reservations in a subnet other than the default.
+
+.. note::
+
+ Deployments should not assume that Kea waits until it has allocated
+ all the addresses from the first subnet in a shared network before
+ allocating addresses from other subnets.
+
+To define a shared network, an additional configuration scope is
+introduced:
+
+::
+
+ {
+ "Dhcp6": {
+ "shared-networks": [
+ {
+ # Name of the shared network. It may be an arbitrary string
+ # and it must be unique among all shared networks.
+ "name": "ipv6-lab-1",
+
+ # The subnet selector can be specified on the shared network
+ # level. Subnets from this shared network will be selected
+ # for clients communicating via relay agent having
+ # the specified IP address.
+ "relay": {
+ "ip-addresses": [ "2001:db8:2:34::1" ]
+ },
+
+ # This starts a list of subnets in this shared network.
+ # There are two subnets in this example.
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8::/48",
+ "pools": [ { "pool": "2001:db8::1 - 2001:db8::ffff" } ]
+ },
+ {
+ "id": 2,
+ "subnet": "3ffe:ffe::/64",
+ "pools": [ { "pool": "3ffe:ffe::/64" } ]
+ }
+ ]
+ }
+ ],
+ # end of shared-networks
+
+ # It is likely that in the network there will be a mix of regular,
+ # "plain" subnets and shared networks. It is perfectly valid
+ # to mix them in the same configuration file.
+ #
+ # This is a regular subnet. It is not part of any shared-network.
+ "subnet6": [
+ {
+ "id": 3,
+ "subnet": "2001:db9::/48",
+ "pools": [ { "pool": "2001:db9::/64" } ],
+ "relay": {
+ "ip-addresses": [ "2001:db8:1:2::1" ]
+ }
+ }
+ ]
+ }
+ }
+
+As demonstrated in the example, it is possible to mix shared and regular
+("plain") subnets. Each shared network must have a unique name. This is
+similar to the ID for subnets, but gives administrators more
+flexibility. It is used for logging, but also internally for
+identifying shared networks.
+
+In principle it makes sense to define only shared networks that consist
+of two or more subnets. However, for testing purposes, an empty subnet
+or a network with just a single subnet is allowed. This
+is not a recommended practice in production networks, as the shared
+network logic requires additional processing and thus lowers the
+server's performance. To avoid unnecessary performance degradation,
+shared subnets should only be defined when required by the deployment.
+
+Shared networks provide an ability to specify many parameters in the
+shared network scope that apply to all subnets within it. If
+necessary, it is possible to specify a parameter in the shared-network scope and
+then override its value in the subnet scope. For example:
+
+::
+
+ {
+ "shared-networks": [
+ {
+ "name": "lab-network3",
+ "relay": {
+ "ip-addresses": [ "2001:db8:2:34::1" ]
+ },
+
+ # This applies to all subnets in this shared network, unless
+ # values are overridden on subnet scope.
+ "valid-lifetime": 600,
+
+ # This option is made available to all subnets in this shared
+ # network.
+ "option-data": [ {
+ "name": "dns-servers",
+ "data": "2001:db8::8888"
+ } ],
+
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/48",
+ "pools": [ { "pool": "2001:db8:1::1 - 2001:db8:1::ffff" } ],
+
+ # This particular subnet uses different values.
+ "valid-lifetime": 1200,
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8::1:2"
+ },
+ {
+ "name": "unicast",
+ "data": "2001:abcd::1"
+ } ]
+ },
+ {
+ "id": 2,
+ "subnet": "2001:db8:2::/48",
+ "pools": [ { "pool": "2001:db8:2::1 - 2001:db8:2::ffff" } ],
+
+ # This subnet does not specify its own valid-lifetime value,
+ # so it is inherited from shared network scope.
+ "option-data": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8:cafe::1"
+ } ]
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+In this example, there is a ``dns-servers`` option defined that is available
+to clients in both subnets in this shared network. Also, the valid
+lifetime is set to 10 minutes (600s). However, the first subnet
+overrides some of the values (the valid lifetime is 20 minutes, there is a different IP
+address for ``dns-servers``), but also adds its own option (the unicast
+address). Assuming a client asking for server unicast and ``dns-servers``
+options is assigned a lease from this subnet, it will get a lease for 20
+minutes and ``dns-servers``, and be allowed to use server unicast at address
+2001:abcd::1. If the same client is assigned to the second subnet, it
+will get a 10-minute lease, a ``dns-servers`` value of 2001:db8:cafe::1, and
+no server unicast.
+
+Some parameters must be the same in all subnets in the same shared
+network. This restriction applies to the ``interface`` and
+``rapid-commit`` settings. The most convenient way is to define them on
+the shared-network scope, but they can be specified for each subnet.
+However, each subnet must have the same value.
+
+.. note::
+
+ There is an inherent ambiguity when using clients that send multiple IA
+ options in a single request, and shared-networks whose subnets have
+ different values for options and configuration parameters. The server
+ sequentially processes IA options in the order that they occur in the
+ client's query; if the leases requested in the IA options end up being
+ fulfilled from different subnets, which parameters and options should
+ apply? Currently, the code uses the values from the last subnet of
+ the last IA option fulfilled.
+
+ We view this largely as a site configuration issue. A shared network
+ generally means the same physical link, so services configured by options
+ from subnet A should be as easily reachable from subnet B and vice versa.
+ There are a number of ways to avoid this situation:
+
+ - Use the same values for options and parameters for subnets within the shared network.
+ - Use subnet selectors or client class guards that ensure that for a single client's query, the same subnet is used for all IA options in that query.
+ - Avoid using shared networks with clients that send multiple IA options per query.
+
+Local and Relayed Traffic in Shared Networks
+--------------------------------------------
+
+It is possible to specify an interface name at the shared-network level,
+to tell the server that this specific shared network is reachable
+directly (not via relays) using the local network interface. As all
+subnets in a shared network are expected to be used on the same physical
+link, it is a configuration error to attempt to define a shared network
+using subnets that are reachable over different interfaces. In other
+words, all subnets within the shared network must have the same value
+for the ``interface`` parameter. The following configuration is an example
+of what **NOT** to do:
+
+::
+
+ {
+ "shared-networks": [
+ {
+ "name": "office-floor-2",
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8::/64",
+ "pools": [ { "pool": "2001:db8::1 - 2001:db8::ffff" } ],
+ "interface": "eth0"
+ },
+ {
+ "id": 2,
+ "subnet": "3ffe:abcd::/64",
+ "pools": [ { "pool": "3ffe:abcd::1 - 3ffe:abcd::ffff" } ],
+ ...
+ # Specifying a different interface name is a configuration
+ # error. This value should rather be "eth0" or the interface
+ # name in the other subnet should be "eth1".
+ # "interface": "eth1"
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+To minimize the chance of configuration errors, it is often more convenient
+to simply specify the interface name once, at the shared-network level, as
+shown in the example below.
+
+::
+
+ {
+ "shared-networks": [
+ {
+ "name": "office-floor-2",
+
+ # This tells Kea that the whole shared network is reachable over a
+ # local interface. This applies to all subnets in this network.
+ "interface": "eth0",
+
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8::/64",
+ "pools": [ { "pool": "2001:db8::1 - 2001:db8::ffff" } ]
+ },
+ {
+ "id": 2,
+ "subnet": "3ffe:abcd::/64",
+ "pools": [ { "pool": "3ffe:abcd::1 - 3ffe:abcd::ffff" } ]
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+
+With relayed traffic, subnets are typically selected using
+the relay agents' addresses. If the subnets are used independently (not
+grouped within a shared network), a different relay
+address can be specified for each of these subnets. When multiple subnets belong to a
+shared network they must be selected via the same relay address and,
+similarly to the case of the local traffic described above, it is a
+configuration error to specify different relay addresses for the respective
+subnets in the shared network. The following configuration is another example
+of what **NOT** to do:
+
+::
+
+ {
+ "shared-networks": [
+ {
+ "name": "kakapo",
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8::/64",
+ "relay": {
+ "ip-addresses": [ "2001:db8::1234" ]
+ },
+ "pools": [ { "pool": "2001:db8::1 - 2001:db8::ffff" } ]
+ },
+ {
+ "id": 2,
+ "subnet": "3ffe:abcd::/64",
+ "pools": [ { "pool": "3ffe:abcd::1 - 3ffe:abcd::ffff" } ],
+ "relay": {
+ # Specifying a different relay address for this
+ # subnet is a configuration error. In this case
+ # it should be 2001:db8::1234 or the relay address
+ # in the previous subnet should be 3ffe:abcd::cafe.
+ "ip-addresses": [ "3ffe:abcd::cafe" ]
+ }
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+Again, it is better to specify the relay address at the shared-network
+level; this value will be inherited by all subnets belonging to the
+shared network.
+
+::
+
+ {
+ "shared-networks": [
+ {
+ "name": "kakapo",
+ "relay": {
+ # This relay address is inherited by both subnets.
+ "ip-addresses": [ "2001:db8::1234" ]
+ },
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8::/64",
+ "pools": [ { "pool": "2001:db8::1 - 2001:db8::ffff" } ]
+ },
+ {
+ "id": 2,
+ "subnet": "3ffe:abcd::/64",
+ "pools": [ { "pool": "3ffe:abcd::1 - 3ffe:abcd::ffff" } ]
+ }
+ ]
+ }
+ ],
+ ...
+ }
+
+Even though it is technically possible to configure two (or more) subnets
+within the shared network to use different relay addresses, this will almost
+always lead to a different behavior than what the user would expect. In this
+case, the Kea server will initially select one of the subnets by matching
+the relay address in the client's packet with the subnet's configuration.
+However, it MAY end up using the other subnet (even though it does not match
+the relay address) if the client already has a lease in this subnet or has a
+host reservation in this subnet, or simply if the initially selected subnet has no
+more addresses available. Therefore, it is strongly recommended to always
+specify subnet selectors (interface or relay address) at the shared-network
+level if the subnets belong to a shared network, as it is rarely useful to
+specify them at the subnet level and may lead to the configuration errors
+described above.
+
+Client Classification in Shared Networks
+----------------------------------------
+
+Sometimes it is desirable to segregate clients into specific subnets
+based on certain properties. This mechanism is called client
+classification and is described in :ref:`classify`. Client
+classification can be applied to subnets belonging to shared networks in
+the same way as it is used for subnets specified outside of shared
+networks. It is important to understand how the server selects subnets
+for clients when client classification is in use, to ensure that the
+appropriate subnet is selected for a given client type.
+
+If a subnet is associated with a class, only the clients belonging to
+this class can use this subnet. If there are no classes specified for a
+subnet, any client connected to a given shared network can use this
+subnet. A common mistake is to assume that the subnet that includes a client
+class is preferred over subnets without client classes. Consider the
+following example:
+
+.. code-block:: json
+
+ {
+ "client-classes": [
+ {
+ "name": "b-devices",
+ "test": "option[1234].hex == 0x0002"
+ }
+ ],
+ "shared-networks": [
+ {
+ "name": "galah",
+ "relay": {
+ "ip-address": [ "2001:db8:2:34::1" ]
+ },
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "pools": [ { "pool": "2001:db8:1::20 - 2001:db8:1::ff" } ]
+ },
+ {
+ "id": 2,
+ "subnet": "2001:db8:3::/64",
+ "pools": [ { "pool": "2001:db8:3::20 - 2001:db8:3::ff" } ],
+ "client-class": "b-devices"
+ }
+ ]
+ }
+ ]
+ }
+
+If the client belongs to the "b-devices" class (because it includes
+option 1234 with a value of 0x0002), that does not guarantee that the
+subnet 2001:db8:3::/64 will be used (or preferred) for this client. The
+server can use either of the two subnets, because the subnet
+2001:db8:1::/64 is also allowed for this client. The client
+classification used in this case should be perceived as a way to
+restrict access to certain subnets, rather than as a way to express subnet
+preference. For example, if the client does not belong to the "b-devices"
+class, it may only use the subnet 2001:db8:1::/64 and will never use the
+subnet 2001:db8:3::/64.
+
+A typical use case for client classification is in a cable network,
+where cable modems should use one subnet and other devices should use
+another subnet within the same shared network. In this case it is
+necessary to apply classification on all subnets. The following example
+defines two classes of devices, and the subnet selection is made based
+on option 1234 values.
+
+::
+
+ {
+ "client-classes": [
+ {
+
+ "name": "a-devices",
+ "test": "option[1234].hex == 0x0001"
+ },
+ {
+ "name": "b-devices",
+ "test": "option[1234].hex == 0x0002"
+ }
+ ],
+ "shared-networks": [
+ {
+ "name": "galah",
+ "relay": {
+ "ip-addresses": [ "2001:db8:2:34::1" ]
+ },
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "pools": [ { "pool": "2001:db8:1::20 - 2001:db8:1::ff" } ],
+ "client-class": "a-devices"
+ },
+ {
+ "id": 2,
+ "subnet": "2001:db8:3::/64",
+ "pools": [ { "pool": "2001:db8:3::20 - 2001:db8:3::ff" } ],
+ "client-class": "b-devices"
+ }
+ ]
+ }
+ ]
+ }
+
+In this example each class has its own restriction. Only clients that
+belong to class "a-devices" are able to use subnet 2001:db8:1::/64
+and only clients belonging to "b-devices" are able to use subnet
+2001:db8:3::/64. Care should be taken not to define too-restrictive
+classification rules, as clients that are unable to use any subnets will
+be refused service. However, this may be a desired outcome if one wishes
+to provide service only to clients with known properties (e.g. only VoIP
+phones allowed on a given link).
+
+It is possible to achieve an effect similar to the one
+presented in this section without the use of shared networks. If the
+subnets are placed in the global subnets scope, rather than in the
+shared network, the server will still use classification rules to pick
+the right subnet for a given class of devices. The major benefit of
+placing subnets within the shared network is that common parameters for
+the logically grouped subnets can be specified once, in the shared
+network scope, e.g. the ``interface`` or ``relay`` parameter. All subnets
+belonging to this shared network will inherit those parameters.
+
+Host Reservations in Shared Networks
+------------------------------------
+
+Subnets that are part of a shared network allow host reservations,
+similar to regular subnets:
+
+::
+
+ {
+ "shared-networks": [
+ {
+ "name": "frog",
+ "relay": {
+ "ip-addresses": [ "2001:db8:2:34::1" ]
+ },
+ "subnet6": [
+ {
+ "subnet": "2001:db8:1::/64",
+ "id": 100,
+ "pools": [ { "pool": "2001:db8:1::1 - 2001:db8:1::64" } ],
+ "reservations": [
+ {
+ "duid": "00:03:00:01:11:22:33:44:55:66",
+ "ip-addresses": [ "2001:db8:1::28" ]
+ }
+ ]
+ },
+ {
+ "subnet": "2001:db8:3::/64",
+ "id": 101,
+ "pools": [ { "pool": "2001:db8:3::1 - 2001:db8:3::64" } ],
+ "reservations": [
+ {
+ "duid": "00:03:00:01:aa:bb:cc:dd:ee:ff",
+ "ip-addresses": [ "2001:db8:2::28" ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+
+
+It is worth noting that Kea conducts additional checks when processing a
+packet if shared networks are defined. First, instead of simply checking
+whether there is a reservation for a given client in its initially
+selected subnet, Kea looks through all subnets in a shared network for a
+reservation. This is one of the reasons why defining a shared network
+may impact performance. If there is a reservation for a client in any
+subnet, that particular subnet is picked for the client. Although
+it is technically not an error, it is considered bad practice to define
+reservations for the same host in multiple subnets belonging to the same
+shared network.
+
+While not strictly mandatory, it is strongly recommended to use explicit
+"id" values for subnets if database storage will be used for host
+reservations. If an ID is not specified, the values for it are
+auto generated, i.e. Kea assigns increasing integer values starting from
+1. Thus, the auto-generated IDs are not stable across configuration
+changes.
+
+.. _dhcp6-serverid:
+
+Server Identifier in DHCPv6
+===========================
+
+The DHCPv6 protocol uses a "server identifier" (also known as a DUID) to
+allow clients to discriminate between several servers present on the
+same link. `RFC 8415 <https://tools.ietf.org/html/rfc8415>`__ currently
+defines four DUID types: DUID-LLT, DUID-EN, DUID-LL, and DUID-UUID.
+
+The Kea DHCPv6 server generates a server identifier once, upon the first
+startup, and stores it in a file. This identifier is not modified across
+restarts of the server and so is a stable identifier.
+
+Kea follows the recommendation from `RFC
+8415 <https://tools.ietf.org/html/rfc8415>`__ to use DUID-LLT as the
+default server identifier. However, ISC has received reports that some
+deployments require different DUID types, and that there is a need to
+administratively select both the DUID type and/or its contents.
+
+The server identifier can be configured using parameters within the
+``server-id`` map element in the global scope of the Kea configuration
+file. The following example demonstrates how to select DUID-EN as a
+server identifier:
+
+::
+
+ "Dhcp6": {
+ "server-id": {
+ "type": "EN"
+ },
+ ...
+ }
+
+Currently supported values for the ``type`` parameter are: "LLT", "EN", and
+"LL", for DUID-LLT, DUID-EN, and DUID-LL respectively.
+
+When a new DUID type is selected, the server generates its value and
+replaces any existing DUID in the file. The server then uses the new
+server identifier in all future interactions with clients.
+
+.. note::
+
+ If the new server identifier is created after some clients have
+ obtained their leases, the clients using the old identifier are not
+ able to renew their leases; the server will ignore messages containing
+ the old server identifier. Clients will continue sending RENEW until
+ they transition to the rebinding state. In this state, they will
+ start sending REBIND messages to the multicast address without a
+ server identifier. The server will respond to the REBIND messages
+ with a new server identifier, and the clients will associate the new
+ server identifier with their leases. Although the clients will be
+ able to keep their leases and will eventually learn the new server
+ identifier, this will be at the cost of an increased number of
+ renewals and multicast traffic due to a need to rebind. Therefore, it
+ is recommended that modification of the server-identifier type and
+ value be avoided if the server has already assigned leases and these
+ leases are still valid.
+
+There are cases when an administrator needs to explicitly specify a DUID
+value rather than allow the server to generate it. The following example
+demonstrates how to explicitly set all components of a DUID-LLT.
+
+::
+
+ "Dhcp6": {
+ "server-id": {
+ "type": "LLT",
+ "htype": 8,
+ "identifier": "A65DC7410F05",
+ "time": 2518920166
+ },
+ ...
+ }
+
+where:
+
+- ``htype`` is a 16-bit unsigned value specifying hardware type,
+
+- ``identifier`` is a link-layer address, specified as a string of
+ hexadecimal digits, and
+
+- ``time`` is a 32-bit unsigned time value.
+
+The hexadecimal representation of the DUID generated as a result of the
+configuration specified above is:
+
+::
+
+ 00:01:00:08:96:23:AB:E6:A6:5D:C7:41:0F:05
+ |type |htype| time | identifier |
+
+A special value of "0" for ``htype`` and ``time`` is allowed, which indicates
+that the server should use ANY value for these components. If the server
+already uses a DUID-LLT, it will use the values from this DUID; if the
+server uses a DUID of a different type or does not yet use any DUID, it
+will generate these values. Similarly, if the ``identifier`` is assigned
+an empty string, the value of the ``identifier`` will be generated. Omitting
+any of these parameters is equivalent to setting them to those special
+values.
+
+For example, the following configuration:
+
+::
+
+ "Dhcp6": {
+ "server-id": {
+ "type": "LLT",
+ "htype": 0,
+ "identifier": "",
+ "time": 2518920166
+ },
+ ...
+ }
+
+indicates that the server should use ANY link-layer address and hardware
+type. If the server is already using DUID-LLT, it will use the
+link-layer address and hardware type from the existing DUID. If the
+server is not yet using any DUID, it will use the link-layer address and
+hardware type from one of the available network interfaces. The server
+will use an explicit value of time; if it is different than a time value
+present in the currently used DUID, that value will be replaced,
+effectively modifying the current server identifier.
+
+The following example demonstrates an explicit configuration of a
+DUID-EN:
+
+::
+
+ "Dhcp6": {
+ "server-id": {
+ "type": "EN",
+ "enterprise-id": 2495,
+ "identifier": "87ABEF7A5BB545"
+ },
+ ...
+ }
+
+where:
+
+- ``enterprise-id`` is a 32-bit unsigned value holding an enterprise
+ number, and
+
+- ``identifier`` is a variable- length identifier within DUID-EN.
+
+The hexadecimal representation of the DUID-EN created according to the
+configuration above is:
+
+::
+
+ 00:02:00:00:09:BF:87:AB:EF:7A:5B:B5:45
+ |type | ent-id | identifier |
+
+As in the case of the DUID-LLT, special values can be used for the
+configuration of the DUID-EN. If the ``enterprise-id`` is "0", the server
+will use a value from the existing DUID-EN. If the server is not using
+any DUID or the existing DUID has a different type, the ISC enterprise
+ID will be used. When an empty string is entered for ``identifier``, the
+identifier from the existing DUID-EN will be used. If the server is not
+using any DUID-EN, a new 6-byte-long ``identifier`` will be generated.
+
+DUID-LL is configured in the same way as DUID-LLT except that the
+``time`` parameter has no effect for DUID-LL, because this DUID type
+only comprises a hardware type and link-layer address. The following
+example demonstrates how to configure DUID-LL:
+
+::
+
+ "Dhcp6": {
+ "server-id": {
+ "type": "LL",
+ "htype": 8,
+ "identifier": "A65DC7410F05"
+ },
+ ...
+ }
+
+which will result in the following server identifier:
+
+::
+
+ 00:03:00:08:A6:5D:C7:41:0F:05
+ |type |htype| identifier |
+
+The server stores the generated server identifier in the following
+location: ``[kea-install-dir]/var/lib/kea/kea-dhcp6-serverid``.
+
+In some uncommon deployments where no stable storage is available, the
+server should be configured not to try to store the server identifier.
+This choice is controlled by the value of the ``persist`` boolean
+parameter:
+
+::
+
+ "Dhcp6": {
+ "server-id": {
+ "type": "EN",
+ "enterprise-id": 2495,
+ "identifier": "87ABEF7A5BB545",
+ "persist": false
+ },
+ ...
+ }
+
+The default value of the ``persist`` parameter is ``true``, which
+configures the server to store the server identifier on a disk.
+
+In the example above, the server is configured not to store the
+generated server identifier on a disk. But if the server identifier is
+not modified in the configuration, the same value is used after
+server restart, because the entire server identifier is explicitly
+specified in the configuration.
+
+.. _data-directory:
+
+DHCPv6 Data Directory
+=====================
+
+The Kea DHCPv6 server puts the server identifier file and the default
+memory lease file into its data directory. By default this directory is
+``prefix/var/lib/kea`` but this location can be changed using the
+``data-directory`` global parameter, as in:
+
+::
+
+ "Dhcp6": {
+ "data-directory": "/var/tmp/kea-server6",
+ ...
+ }
+
+.. _stateless-dhcp6:
+
+Stateless DHCPv6 (INFORMATION-REQUEST Message)
+==============================================
+
+Typically DHCPv6 is used to assign both addresses and options. These
+assignments (leases) have a state that changes over time, hence their
+description as "stateful." DHCPv6 also supports a "stateless" mode, where clients
+request only configuration options. This mode is considered lightweight
+from the server perspective, as it does not require any state tracking.
+
+The Kea server supports stateless mode. When clients send
+INFORMATION-REQUEST messages, the server sends back answers with the
+requested options, if they are available in the server
+configuration. The server attempts to use per-subnet options first; if
+that fails, it then tries to provide options
+defined in the global scope.
+
+Stateless and stateful mode can be used together. No special
+configuration directives are required to handle this; simply use the
+configuration for stateful clients and the stateless clients will get
+only the options they requested.
+
+It is possible to run a server that provides only options and no addresses or
+prefixes. If the options have the same value in each subnet, the
+configuration can define the required options in the global scope and skip
+subnet definitions altogether. Here's a simple example of such a
+configuration:
+
+::
+
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ "ethX" ]
+ },
+ "option-data": [ {
+ "name": "dns-servers",
+ "data": "2001:db8::1, 2001:db8::2"
+ } ],
+ "lease-database": {
+ "type": "memfile"
+ }
+ }
+
+This very simple configuration provides DNS server information to
+all clients in the network, regardless of their location. The
+memfile lease database must be specified, as Kea
+requires a lease database to be specified even if it is not used.
+
+.. _dhcp6-rfc7550:
+
+Support for RFC 7550 (now part of RFC 8415)
+===========================================
+
+`RFC 7550 <https://tools.ietf.org/html/rfc7550>`__ introduced some
+changes to the previous DHCPv6 specifications, `RFC
+3315 <https://tools.ietf.org/html/rfc3315>`__ and `RFC
+3633 <https://tools.ietf.org/html/rfc3633>`__, to resolve issues
+with the coexistence of multiple stateful options in the messages sent
+between clients and servers. Those changes were later included in
+the most recent DHCPv6 protocol specification, `RFC
+8415 <https://tools.ietf.org/html/rfc8415>`__, which obsoleted `RFC
+7550 <https://tools.ietf.org/html/rfc7550>`__. Kea supports `RFC
+8415 <https://tools.ietf.org/html/rfc8415>`__ along with these protocol
+changes, which are briefly described below.
+
+When a client, such as a requesting router, requests an allocation of
+both addresses and prefixes during the 4-way (SARR) exchange with the
+server, and the server is not configured to allocate any prefixes but
+can allocate some addresses, it will respond with the IA_NA(s)
+containing allocated addresses and the IA_PD(s) containing the
+NoPrefixAvail status code. According to the updated specifications, if
+the client can operate without prefixes it should accept allocated
+addresses and transition to the "bound" state. When the client
+subsequently sends RENEW/REBIND messages to the server to extend the
+lifetimes of the allocated addresses, according to the T1 and T2 times, and
+if the client is still interested in obtaining prefixes from the server,
+it may also include an IA_PD in the RENEW/REBIND to request allocation
+of the prefixes. If the server still cannot allocate the prefixes, it
+will respond with the IA_PD(s) containing the NoPrefixAvail status code.
+However, if the server can allocate the prefixes, it allocates and
+sends them in the IA_PD(s) to the client. A similar situation occurs when
+the server is unable to allocate addresses for the client but can
+delegate prefixes: the client may request allocation of the addresses
+while renewing the delegated prefixes. Allocating leases for other IA
+types while renewing existing leases is by default supported by the Kea
+DHCPv6 server, and the server provides no configuration mechanisms to
+disable this behavior.
+
+The following are the other behaviors first introduced in `RFC
+7550 <https://tools.ietf.org/html/rfc7550>`__ (now part of `RFC
+8415 <https://tools.ietf.org/html/rfc8415>`__) and supported by the Kea
+DHCPv6 server:
+
+- Set T1/T2 timers to the same value for all stateful (IA_NA and IA_PD)
+ options to facilitate renewal of all of a client's leases at the same
+ time (in a single message exchange).
+
+- Place NoAddrsAvail and NoPrefixAvail status codes in the IA_NA and
+ IA_PD options in the ADVERTISE message, rather than as the top-level
+ options.
+
+.. _dhcp6-relay-override:
+
+Using a Specific Relay Agent for a Subnet
+=========================================
+
+The DHCPv6 server follows the same principles as the DHCPv4 server to
+select a subnet for the client, with noticeable differences mainly for
+relays.
+
+.. note::
+
+ When the selected subnet is a member of a shared network, the
+ whole shared network is selected.
+
+A relay must have an interface connected to the link on which the
+clients are being configured. Typically the relay has a global IPv6
+address configured on that interface, which belongs to the subnet from
+which the server assigns addresses. Normally, the server is able to
+use the IPv6 address inserted by the relay (in the ``link-addr`` field in
+the RELAY-FORW message) to select the appropriate subnet.
+
+However, that is not always the case; the relay address may not match
+the subnet in certain deployments. This usually means that there is more
+than one subnet allocated for a given link. The two most common examples
+of this are long-lasting network renumbering (where both the
+old and new address spaces are still being used) and a cable network. In a
+cable network, both cable modems and the devices behind them are
+physically connected to the same link, yet they use distinct addressing.
+In such a case, the DHCPv6 server needs additional information (the
+value of the ``interface-id`` option or the IPv6 address inserted in the
+``link-addr`` field in the RELAY-FORW message) to properly select an
+appropriate subnet.
+
+The following example assumes that there is a subnet 2001:db8:1::/64
+that is accessible via a relay that uses 3000::1 as its IPv6 address.
+The server is able to select this subnet for any incoming packets that
+come from a relay that has an address in the 2001:db8:1::/64 subnet. It also
+selects that subnet for a relay with address 3000::1.
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "pools": [
+ {
+ "pool": "2001:db8:1::1-2001:db8:1::ffff"
+ }
+ ],
+ "relay": {
+ "ip-addresses": [ "3000::1" ]
+ }
+ }
+ ]
+ }
+
+If ``relay`` is specified, the ``ip-addresses`` parameter within it is
+mandatory. The ``ip-addresses`` parameter supports specifying a list of addresses.
+
+.. _dhcp6-client-class-relay:
+
+Segregating IPv6 Clients in a Cable Network
+===========================================
+
+In certain cases, it is useful to mix relay address information
+(introduced in :ref:`dhcp6-relay-override`) with client classification (explained
+in :ref:`classify`). One specific example is in a cable network,
+where modems typically get addresses from a different subnet than all
+the devices connected behind them.
+
+Let us assume that there is one Cable Modem Termination System (CMTS)
+with one CM MAC (a physical link that modems are connected to). We want
+the modems to get addresses from the 3000::/64 subnet, while everything
+connected behind the modems should get addresses from the 2001:db8:1::/64
+subnet. The CMTS that acts as a relay uses address 3000::1.
+The following configuration can serve that situation:
+
+::
+
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "3000::/64",
+ "pools": [
+ { "pool": "3000::2 - 3000::ffff" }
+ ],
+ "client-class": "VENDOR_CLASS_docsis3.0",
+ "relay": {
+ "ip-addresses": [ "3000::1" ]
+ }
+ },
+ {
+ "id": 2,
+ "subnet": "2001:db8:1::/64",
+ "pools": [
+ {
+ "pool": "2001:db8:1::1-2001:db8:1::ffff"
+ }
+ ],
+ "relay": {
+ "ip-addresses": [ "3000::1" ]
+ }
+ }
+ ]
+ }
+
+.. _mac-in-dhcpv6:
+
+MAC/Hardware Addresses in DHCPv6
+================================
+
+MAC/hardware addresses are available in DHCPv4 messages from
+clients, and administrators frequently use that information to perform
+certain tasks like per-host configuration and address reservation for
+specific MAC addresses. Unfortunately, the DHCPv6 protocol does not
+provide any completely reliable way to retrieve that information. To
+mitigate that issue, a number of mechanisms have been implemented in
+Kea. Each of these mechanisms works in certain cases, but may not in
+others. Whether the mechanism works in a particular deployment is
+somewhat dependent on the network topology and the technologies used.
+
+Kea allows specification of which of the supported methods should be
+used and in what order, via the ``mac-sources`` parameter. This configuration
+may be considered a fine
+tuning of the DHCP deployment.
+
+Here is an example:
+
+::
+
+ "Dhcp6": {
+ "mac-sources": [
+ "method1",
+ "method2",
+ "method3",
+ ...
+ ],
+
+ "subnet6": [
+ {
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+When not specified, a value of "any" is used, which instructs
+the server to attempt to try all the methods in sequence and use the
+value returned by the first one that succeeds. In a typical deployment the default value
+of "any" is sufficient and there is no need to select specific
+methods. Changing the value of this parameter is most useful in
+cases when an administrator wants to disable certain methods; for
+example, if the administrator trusts the network infrastructure more
+than the information provided by the clients themselves, they may prefer
+information provided by the relays over that provided by clients.
+
+If specified, ``mac-sources`` must have at least one value.
+
+Supported methods are:
+
+- ``any`` - this is not an actual method, just a keyword that instructs Kea to
+ try all other methods and use the first one that succeeds. This is
+ the default operation if no ``mac-sources`` are defined.
+
+- ``raw`` - in principle, a DHCPv6 server could use raw sockets to
+ receive incoming traffic and extract MAC/hardware address
+ information. This is currently not implemented for DHCPv6 and this
+ value has no effect.
+
+- ``duid`` - DHCPv6 uses DUID identifiers instead of MAC addresses.
+ There are currently four DUID types defined, and two of them
+ (DUID-LLT, which is the default, and DUID-LL) convey MAC address
+ information. Although `RFC 8415 <https://tools.ietf.org/html/rfc8415>`__
+ forbids it, it is possible to
+ parse those DUIDs and extract necessary information from them. This
+ method is not completely reliable, as clients may use other DUID
+ types, namely DUID-EN or DUID-UUID.
+
+- ``ipv6-link-local`` - another possible acquisition method comes from
+ the source IPv6 address. In typical usage, clients are sending their
+ packets from IPv6 link-local addresses. There is a good chance that
+ those addresses are based on EUI-64, which contains a MAC address.
+ This method is not completely reliable, as clients may use other
+ link-local address types. In particular, privacy extensions, defined
+ in `RFC 4941 <https://tools.ietf.org/html/rfc4941>`__, do not use MAC
+ addresses. Also note that successful extraction requires that the
+ address's u-bit must be set to "1" and its g-bit set to "0", indicating
+ that it is an interface identifier as per `RFC 2373, section
+ 2.5.1 <https://tools.ietf.org/html/rfc2373#section-2.5.1>`__.
+
+- ``client-link-addr-option`` - one extension defined to alleviate
+ missing MAC issues is the client link-layer address option, defined
+ in `RFC 6939 <https://tools.ietf.org/html/rfc6939>`__. This is an
+ option that is inserted by a relay and contains information about a
+ client's MAC address. This method requires a relay agent that
+ supports the option and is configured to insert it. This method is
+ useless for directly connected clients. The value ``rfc6939`` is an alias for
+ ``client-link-addr-option``.
+
+- ``remote-id`` - `RFC 4649 <https://tools.ietf.org/html/rfc4649>`__
+ defines a ``remote-id`` option that is inserted by a relay agent.
+ Depending on the relay agent configuration, the inserted option may
+ convey the client's MAC address information. The value ``rfc4649``
+ is an alias for ``remote-id``.
+
+- ``subscriber-id`` - Defined in `RFC 4580 <https://tools.ietf.org/html/rfc4580>`__,
+ ``subscriber-id`` is somewhat similar to ``remote-id``; it is also inserted
+ by a relay agent. The value ``rfc4580`` is an alias for
+ ``subscriber-id``. This method is currently not implemented.
+
+- ``docsis-cmts`` - Yet another possible source of MAC address
+ information are the DOCSIS options inserted by a CMTS that acts as a
+ DHCPv6 relay agent in cable networks. This method attempts to extract
+ MAC address information from sub-option 1026 (cm mac) of the
+ vendor-specific option with ``vendor-id=4491``. This vendor option is
+ extracted from the Relay-forward message, not the original client's
+ message.
+
+- ``docsis-modem`` - The final possible source of MAC address
+ information are the DOCSIS options inserted by the cable modem
+ itself. This method attempts to extract MAC address information from
+ sub-option 36 (``device-id``) of the vendor-specific option with
+ ``vendor-id=4491``. This vendor option is extracted from the original
+ client's message, not from any relay options.
+
+An empty ``mac-sources`` parameter is not allowed. Administrators who do not want to specify it
+should either simply omit the ``mac-sources`` definition or specify it with the
+"any" value, which is the default.
+
+.. _dhcp6-decline:
+
+Duplicate Addresses (DHCPDECLINE Support)
+=========================================
+
+The DHCPv6 server is configured with a certain pool of addresses that it
+is expected to hand out to DHCPv6 clients. It is assumed that the server
+is authoritative and has complete jurisdiction over those addresses.
+However, for various reasons such as misconfiguration or a faulty
+client implementation that retains its address beyond the valid
+lifetime, there may be devices connected that use those addresses
+without the server's approval or knowledge.
+
+Such an unwelcome event can be detected by legitimate clients (using
+Duplicate Address Detection) and reported to the DHCPv6 server using a
+DHCPDECLINE message. The server does a sanity check (to see whether
+the client declining an address really was supposed to use it), then
+conducts a clean-up operation, and confirms the DHCPDECLINE by sending back a REPLY
+message. Any DNS entries related to that address are removed, the
+event is logged, and hooks are triggered. After that is
+complete, the address is marked as declined (which indicates that
+it is used by an unknown entity and thus not available for assignment)
+and a probation time is set on it. Unless otherwise configured, the
+probation period lasts 24 hours; after that time, the server will
+recover the lease (i.e. put it back into the available state) and the
+address will be available for assignment again. It should be noted that
+if the underlying issue of a misconfigured device is not resolved, the
+duplicate-address scenario will repeat. If reconfigured correctly, this
+mechanism provides an opportunity to recover from such an event
+automatically, without any system administrator intervention.
+
+To configure the decline probation period to a value other than the
+default, the following syntax can be used:
+
+::
+
+ "Dhcp6": {
+ "decline-probation-period": 3600,
+ "subnet6": [
+ {
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+The parameter is expressed in seconds, so the example above
+instructs the server to recycle declined leases after one hour.
+
+There are several statistics and hook points associated with the decline
+handling procedure. The ``lease6_decline`` hook point is triggered after the
+incoming DHCPDECLINE message has been sanitized and the server is about
+to decline the lease. The ``declined-addresses`` statistic is increased
+after the hook returns (both the global and subnet-specific variants). (See
+:ref:`dhcp6-stats` and :ref:`hooks-libraries`
+for more details on DHCPv6 statistics and Kea hook points.)
+
+Once the probation time elapses, the declined lease is recovered using
+the standard expired-lease reclamation procedure, with several
+additional steps. In particular, both ``declined-addresses`` statistics
+(global and subnet-specific) are decreased. At the same time,
+``reclaimed-declined-addresses`` statistics (again in two variants, global
+and subnet-specific) are increased.
+
+A note about statistics: The Kea server does not decrease the
+``assigned-nas`` statistics when a DHCPDECLINE message is received and
+processed successfully. While technically a declined address is no
+longer assigned, the primary usage of the ``assigned-nas`` statistic
+is to monitor pool utilization. Most people would forget to include
+``declined-addresses`` in the calculation, and would simply use
+``assigned-nas``/``total-nas``. This would cause a bias towards
+under-representing pool utilization. As this has a potential to cause serious
+confusion, ISC decided not to decrease ``assigned-nas`` immediately after
+receiving DHCPDECLINE, but to do it later when Kea recovers the address
+back to the available pool.
+
+.. _dhcp6-stats:
+
+Statistics in the DHCPv6 Server
+===============================
+
+The DHCPv6 server supports the following statistics:
+
+.. tabularcolumns:: |p{0.2\linewidth}|p{0.1\linewidth}|p{0.7\linewidth}|
+
+.. table:: DHCPv6 statistics
+ :class: longtable
+ :widths: 20 10 70
+
+
+ +---------------------------------------------------+----------------+------------------------------------+
+ | Statistic | Data Type | Description |
+ +===================================================+================+====================================+
+ | pkt6-received | integer | Number of DHCPv6 packets received. |
+ | | | This includes all packets: valid, |
+ | | | bogus, corrupted, rejected, etc. |
+ | | | This statistic is expected to grow |
+ | | | rapidly. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-receive-drop | integer | Number of incoming packets that |
+ | | | were dropped. The exact reason for |
+ | | | dropping packets is logged, but |
+ | | | the most common reasons may be: an |
+ | | | unacceptable or not supported |
+ | | | packet type is received, direct |
+ | | | responses are forbidden, the |
+ | | | server-id sent by the client does |
+ | | | not match the server's server-id, |
+ | | | or the packet is malformed. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-parse-failed | integer | Number of incoming packets that |
+ | | | could not be parsed. A non-zero |
+ | | | value of this statistic indicates |
+ | | | that the server received a |
+ | | | malformed or truncated packet. |
+ | | | This may indicate problems in the |
+ | | | network, faulty clients, faulty |
+ | | | relay agents, or a bug in the |
+ | | | server. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-solicit-received | integer | Number of SOLICIT packets |
+ | | | received. This statistic is |
+ | | | expected to grow; its increase |
+ | | | means that clients that just |
+ | | | booted started their configuration |
+ | | | process and their initial packets |
+ | | | reached the Kea server. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-advertise-received | integer | Number of ADVERTISE packets |
+ | | | received. ADVERTISE packets are |
+ | | | sent by the server and the server |
+ | | | is never expected to receive them. |
+ | | | A non-zero value of this statistic |
+ | | | indicates an error occurring in |
+ | | | the network. One likely cause |
+ | | | would be a misbehaving relay |
+ | | | agent that incorrectly forwards |
+ | | | ADVERTISE messages towards the |
+ | | | server, rather than back to the |
+ | | | clients. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-request-received | integer | Number of DHCPREQUEST packets |
+ | | | received. This statistic is |
+ | | | expected to grow. Its increase |
+ | | | means that clients that just |
+ | | | booted received the server's |
+ | | | response (DHCPADVERTISE) and |
+ | | | accepted it, and are now |
+ | | | requesting an address |
+ | | | (DHCPREQUEST). |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-reply-received | integer | Number of REPLY packets received. |
+ | | | This statistic is expected to |
+ | | | remain zero at all times, as REPLY |
+ | | | packets are sent by the server and |
+ | | | the server is never expected to |
+ | | | receive them. A non-zero value |
+ | | | indicates an error. One likely |
+ | | | cause would be a misbehaving relay |
+ | | | agent that incorrectly forwards |
+ | | | REPLY messages towards the server, |
+ | | | rather than back to the clients. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-renew-received | integer | Number of RENEW packets received. |
+ | | | This statistic is expected to |
+ | | | grow; its increase means that |
+ | | | clients received their addresses |
+ | | | and prefixes and are trying to |
+ | | | renew them. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-rebind-received | integer | Number of REBIND packets received. |
+ | | | A non-zero value indicates that |
+ | | | clients did not receive responses |
+ | | | to their RENEW messages (through |
+ | | | the regular lease-renewal |
+ | | | mechanism) and are attempting to |
+ | | | find any server that is able to |
+ | | | take over their leases. It may |
+ | | | mean that some servers' REPLY |
+ | | | messages never reached the |
+ | | | clients. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-release-received | integer | Number of RELEASE packets |
+ | | | received. This statistic is |
+ | | | expected to grow when a device is |
+ | | | being shut down in the network; it |
+ | | | indicates that the address or |
+ | | | prefix assigned is reported as no |
+ | | | longer needed. Note that many |
+ | | | devices, especially wireless, do |
+ | | | not send RELEASE packets either |
+ | | | because of design choice or due to |
+ | | | the client moving out of range. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-decline-received | integer | Number of DECLINE packets |
+ | | | received. This statistic is |
+ | | | expected to remain close to zero. |
+ | | | Its increase means that a client |
+ | | | leased an address, but discovered |
+ | | | that the address is currently used |
+ | | | by an unknown device in the |
+ | | | network. If this statistic is |
+ | | | growing, it may indicate a |
+ | | | misconfigured server or devices |
+ | | | that have statically assigned |
+ | | | conflicting addresses. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-infrequest-received | integer | Number of INFORMATION-REQUEST |
+ | | | packets received. This statistic |
+ | | | is expected to grow if there are |
+ | | | devices that are using stateless |
+ | | | DHCPv6. INFORMATION-REQUEST |
+ | | | messages are used by clients that |
+ | | | request stateless configuration, |
+ | | | i.e. options and parameters other |
+ | | | than addresses or prefixes. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-dhcpv4-query-received | integer | Number of DHCPv4-QUERY packets |
+ | | | received. This statistic is |
+ | | | expected to grow if there are |
+ | | | devices that are using |
+ | | | DHCPv4-over-DHCPv6. DHCPv4-QUERY |
+ | | | messages are used by DHCPv4 |
+ | | | clients on an IPv6-only line which |
+ | | | encapsulates the requests over |
+ | | | DHCPv6. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-dhcpv4-response-received | integer | Number of DHCPv4-RESPONSE packets |
+ | | | received. This statistic is |
+ | | | expected to remain zero at all |
+ | | | times, as DHCPv4-RESPONSE packets |
+ | | | are sent by the server and the |
+ | | | server is never expected to |
+ | | | receive them. A non-zero value |
+ | | | indicates an error. One likely |
+ | | | cause would be a misbehaving relay |
+ | | | agent that incorrectly forwards |
+ | | | DHCPv4-RESPONSE message towards |
+ | | | the server rather than back to the |
+ | | | clients. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-unknown-received | integer | Number of packets received of an |
+ | | | unknown type. A non-zero value of |
+ | | | this statistic indicates that the |
+ | | | server received a packet that it |
+ | | | was unable to recognize; either it |
+ | | | had an unsupported type or was |
+ | | | possibly malformed. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-sent | integer | Number of DHCPv6 packets sent. |
+ | | | This statistic is expected to grow |
+ | | | every time the server transmits a |
+ | | | packet. In general, it should |
+ | | | roughly match pkt6-received, as |
+ | | | most incoming packets cause the |
+ | | | server to respond. There are |
+ | | | exceptions (e.g. server receiving |
+ | | | a REQUEST with server-id matching |
+ | | | another server), so do not worry |
+ | | | if it is less than pkt6-received. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-advertise-sent | integer | Number of ADVERTISE packets sent. |
+ | | | This statistic is expected to grow |
+ | | | in most cases after a SOLICIT is |
+ | | | processed. There are certain |
+ | | | uncommon, but valid, cases where |
+ | | | incoming SOLICIT packets are |
+ | | | dropped, but in general this |
+ | | | statistic is expected to be close |
+ | | | to pkt6-solicit-received. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-reply-sent | integer | Number of REPLY packets sent. This |
+ | | | statistic is expected to grow in |
+ | | | most cases after a SOLICIT (with |
+ | | | rapid-commit), REQUEST, RENEW, |
+ | | | REBIND, RELEASE, DECLINE, or |
+ | | | INFORMATION-REQUEST is processed. |
+ | | | There are certain cases where |
+ | | | there is no response. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | pkt6-dhcpv4-response-sent | integer | Number of DHCPv4-RESPONSE packets |
+ | | | sent. This statistic is expected |
+ | | | to grow in most cases after a |
+ | | | DHCPv4-QUERY is processed. There |
+ | | | are certain cases where there is |
+ | | | no response. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].total-nas | big integer | Total number of NA addresses |
+ | | | available for DHCPv6 management |
+ | | | for a given subnet; in other |
+ | | | words, this is the count of all |
+ | | | addresses in all configured pools. |
+ | | | This statistic changes only during |
+ | | | configuration changes. It does not |
+ | | | take into account any addresses |
+ | | | that may be reserved due to host |
+ | | | reservation. The *id* is the |
+ | | | subnet-id of a given subnet. This |
+ | | | statistic is exposed for each |
+ | | | subnet separately, and is reset |
+ | | | during a reconfiguration event. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pool[pid].total-nas | big integer | Total number of NA addresses |
+ | | | available for DHCPv6 management |
+ | | | for a given subnet pool; in other |
+ | | | words, this is the count of all |
+ | | | addresses in configured subnet |
+ | | | pool. This statistic changes only |
+ | | | during configuration changes. It |
+ | | | does not take into account any |
+ | | | addresses that may be reserved due |
+ | | | to host reservation. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | The *pid* is the pool-id of a |
+ | | | given pool. This statistic is |
+ | | | exposed for each subnet pool |
+ | | | separately, and is reset during a |
+ | | | reconfiguration event. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | cumulative-assigned-nas | integer | Cumulative number of NA addresses |
+ | | | that have been assigned since |
+ | | | server startup. It is incremented |
+ | | | each time a NA address is assigned |
+ | | | and is not reset when the server |
+ | | | is reconfigured. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].cumulative-assigned-nas | integer | Cumulative number of NA addresses |
+ | | | in a given subnet that were |
+ | | | assigned. It increases every time |
+ | | | a new lease is allocated (as a |
+ | | | result of receiving a REQUEST |
+ | | | message) and is never decreased. |
+ | | | The *id* is the subnet-id of a |
+ | | | given subnet. This statistic is |
+ | | | exposed for each subnet |
+ | | | separately, and is reset during a |
+ | | | reconfiguration event. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pool[pid].cumulative-assigned-nas | integer | Cumulative number of NA addresses |
+ | | | in a given subnet pool that were |
+ | | | assigned. It increases every time |
+ | | | a new lease is allocated (as a |
+ | | | result of receiving a REQUEST |
+ | | | message) and is never decreased. |
+ | | | The *id* is the subnet-id of a |
+ | | | given subnet. The *pid* is the |
+ | | | pool-id of a given pool. This |
+ | | | statistic is exposed for each |
+ | | | subnet pool separately, and is |
+ | | | reset during a reconfiguration |
+ | | | event. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].assigned-nas | integer | Number of NA addresses in a given |
+ | | | subnet that are assigned. It |
+ | | | increases every time a new lease |
+ | | | is allocated (as a result of |
+ | | | receiving a REQUEST message) and |
+ | | | is decreased every time a lease is |
+ | | | released (a RELEASE message is |
+ | | | received) or expires. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | This statistic is exposed for each |
+ | | | subnet separately, and is reset |
+ | | | during a reconfiguration event. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pool[pid].assigned-nas | integer | Number of NA addresses in a given |
+ | | | subnet pool that are assigned. It |
+ | | | increases every time a new lease |
+ | | | is allocated (as a result of |
+ | | | receiving a REQUEST message) and |
+ | | | is decreased every time a lease is |
+ | | | released (a RELEASE message is |
+ | | | received) or expires. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | The *pid* is the pool-id of the |
+ | | | pool. This statistic is exposed |
+ | | | for each subnet pool separately, |
+ | | | and is reset during a |
+ | | | reconfiguration event. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].total-pds | big integer | Total number of PD prefixes |
+ | | | available for DHCPv6 management |
+ | | | for a given subnet; in other |
+ | | | words, this is the count of all |
+ | | | prefixes in all configured pools. |
+ | | | This statistic changes only during |
+ | | | configuration changes. Note it |
+ | | | does not take into account any |
+ | | | prefixes that may be reserved due |
+ | | | to host reservation. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | This statistic is exposed for each |
+ | | | subnet separately, and is reset |
+ | | | during a reconfiguration event. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pd-pool[pid].total-pds | big integer | Total number of PD prefixes |
+ | | | available for DHCPv6 management |
+ | | | for a given subnet pool; in other |
+ | | | words, this is the count of all |
+ | | | prefixes in configured subnet |
+ | | | pd-pool. This statistic changes |
+ | | | only during configuration changes. |
+ | | | It does not take into account any |
+ | | | prefixes that may be reserved due |
+ | | | to host reservation. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | The *pid* is the pool-id of a |
+ | | | given pool. This statistic is |
+ | | | exposed for each subnet pd-pool |
+ | | | separately, and is reset during a |
+ | | | reconfiguration event. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | cumulative-assigned-pds | integer | Cumulative number of PD prefixes |
+ | | | that have been assigned since |
+ | | | server startup. It is incremented |
+ | | | each time a PD prefix is assigned |
+ | | | and is not reset when the server |
+ | | | is reconfigured. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].cumulative-assigned-pds | integer | Cumulative number of PD prefixes |
+ | | | in a given subnet that were |
+ | | | assigned. It increases every time |
+ | | | a new lease is allocated (as a |
+ | | | result of receiving a REQUEST |
+ | | | message) and is never decreased. |
+ | | | The *id* is the subnet-id of a |
+ | | | given subnet. This statistic is |
+ | | | exposed for each subnet |
+ | | | separately, and is reset during a |
+ | | | reconfiguration event. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pd-pool[pid].cumulative-assigned-pds | integer | Cumulative number of PD prefixes |
+ | | | in a given subnet pd-pool that |
+ | | | were assigned. It increases every |
+ | | | time a new lease is allocated (as |
+ | | | a result of receiving a REQUEST |
+ | | | message) and is never decreased. |
+ | | | The *id* is the subnet-id of a |
+ | | | given subnet. The *pid* is the |
+ | | | pool-id of a given pd-pool. This |
+ | | | statistic is exposed for each |
+ | | | subnet pd-pool separately, and is |
+ | | | reset during a reconfiguration |
+ | | | event. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].assigned-pds | integer | Number of PD prefixes in a given |
+ | | | subnet that are assigned. It |
+ | | | increases every time a new lease |
+ | | | is allocated (as a result of |
+ | | | receiving a REQUEST message) and |
+ | | | is decreased every time a lease is |
+ | | | released (a RELEASE message is |
+ | | | received) or expires. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | This statistic is exposed for each |
+ | | | subnet separately, and is reset |
+ | | | during a reconfiguration event. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pd-pool[pid].assigned-pds | integer | Number of PD prefixes in a given |
+ | | | subnet pd-pool that are assigned. |
+ | | | It increases every time a new |
+ | | | lease is allocated (as a result of |
+ | | | receiving a REQUEST message) and |
+ | | | is decreased every time a lease is |
+ | | | released (a RELEASE message is |
+ | | | received) or expires. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | The *pid* is the pool-id of the |
+ | | | pd-pool. This statistic is exposed |
+ | | | for each subnet pd-pool |
+ | | | separately, and is reset during a |
+ | | | reconfiguration event. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | reclaimed-leases | integer | Number of expired leases that have |
+ | | | been reclaimed since server |
+ | | | startup. It is incremented each |
+ | | | time an expired lease is reclaimed |
+ | | | (counting both NA and PD |
+ | | | reclamations). This statistic |
+ | | | never decreases. It can be used as |
+ | | | a long-term indicator of how many |
+ | | | actual leases have been reclaimed. |
+ | | | This is a global statistic that |
+ | | | covers all subnets. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].reclaimed-leases | integer | Number of expired leases |
+ | | | associated with a given subnet |
+ | | | that have been reclaimed since |
+ | | | server startup. It is incremented |
+ | | | each time an expired lease is |
+ | | | reclaimed (counting both NA and PD |
+ | | | reclamations). The *id* is the |
+ | | | subnet-id of a given subnet. This |
+ | | | statistic is exposed for each |
+ | | | subnet separately. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pool[pid].reclaimed-leases | integer | Number of expired NA addresses |
+ | | | associated with a given subnet |
+ | | | pool that have been reclaimed |
+ | | | since server startup. It is |
+ | | | incremented each time an expired |
+ | | | lease is reclaimed. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | The *pid* is the pool-id of the |
+ | | | pool. This statistic is exposed |
+ | | | for each subnet pool separately, |
+ | | | and is reset during a |
+ | | | reconfiguration event. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pd-pool[pid].reclaimed-leases | integer | Number of expired PD prefixes |
+ | | | associated with a given subnet |
+ | | | pd-pool that have been reclaimed |
+ | | | since server startup. It is |
+ | | | incremented each time an expired |
+ | | | lease is reclaimed. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | The *pid* is the pool-id of the |
+ | | | pd-pool. This statistic is exposed |
+ | | | for each subnet pd-pool |
+ | | | separately, and is reset during a |
+ | | | reconfiguration event. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | declined-addresses | integer | Number of IPv6 addresses that are |
+ | | | currently declined; a count of the |
+ | | | number of leases currently |
+ | | | unavailable. Once a lease is |
+ | | | recovered, this statistic will be |
+ | | | decreased; ideally, this statistic |
+ | | | should be zero. If this statistic |
+ | | | is non-zero or increasing, a |
+ | | | network administrator should |
+ | | | investigate whether there is a |
+ | | | misbehaving device in the network. |
+ | | | This is a global statistic that |
+ | | | covers all subnets. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].declined-addresses | integer | Number of IPv6 addresses that are |
+ | | | currently declined in a given |
+ | | | subnet; a count of the number of |
+ | | | leases currently unavailable. Once |
+ | | | a lease is recovered, this |
+ | | | statistic will be decreased; |
+ | | | ideally, this statistic should be |
+ | | | zero. If this statistic is |
+ | | | non-zero or increasing, a network |
+ | | | administrator should investigate |
+ | | | whether there is a misbehaving |
+ | | | device in the network. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | This statistic is exposed for each |
+ | | | subnet separately. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pool[pid].declined-addresses | integer | Number of IPv6 addresses that are |
+ | | | currently declined in a given |
+ | | | subnet pool; a count of the number |
+ | | | of leases currently unavailable. |
+ | | | Once a lease is recovered, this |
+ | | | statistic will be decreased; |
+ | | | ideally, this statistic should be |
+ | | | zero. If this statistic is |
+ | | | non-zero or increasing, a network |
+ | | | administrator should investigate |
+ | | | whether there is a misbehaving |
+ | | | device in the network. The *id* is |
+ | | | the subnet-id of a given subnet. |
+ | | | The *pid* is the pool-id of the |
+ | | | pool. This statistic is exposed |
+ | | | for each subnet pool separately. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | reclaimed-declined-addresses | integer | Number of IPv6 addresses that were |
+ | | | declined, but have now been |
+ | | | recovered. Unlike |
+ | | | declined-addresses, this statistic |
+ | | | never decreases. It can be used as |
+ | | | a long-term indicator of how many |
+ | | | actual valid declines were |
+ | | | processed and recovered from. This |
+ | | | is a global statistic that covers |
+ | | | all subnets. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].reclaimed-declined-addresses | integer | Number of IPv6 addresses that were |
+ | | | declined, but have now been |
+ | | | recovered. Unlike |
+ | | | declined-addresses, this statistic |
+ | | | never decreases. It can be used as |
+ | | | a long-term indicator of how many |
+ | | | actual valid declines were |
+ | | | processed and recovered from. The |
+ | | | *id* is the subnet-id of a given |
+ | | | subnet. This statistic is exposed |
+ | | | for each subnet separately. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].pool[pid].reclaimed-declined-addresses | integer | Number of IPv6 addresses that were |
+ | | | declined, but have now been |
+ | | | recovered. Unlike |
+ | | | declined-addresses, this statistic |
+ | | | never decreases. It can be used as |
+ | | | a long-term indicator of how many |
+ | | | actual valid declines were |
+ | | | processed and recovered from. The |
+ | | | *id* is the subnet-id of a given |
+ | | | subnet. The *pid* is the pool-id |
+ | | | of the pool. This statistic is |
+ | | | exposed for each subnet pool |
+ | | | separately. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | v6-allocation-fail | integer | Number of total address allocation |
+ | | | failures for a particular client. |
+ | | | This consists in the number of |
+ | | | lease allocation attempts that the |
+ | | | server made before giving up and |
+ | | | was unable to use any of the |
+ | | | address pools. This is a global |
+ | | | statistic that covers all subnets. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].v6-allocation-fail | integer | Number of total address allocation |
+ | | | failures for a particular client. |
+ | | | This consists in the number of |
+ | | | lease allocation attempts that the |
+ | | | server made before giving up and |
+ | | | was unable to use any of the |
+ | | | address pools. The *id* is the |
+ | | | subnet-id of a given subnet. This |
+ | | | statistic is exposed for each |
+ | | | subnet separately. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | v6-allocation-fail-shared-network | integer | Number of address allocation |
+ | | | failures for a particular client |
+ | | | connected to a shared network. |
+ | | | This is a global statistic that |
+ | | | covers all subnets. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].v6-allocation-fail-shared-network | integer | Number of address allocation |
+ | | | failures for a particular client |
+ | | | connected to a shared network. |
+ | | | The *id* is the subnet-id of a |
+ | | | given subnet. This statistic is |
+ | | | exposed for each subnet |
+ | | | separately. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | v6-allocation-fail-subnet | integer | Number of address allocation |
+ | | | failures for a particular client |
+ | | | connected to a subnet that does |
+ | | | not belong to a shared network. |
+ | | | This is a global statistic that |
+ | | | covers all subnets. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].v6-allocation-fail-subnet | integer | Number of address allocation |
+ | | | failures for a particular client |
+ | | | connected to a subnet that does |
+ | | | not belong to a shared network. |
+ | | | The *id* is the subnet-id of a |
+ | | | given subnet. This statistic is |
+ | | | exposed for each subnet |
+ | | | separately. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | v6-allocation-fail-no-pools | integer | Number of address allocation |
+ | | | failures because the server could |
+ | | | not use any configured pools for |
+ | | | a particular client. It is also |
+ | | | possible that all of the subnets |
+ | | | from which the server attempted to |
+ | | | assign an address lack address |
+ | | | pools. In this case, it should be |
+ | | | considered misconfiguration if an |
+ | | | operator expects that some clients |
+ | | | should be assigned dynamic |
+ | | | addresses. This is a global |
+ | | | statistic that covers all subnets. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].v6-allocation-fail-no-pools | integer | Number of address allocation |
+ | | | failures because the server could |
+ | | | not use any configured pools for |
+ | | | a particular client. It is also |
+ | | | possible that all of the subnets |
+ | | | from which the server attempted to |
+ | | | assign an address lack address |
+ | | | pools. In this case, it should be |
+ | | | considered misconfiguration if an |
+ | | | operator expects that some clients |
+ | | | should be assigned dynamic |
+ | | | addresses. The *id* is the |
+ | | | subnet-id of a given subnet. This |
+ | | | statistic is exposed for each |
+ | | | subnet separately. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | v6-allocation-fail-classes | integer | Number of address allocation |
+ | | | failures when the client's packet |
+ | | | belongs to one or more classes. |
+ | | | There may be several reasons why a |
+ | | | lease was not assigned. One of |
+ | | | them may be a case when all pools |
+ | | | require packet to belong to |
+ | | | certain classes and the incoming |
+ | | | packet didn't belong to any of |
+ | | | them. Another case where this |
+ | | | information may be useful is to |
+ | | | point out that the pool reserved |
+ | | | to a given class has ran out of |
+ | | | addresses. This is a global |
+ | | | statistic that covers all subnets. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].v6-allocation-fail-classes | integer | Number of address allocation |
+ | | | failures when the client's packet |
+ | | | belongs to one or more classes. |
+ | | | There may be several reasons why a |
+ | | | lease was not assigned. One of |
+ | | | them may be a case when all pools |
+ | | | require packet to belong to |
+ | | | certain classes and the incoming |
+ | | | packet didn't belong to any of |
+ | | | them. Another case where this |
+ | | | information may be useful is to |
+ | | | point out that the pool reserved |
+ | | | to a given class has ran out of |
+ | | | addresses. The *id* is the |
+ | | | subnet-id of a given subnet. This |
+ | | | statistic is exposed for each |
+ | | | subnet separately. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | v6-ia-na-lease-reuses | integer | Number of times an IA_NA lease had |
+ | | | its CLTT increased in memory and |
+ | | | its expiration time left unchanged |
+ | | | in persistent storage as part of |
+ | | | the lease caching feature. This is |
+ | | | referred to as a lease reuse. |
+ | | | This statistic is global. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].v6-ia-na-lease-reuses | integer | Number of times an IA_NA lease had |
+ | | | its CLTT increased in memory and |
+ | | | its expiration time left unchanged |
+ | | | in persistent storage as part of |
+ | | | the lease caching feature. This is |
+ | | | referred to as a lease reuse. |
+ | | | This statistic is on a per-subnet |
+ | | | basis. The *id* is the subnet-id |
+ | | | of a given subnet. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | v6-ia-pd-lease-reuses | integer | Number of times an IA_PD lease had |
+ | | | its CLTT increased in memory and |
+ | | | its expiration time left unchanged |
+ | | | in persistent storage as part of |
+ | | | the lease caching feature. This is |
+ | | | referred to as a lease reuse. |
+ | | | This statistic is global. |
+ +---------------------------------------------------+----------------+------------------------------------+
+ | subnet[id].v6-ia-pd-lease-reuses | integer | Number of times an IA_PD lease had |
+ | | | its CLTT increased in memory and |
+ | | | its expiration time left unchanged |
+ | | | in persistent storage as part of |
+ | | | the lease caching feature. This is |
+ | | | referred to as a lease reuse. |
+ | | | This statistic is on a per-subnet |
+ | | | basis. The *id* is the subnet-id |
+ | | | of a given subnet. |
+ +---------------------------------------------------+----------------+------------------------------------+
+
+.. note::
+
+ The pool ID can be configured on each pool by explicitly setting the ``pool-id``
+ parameter in the pool parameter map. If not configured, ``pool-id`` defaults to 0.
+ The statistics related to pool ID 0 refer to all the statistics of all the pools
+ that have unconfigured ``pool-id``.
+ The pool ID does not need to be unique within the subnet or across subnets.
+ The statistics regarding a specific pool ID within a subnet will be combined with the
+ other statistics of all other pools with the same pool ID in the respective subnet.
+
+.. note::
+
+ This section describes DHCPv6-specific statistics. For a general
+ overview and usage of statistics, see :ref:`stats`.
+
+The DHCPv6 server provides two global parameters to control the default sample
+limits of statistics:
+
+- ``statistic-default-sample-count`` - determines the default maximum
+ number of samples which are kept. The special value of 0
+ indicates that a default maximum age should be used.
+
+- ``statistic-default-sample-age`` - determines the default maximum
+ age in seconds of samples which are kept.
+
+For instance, to reduce the statistic-keeping overhead, set
+the default maximum sample count to 1 so only one sample is kept:
+
+::
+
+ "Dhcp6": {
+ "statistic-default-sample-count": 1,
+ "subnet6": [
+ {
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+Statistics can be retrieved periodically to gain more insight into Kea operations. One tool that
+leverages that capability is ISC Stork. See :ref:`stork` for details.
+
+
+.. _dhcp6-ctrl-channel:
+
+Management API for the DHCPv6 Server
+====================================
+
+The management API allows the issuing of specific management commands,
+such as statistics retrieval, reconfiguration, or shutdown. For more
+details, see :ref:`ctrl-channel`. Currently, the only supported
+communication channel type is the UNIX stream socket. By default there are
+no sockets open; to instruct Kea to open a socket, the following entry
+in the configuration file can be used:
+
+::
+
+ "Dhcp6": {
+ "control-socket": {
+ "socket-type": "unix",
+ "socket-name": "/path/to/the/unix/socket"
+ },
+
+ "subnet6": [
+ {
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+The length of the path specified by the ``socket-name`` parameter is
+restricted by the maximum length for the UNIX socket name on the administrator's
+operating system, i.e. the size of the ``sun_path`` field in the
+``sockaddr_un`` structure, decreased by 1. This value varies on
+different operating systems, between 91 and 107 characters. Typical
+values are 107 on Linux and 103 on FreeBSD.
+
+Communication over the control channel is conducted using JSON
+structures. See the
+`Control Channel section in the Kea Developer's Guide
+<https://reports.kea.isc.org/dev_guide/d2/d96/ctrlSocket.html>`__
+for more details.
+
+The DHCPv6 server supports the following operational commands:
+
+- :isccmd:`build-report`
+- :isccmd:`config-get`
+- :isccmd:`config-hash-get`
+- :isccmd:`config-reload`
+- :isccmd:`config-set`
+- :isccmd:`config-test`
+- :isccmd:`config-write`
+- :isccmd:`dhcp-disable`
+- :isccmd:`dhcp-enable`
+- :isccmd:`leases-reclaim`
+- :isccmd:`list-commands`
+- :isccmd:`shutdown`
+- :isccmd:`status-get`
+- :isccmd:`version-get`
+
+as described in :ref:`commands-common`. In addition, it supports the
+following statistics-related commands:
+
+- :isccmd:`statistic-get`
+- :isccmd:`statistic-reset`
+- :isccmd:`statistic-remove`
+- :isccmd:`statistic-get`-all
+- :isccmd:`statistic-reset`-all
+- :isccmd:`statistic-remove`-all
+- :isccmd:`statistic-sample-age-set`
+- :isccmd:`statistic-sample-age-set`-all
+- :isccmd:`statistic-sample-count-set`
+- :isccmd:`statistic-sample-count-set`-all
+
+as described in :ref:`command-stats`.
+
+.. _dhcp6-user-contexts:
+
+User Contexts in IPv6
+=====================
+
+Kea allows the loading of hook libraries that can sometimes benefit from
+additional parameters. If such a parameter is specific to the whole
+library, it is typically defined as a parameter for the hook library.
+However, sometimes there is a need to specify parameters that are
+different for each pool.
+
+See :ref:`user-context` for additional background regarding the
+user-context idea. See :ref:`user-context-hooks` for a discussion from the
+hooks perspective.
+
+User contexts can be specified at global scope; at the shared-network, subnet,
+pool, client-class, option-data, or definition level; and via host
+reservation. One other useful feature is the ability to store comments or
+descriptions.
+
+Let's consider an example deployment of lightweight 4over6, an
+IPv6 transition technology that allows mapping IPv6 prefixes into full
+or partial IPv4 addresses. In the DHCP context, these are specific
+parameters that are supposed to be delivered to clients in the form of
+additional options. Values of these options are correlated to delegated
+prefixes, so it is reasonable to keep these parameters together with the
+prefix delegation (PD) pool. On the other hand, lightweight 4over6 is not a commonly used
+feature, so it is not a part of the base Kea code. The solution to this
+problem is to specify a user context. For each PD pool that is expected to be
+used for lightweight 4over6, a user context with extra parameters is
+defined. Those extra parameters will be used by a hook library
+and loaded only when dynamic calculation of the lightweight 4over6
+option is actually needed. An example configuration looks as follows:
+
+::
+
+ "Dhcp6": {
+ "subnet6": [ {
+ "pd-pools": [
+ {
+ "prefix": "2001:db8::",
+ "prefix-len": 56,
+ "delegated-len": 64,
+
+ # This is a pool specific context.
+ "user-context": {
+ "threshold-percent": 85,
+ "v4-network": "192.168.0.0/16",
+ "v4-overflow": "10.0.0.0/16",
+ "lw4over6-sharing-ratio": 64,
+ "lw4over6-v4-pool": "192.0.2.0/24",
+ "lw4over6-sysports-exclude": true,
+ "lw4over6-bind-prefix-len": 56
+ }
+ } ],
+ "id": 1,
+ "subnet": "2001:db8::/32",
+
+ # This is a subnet-specific context. Any type of
+ # information can be entered here as long as it is valid JSON.
+ "user-context": {
+ "comment": "Those v4-v6 migration technologies are tricky.",
+ "experimental": true,
+ "billing-department": 42,
+ "contacts": [ "Alice", "Bob" ]
+ }
+ } ]
+ }
+
+Kea does not interpret or use the user-context information; it simply
+stores it and makes it available to the hook libraries. It is up to each
+hook library to extract that information and use it. The parser
+translates a ``comment`` entry into a user context with the entry, which
+allows a comment to be attached inside the configuration itself.
+
+.. _dhcp6-std:
+
+Supported DHCPv6 Standards
+==========================
+
+The following standards are currently supported in Kea:
+
+- *Dynamic Host Configuration Protocol for IPv6*, `RFC
+ 3315 <https://tools.ietf.org/html/rfc3315>`__: Supported messages are
+ SOLICIT, ADVERTISE, REQUEST, RELEASE, RENEW, REBIND,
+ INFORMATION-REQUEST, CONFIRM, DECLINE and REPLY. The only
+ unsupported message is RECONFIGURE.
+
+- *Dynamic Host Configuration Protocol (DHCPv6) Options for
+ Session Initiation Protocol (SIP) Servers*, `RFC 3319
+ <https://tools.ietf.org/html/rfc3319>`__: All defined options are supported.
+
+- *IPv6 Prefix Options for Dynamic Host Configuration Protocol (DHCP)
+ version 6*, `RFC 3633 <https://tools.ietf.org/html/rfc3633>`__:
+ Supported options are IA_PD and IA_PREFIX. Also supported is the
+ status code NoPrefixAvail.
+
+- *DNS Configuration options for Dynamic Host Configuration Protocol for IPv6
+ (DHCPv6)*, `RFC 3646 <https://tools.ietf.org/html/rfc3646>`__: All defined
+ options are supported.
+
+- *Stateless Dynamic Host Configuration Protocol (DHCP) Service for IPv6*, `RFC
+ 3736 <https://tools.ietf.org/html/rfc3736>`__: Server operation in
+ stateless mode is supported. Kea is currently server-only, so the client side
+ is not implemented.
+
+- *Information Refresh Time Option for Dynamic Host Configuration Protocol for
+ IPv6 (DHCPv6)*, `RFC 4242 <https://tools.ietf.org/html/rfc4242>`__: The
+ sole defined option (``information-refresh-time``) is supported.
+
+- *The Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Relay
+ Agent Remote-ID Option*, `RFC
+ 4649 <https://tools.ietf.org/html/rfc4649>`__: The REMOTE-ID option is
+ supported.
+
+- *Resolution of Fully Qualified Domain Name (FQDN) Conflicts among Dynamic Host
+ Configuration Protocol (DHCP) Clients*, `RFC 4703
+ <https://tools.ietf.org/html/rfc4703>`__: The DHCPv6 server uses the DHCP-DDNS
+ server to resolve conflicts.
+
+- *The Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Client
+ Fully Qualified Domain Name (FQDN) Option*, `RFC
+ 4704 <https://tools.ietf.org/html/rfc4704>`__: The supported option is
+ CLIENT_FQDN.
+
+- *Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Option for
+ Dual-Stack Lite*, `RFC 6334 <https://tools.ietf.org/html/rfc6334>`__:
+ The AFTR-Name DHCPv6 Option is supported.
+
+- *Relay-Supplied DHCP Options*, `RFC
+ 6422 <https://tools.ietf.org/html/rfc6422>`__: The full functionality is
+ supported: OPTION_RSOO; the ability of the server to echo back the
+ options; verification of whether an option is RSOO-enabled; the ability to mark
+ additional options as RSOO-enabled.
+
+- *Prefix Exclude Option for DHCPv6-based Prefix Delegation*, `RFC
+ 6603 <https://tools.ietf.org/html/rfc6603>`__: The Prefix Exclude option
+ is supported.
+
+- *Client Link-Layer Address Option in DHCPv6*, `RFC
+ 6939 <https://tools.ietf.org/html/rfc6939>`__: The supported option is
+ the client link-layer address option.
+
+- *Issues and Recommendations with Multiple Stateful DHCPv6 Options*,
+ `RFC 7550 <https://tools.ietf.org/html/rfc7550>`__: All
+ recommendations related to the DHCPv6 server operation are supported.
+
+- *DHCPv6 Options for Configuration of Softwire Address and Port-Mapped
+ Clients*, `RFC 7598 <https://tools.ietf.org/html/rfc7598>`__: All
+ options indicated in this specification are supported by the DHCPv6
+ server.
+
+- *Generalized UDP Source Port for DHCP Relay*, `RFC 8357
+ <https://tools.ietf.org/html/rfc8357>`__: The Kea server is able
+ to handle Relay Source Port option in a received Relay-forward
+ message, remembers the UDP port and sends back Relay-reply with a
+ copy of the option to the relay agent using this UDP port.
+
+- *Dynamic Host Configuration Protocol for IPv6 (DHCPv6)*, `RFC 8415
+ <https://tools.ietf.org/html/rfc8415>`__: This new DHCPv6 protocol specification
+ obsoletes RFC 3315, RFC 3633, RFC 3736, RFC 4242, RFC 7083, RFC 7283,
+ and RFC 7550. All features, with the exception of the RECONFIGURE mechanism and
+ the now-deprecated temporary addresses (IA_TA) mechanism, are supported.
+
+- *Captive-Portal Identification in DHCP and Router Advertisements (RAs)*, `RFC 8910
+ <https://tools.ietf.org/html/rfc8910>`__: The Kea server can configure both v4
+ and v6 versions of the captive portal options.
+
+- *DHCP and Router Advertisement Options for the Discovery of Network-designated
+ Resolvers (DNR)*, `RFC 9463 <https://tools.ietf.org/html/rfc9463>`__. The Kea server
+ supports the DNR option. Part of its value (SvcParams) must be configured in
+ hex.
+
+.. _dhcp6-limit:
+
+DHCPv6 Server Limitations
+=========================
+
+These are the current known limitations of the Kea DHCPv6 server software. Most of
+them are reflections of the current stage of development and should be
+treated as “not implemented yet”, rather than actual limitations.
+
+- The server will allocate, renew, or rebind a maximum of one lease for
+ a particular IA option (IA_NA or IA_PD) sent by a client. `RFC
+ 8415 <https://tools.ietf.org/html/rfc8415>`__ allows for multiple
+ addresses or prefixes to be allocated for a single IA.
+
+- Temporary addresses are not supported. There is no intention to ever
+ implement this feature, as it is deprecated in `RFC 8415
+ <https://tools.ietf.org/html/rfc8415>`__.
+
+- Client reconfiguration (RECONFIGURE) is not yet supported.
+
+.. _dhcp6-srv-examples:
+
+Kea DHCPv6 Server Examples
+==========================
+
+A collection of simple-to-use examples for the DHCPv6 component of Kea
+is available with the source files, located in the ``doc/examples/kea6``
+directory.
+
+.. _dhcp6-cb:
+
+Configuration Backend in DHCPv6
+===============================
+
+In the :ref:`config-backend` section we have described the Configuration
+Backend (CB) feature, its applicability, and its limitations. This section focuses
+on the usage of the CB with the DHCPv6 server. It lists the supported
+parameters, describes limitations, and gives examples of DHCPv6
+server configurations to take advantage of the CB. Please also refer to
+the corresponding section :ref:`dhcp4-cb` for DHCPv4-specific usage of
+the CB.
+
+.. _dhcp6-cb-parameters:
+
+Supported Parameters
+--------------------
+
+The ultimate goal for the CB is to serve as a central configuration
+repository for one or multiple Kea servers connected to a database.
+In currently supported Kea versions, only a subset of
+the DHCPv6 server parameters can be configured in the database. All other
+parameters must be specified in the JSON configuration file, if
+required.
+
+All supported parameters can be configured via :ischooklib:`libdhcp_cb_cmds.so`.
+The general rule is that
+scalar global parameters are set using
+:isccmd:`remote-global-parameter6-set`; shared-network-specific parameters
+are set using :isccmd:`remote-network6-set`; and subnet-level and pool-level
+parameters are set using :isccmd:`remote-subnet6-set`. Whenever
+there is an exception to this general rule, it is highlighted in the
+table. Non-scalar global parameters have dedicated commands; for example,
+the global DHCPv6 options (``option-data``) are modified using
+:isccmd:`remote-option6-global-set`. Client classes, together with class-specific
+option definitions and DHCPv6 options, are configured using the
+:isccmd:`remote-class6-set` command.
+
+The :ref:`cb-sharing` section explains the concept of shareable
+and non-shareable configuration elements and the limitations for
+sharing them between multiple servers. In the DHCP configuration (both DHCPv4
+and DHCPv6), the shareable configuration elements are subnets and shared
+networks. Thus, they can be explicitly associated with multiple server tags.
+The global parameters, option definitions, and global options are non-shareable
+and can be associated with only one server tag. This rule does not apply
+to the configuration elements associated with ``all`` servers. Any configuration
+element associated with ``all`` servers (using the ``all`` keyword as a server tag) is
+used by all servers connecting to the configuration database.
+
+The following table lists DHCPv6-specific parameters supported by the
+Configuration Backend, with an indication of the level of the hierarchy
+at which it is currently supported.
+
+.. table:: List of DHCPv6 parameters supported by the Configuration Backend
+
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | Parameter | Global | Client | Shared | Subnet | Pool | Prefix |
+ | | | Class | Network | | | Delegation |
+ | | | | | | | Pool |
+ +=============================+============================+===========+===========+===========+===========+============+
+ | allocator | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | cache-max-age | yes | n/a | no | no | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | cache-threshold | yes | n/a | no | no | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | calculate-tee-times | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | client-class | n/a | n/a | yes | yes | yes | yes |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | ddns-send-update | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | ddns-override-no-update | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | ddns-override-client-update | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | ddns-replace-client-name | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | ddns-generated-prefix | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | ddns-qualifying-suffix | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | decline-probation-period | yes | n/a | n/a | n/a | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | delegated-len | n/a | n/a | n/a | n/a | n/a | yes |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | dhcp4o6-port | yes | n/a | n/a | n/a | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | excluded-prefix | n/a | n/a | n/a | n/a | n/a | yes |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | excluded-prefix-len | n/a | n/a | n/a | n/a | n/a | yes |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | hostname-char-set | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | hostname-char-replacement | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | interface | n/a | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | interface-id | n/a | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | max-preferred-lifetime | yes | yes | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | max-valid-lifetime | yes | yes | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | min-preferred-lifetime | yes | yes | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | min-valid-lifetime | yes | yes | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | option-data | yes (via | yes | yes | yes | yes | yes |
+ | | remote-option6-global-set) | | | | | |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | option-def | yes (via | yes | n/a | n/a | n/a | n/a |
+ | | remote-option-def6-set) | | | | | |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | pd-allocator | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | preferred-lifetime | yes | yes | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | prefix | n/a | n/a | n/a | n/a | n/a | yes |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | prefix-len | n/a | n/a | n/a | n/a | n/a | yes |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | rapid-commit | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | rebind-timer | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | relay | n/a | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | renew-timer | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | require-client-classes | n/a | n/a | yes | yes | yes | yes |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | reservation-mode | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | reservations-global | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | reservations-in-subnet | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | reservations-out-of-pool | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | t1-percent | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | t2-percent | yes | n/a | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+ | valid-lifetime | yes | yes | yes | yes | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+
+
+- ``yes`` - indicates that the parameter is supported at the given
+ level of the hierarchy and can be configured via the Configuration Backend.
+
+- ``no`` - indicates that a parameter is supported at the given level
+ of the hierarchy but cannot be configured via the Configuration Backend.
+
+- ``n/a`` - indicates that a given parameter is not applicable
+ at the particular level of the hierarchy or that the
+ server does not support the parameter at that level.
+
+Some scalar parameters contained by top level global maps are supported by the Configuration Backend.
+
+.. table:: List of DHCPv6 map parameters supported by the Configuration Backend
+
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | Parameter name (flat naming format) | Global map | Parameter name |
+ +==================================================================+==============================+==================================+
+ | compatibility.lenient-option-parsing | compatibility | lenient-option-parsing |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | control-socket.socket-name | control-socket | socket-name |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | control-socket.socket-type | control-socket | socket-type |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.enable-updates | dhcp-ddns | enable-updates |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.max-queue-size | dhcp-ddns | max-queue-size |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.ncr-format | dhcp-ddns | ncr-format |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.ncr-protocol | dhcp-ddns | ncr-protocol |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.sender-ip | dhcp-ddns | sender-ip |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.sender-port | dhcp-ddns | sender-port |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.server-ip | dhcp-ddns | server-ip |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.server-port | dhcp-ddns | server-port |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.generated-prefix | dhcp-ddns | generated-prefix |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.hostname-char-replacement | dhcp-ddns | hostname-char-replacement |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.hostname-char-set | dhcp-ddns | hostname-char-set |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.override-client-update | dhcp-ddns | override-client-update |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.override-no-update | dhcp-ddns | override-no-update |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.qualifying-suffix | dhcp-ddns | qualifying-suffix |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-ddns.replace-client-name | dhcp-ddns | replace-client-name |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | expired-leases-processing.flush-reclaimed-timer-wait-time | expired-leases-processing | flush-reclaimed-timer-wait-time |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | expired-leases-processing.hold-reclaimed-time | expired-leases-processing | hold-reclaimed-time |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | expired-leases-processing.max-reclaim-leases | expired-leases-processing | max-reclaim-leases |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | expired-leases-processing.max-reclaim-time | expired-leases-processing | max-reclaim-time |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | expired-leases-processing.reclaim-timer-wait-time | expired-leases-processing | reclaim-timer-wait-time |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | expired-leases-processing.unwarned-reclaim-cycles | expired-leases-processing | unwarned-reclaim-cycles |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | multi-threading.enable-multi-threading | multi-threading | enable-multi-threading |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | multi-threading.thread-pool-size | multi-threading | thread-pool-size |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | multi-threading.packet-queue-size | multi-threading | packet-queue-size |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | sanity-checks.lease-checks | sanity-checks | lease-checks |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | sanity-checks.extended-info-checks | sanity-checks | extended-info-checks |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | server-id.type | server-id | type |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | server-id.enterprise-id | server-id | enterprise-id |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | server-id.identifier | server-id | identifier |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | server-id.persist | server-id | persist |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-queue-control.enable-queue | dhcp-queue-control | enable-queue |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-queue-control.queue-type | dhcp-queue-control | queue-type |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+ | dhcp-queue-control.capacity | dhcp-queue-control | capacity |
+ +------------------------------------------------------------------+------------------------------+----------------------------------+
+
+.. _dhcp6-cb-json:
+
+Enabling the Configuration Backend
+----------------------------------
+
+Consider the following configuration snippet, which uses a MySQL configuration
+database:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "server-tag": "my DHCPv6 server",
+ "config-control": {
+ "config-databases": [
+ {
+ "type": "mysql",
+ "name": "kea",
+ "user": "kea",
+ "password": "kea",
+ "host": "2001:db8:1::1",
+ "port": 3302
+ }
+ ],
+ "config-fetch-wait-time": 20
+ },
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/kea/hooks/libdhcp_mysql_cb.so"
+ },
+ {
+ "library": "/usr/local/lib/kea/hooks/libdhcp_cb_cmds.so"
+ }
+ ]
+ }
+ }
+
+The configuration structure is almost identical to that of the DHCPv4 server
+(see :ref:`dhcp4-cb-json` for the detailed description).
+
+.. _dhcp6-compatibility:
+
+Kea DHCPv6 Compatibility Configuration Parameters
+=================================================
+
+ISC's intention is for Kea to follow the RFC documents to promote better standards
+compliance. However, many buggy DHCP implementations already exist that cannot be
+easily fixed or upgraded. Therefore, Kea provides an easy-to-use compatibility
+mode for broken or non-compliant clients. For that purpose, the compatibility option must be
+enabled to permit uncommon practices:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "compatibility": {
+ }
+ }
+ }
+
+
+Lenient Option Parsing
+----------------------
+
+By default, DHCPv6 option 16's ``vendor-class-data`` field is parsed as a set of
+length-value pairs. Same for tuple fields defined in custom options.
+
+With ``"lenient-option-parsing": true``, if a length ever exceeds the rest of
+the option's buffer, previous versions of Kea returned a log message ``unable to
+parse the opaque data tuple, the buffer length is x, but the tuple length is y``
+with ``x < y``; this no longer occurs. Instead, the value is considered to be the rest of the buffer,
+or in terms of the log message above, the tuple length ``y`` becomes ``x``.
+
+Enabling this flag is expected to improve compatibility with devices such as RAD
+MiNID.
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "compatibility": {
+ "lenient-option-parsing": true
+ }
+ }
+ }
+
+.. _dhcp6_allocation_strategies:
+
+Allocation Strategies in DHCPv6
+===============================
+
+A DHCP server follows a complicated algorithm to select a DHCPv6 lease for a client.
+It prefers assigning specific addresses or delegated prefixes requested by the client
+and the ones for which the client has reservations.
+
+When the client requests a specific delegated prefix, there are a few steps that
+:iscman:`kea-dhcp6` goes through to try to satisfy the request, in the following
+order:
+
+1. It searches for a lease that matches the requested prefix and prefix length.
+2. It searches for a lease that matches the prefix length.
+3. It searches for a lease with a larger address space (smaller prefix length).
+4. It searches for a lease with a smaller address space (larger prefix length).
+
+If the client requests no particular
+lease and has no reservations, or other clients are already using any requested leases, the server must
+find another available lease within the configured pools. A server function called
+an "allocator" is responsible in Kea for finding an available lease in such a case.
+
+The Kea DHCPv6 server provides configuration parameters to select different allocators
+at the global, shared-network, and subnet levels. It also
+allows for selecting different allocation strategies for address assignments and
+prefix delegation.
+
+Consider the following example:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "allocator": "iterative",
+ "pd-allocator": "random",
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "allocator": "random"
+ },
+ {
+ "id": 2,
+ "subnet": "2001:db8:2::/64",
+ "pd-allocator": "iterative"
+ }
+ ]
+ }
+ }
+
+The iterative allocator is globally selected for address assignments, while the
+random allocator is globally selected for prefix delegation. These settings
+are selectively overridden at the subnet level.
+
+The following sections describe the supported allocators and their
+recommended uses.
+
+
+Allocators Comparison
+---------------------
+
+In the table below, we briefly compare the supported allocators. The
+detailed allocators' descriptions are in later sections.
+
+.. table:: Comparison of the lease allocators supported by Kea DHCPv6
+
+ +------------------+-----------------------------+------------------------------+-----------------------+------------------------------+----------------+
+ | Allocator | Low Utilization Performance | High Utilization Performance | Lease Randomization | Startup/Configuration | Memory Usage |
+ +==================+=============================+==============================+=======================+==============================+================+
+ | Iterative | very high | low | no | very fast | low |
+ +------------------+-----------------------------+------------------------------+-----------------------+------------------------------+----------------+
+ | Random | high | low | yes | very fast | high (varying) |
+ +------------------+-----------------------------+------------------------------+-----------------------+------------------------------+----------------+
+ | Free Lease Queue | high | high | yes | slow (depends on pool sizes) | high (varying) |
+ +------------------+-----------------------------+------------------------------+-----------------------+------------------------------+----------------+
+
+
+Iterative Allocator
+-------------------
+This is the default allocator used by the Kea DHCPv6 server. It remembers the
+last offered lease and offers the following sequential lease to the next client.
+For example, it may offer addresses in this order: ``2001:db8:1::10``,
+``2001:db8:1::11``, ``2001:db8:1::12``, and so on. Similarly, it offers the
+next sequential delegated prefix after the previous one to the next client. The time to
+find and offer the next lease is very short; thus, this is the most performant
+allocator when pool utilization is low and there is a high probability
+that the next selected lease is available.
+
+The iterative allocation underperforms when multiple DHCP servers share a lease
+database or are connected to a cluster. The servers tend to offer and allocate
+the same blocks of addresses to different clients independently, which causes many
+allocation conflicts between the servers and retransmissions by clients. A random
+allocation addresses this issue by dispersing the allocation order.
+
+Random Allocator
+----------------
+
+The random allocator uses a uniform randomization function to select offered
+addresses and delegated prefixes from subnet pools. It is suitable in deployments
+where multiple servers are connected
+to a shared database or a database cluster. By dispersing the offered leases, the
+servers minimize the risk of allocating the same lease to two different clients at
+the same or nearly the same time. In addition, it improves the server's
+resilience against attacks based on allocation predictability.
+
+The random allocator is, however, slightly slower than the iterative allocator.
+Moreover, it increases the server's memory consumption because it must remember
+randomized leases to avoid offering them repeatedly. Memory consumption grows
+with the number of offered leases; in other words, larger pools and more
+clients increase memory consumption by random allocation.
+
+Free Lease Queue Allocator (Prefix Delegation Only)
+---------------------------------------------------
+
+This is a sophisticated allocator whose use should be considered in subnets
+with highly utilized delegated prefix pools. In such cases, it can take a
+considerable amount of time for the iterative or random allocator to find
+an available prefix, because they must repeatedly check whether there is a
+valid lease for a prefix they will offer. The number of checks can be as
+high as the number of delegated prefixes in the subnet when the subnet pools
+are exhausted, which can have a direct negative impact on the DHCP response time for
+each request.
+
+The Free Lease Queue (FLQ) allocator tracks lease allocations and de-allocations
+and maintains a running list of available delegated prefixes for each pool.
+It allows an available lease to be selected within a constant time, regardless of
+the subnet pools' utilization. The allocator continuously updates the list of
+free leases by removing any allocated leases and adding released or
+reclaimed ones.
+
+The following configuration snippet shows how to select the FLQ allocator
+for prefix delegation in a subnet:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "pd-allocator": "flq"
+ }
+ ]
+ }
+ }
+
+.. note::
+
+ The Free Lease Queue allocator can only be used for DHCPv6 prefix delegation.
+ An attempt to use this allocator for address assignment (with the ``allocator``
+ parameter) will cause a configuration error. DHCPv6 address pools are
+ typically very large and their utilization is low; in these situation, the benefits
+ of using the FLQ allocator diminish. The amount of time required for the
+ allocator to populate the free lease queue would cause the server to freeze
+ upon startup.
+
+There are several considerations that the administrator should take into account
+before using this allocator for prefix delegation. The FLQ allocator can heavily
+impact the server's startup and reconfiguration time, because the allocator
+has to populate the list of free leases for each subnet where it is used.
+These delays can be observed both during the configuration reload and when
+the subnets are created using :ischooklib:`libdhcp_subnet_cmds.so`. The allocator
+increases the memory consumption to hold the list of free leases,
+proportional to the total size of the pools for which this allocator is used.
+Finally, lease reclamation must be enabled with a low value of the
+``reclaim-timer-wait-time`` parameter, to ensure that the server frequently
+collects expired leases and makes them available for allocation via the
+free lease queue. Expired leases are not considered free by
+the allocator until they are reclaimed by the server. See
+:ref:`lease-reclamation` for more details about the lease reclamation process.
+
+We recommend that the FLQ allocator be selected
+only after careful consideration. The server puts no restrictions on the
+delegated prefix pool sizes used with the FLQ allocator, so we advise users to
+test how long it takes for the server to load the pools before deploying the
+configuration using the FLQ allocator in production. We also recommend
+specifying another allocator type in the global configuration settings
+and overriding this selection at the subnet or shared-network level, to use
+the FLQ allocator only for selected subnets. That way, when a new subnet is
+added without an allocator specification, the global setting is used, thus
+avoiding unnecessary impact on the server's startup time.
+
+Like the random allocator, the FLQ allocator offers leases in
+random order, which makes it suitable for use with a shared lease database.
diff --git a/doc/sphinx/arm/ext-gss-tsig.rst b/doc/sphinx/arm/ext-gss-tsig.rst
new file mode 100644
index 0000000..404a15e
--- /dev/null
+++ b/doc/sphinx/arm/ext-gss-tsig.rst
@@ -0,0 +1,1303 @@
+.. _gss-tsig:
+
+GSS-TSIG
+========
+
+.. _gss-tsig-overview:
+
+GSS-TSIG Overview
+-----------------
+
+Kea provides support for DNS updates, which can be protected using
+Transaction Signatures (or TSIG). This protection is often adequate.
+However, some systems, in particular Active Directory (AD) on Microsoft
+Windows servers, have chosen to adopt a more complex GSS-TSIG approach that offers
+additional capabilities, such as using negotiated dynamic keys.
+
+Kea supports GSS-TSIG to protect DNS updates sent by
+the Kea DHCP-DDNS (D2) server in a premium hook, called :ischooklib:`libddns_gss_tsig.so`.
+
+GSS-TSIG is defined in `RFC 3645 <https://tools.ietf.org/html/rfc3645>`__.
+The GSS-TSIG protocol itself is an implementation of generic GSS-API v2
+services, defined in `RFC 2743 <https://tools.ietf.org/html/rfc2743>`__.
+
+Many protocols are involved in this mechanism:
+
+ - Kerberos 5 - `RFC 4120 <https://tools.ietf.org/html/rfc4120>`__, which
+ provides the security framework;
+ - GSS-API (Generic Security Services Application Program Interface) -
+ `RFC 2743 <https://tools.ietf.org/html/rfc2743>`__ for the API,
+ `RFC 2744 <https://tools.ietf.org/html/rfc2743>`__ for the C bindings, and
+ `RFC 4121 <https://tools.ietf.org/html/rfc4121>`__ for the application
+ to Kerberos 5;
+ - SPNEGO (Simple and Protected GSS-API Negotiation Mechanism) -
+ `RFC 4178 <https://tools.ietf.org/html/rfc4178>`__ for the negotiation;
+ - DNS update `RFC 2136 <https://tools.ietf.org/html/rfc2136>`__;
+ - TSIG (Secret Key Transaction Authentication for DNS) -
+ `RFC 8945 <https://tools.ietf.org/html/rfc8945>`__, which
+ protects DNS exchanges;
+ - Secure Domain Name System (DNS) Dynamic Update -
+ `RFC 3007 <https://tools.ietf.org/html/rfc3007>`__, which is the
+ application of TSIG to DNS update protection;
+ - TKEY (Secret Key Establishment for DNS) -
+ `RFC 2930 <https://tools.ietf.org/html/rfc2930>`__, which establishes
+ secret keys for TSIG by transmitting crypto payloads between DNS
+ parties; and
+ - GSS-TSIG - `RFC 3645 <https://tools.ietf.org/html/rfc3645>`__, which
+ is the application of GSS-API to TSIG.
+
+To summarize, GSS-API for Kerberos 5 with SPNEGO and TKEY are used to
+negotiate a security context between the Kea D2 server and a DNS server:
+
+.. figure:: ../uml/tkey.*
+
+The security context is then used by GSS-TSIG to protect updates:
+
+.. figure:: ../uml/update.*
+
+The Kea implementation of GSS-TSIG uses a GSS-API for Kerberos 5 with
+the SPNEGO library. Two implementations meet this criteria: MIT Kerberos
+5 and Heimdal.
+
+.. _gss-tsig-install:
+
+GSS-TSIG Compilation
+--------------------
+
+The following procedure was tested on Ubuntu 20.10 and 21.04. A similar
+approach can be applied to other systems.
+
+1. Obtain the Kea sources and premium packages, extract the Kea sources,
+ and then extract the premium packages into the ``premium/`` directory within the Kea
+ source tree.
+
+2. Run autoreconf:
+
+.. code-block:: console
+
+ autoreconf -i
+
+3. Make sure ``./configure --help`` shows the ``--with-gssapi`` option.
+
+4. Install either the MIT (``libkrb5-dev``) or the Heimdal (``heimdal-dev``) library,
+ for instance:
+
+.. code-block:: console
+
+ sudo apt install libkrb5-dev
+
+5. Run ``configure`` with the ``--with-gssapi`` option:
+
+.. code-block:: console
+
+ ./configure --with-gssapi
+
+.. note:
+
+ It is ``--with-gssapi`` (with no dash between "gss" and "api"), to maintain
+ consistency with the BIND 9 option.
+
+The ``--with-gssapi`` parameter requires the ``krb5-config`` tool to be present. This
+tool is provided by both MIT Kerberos 5 and Heimdal; however, on some systems
+where both Kerberos 5 and Heimdal are installed, it is a symbolic link
+to one of them. If the tool is not in the standard location, it can be specified
+with ``--with-gssapi=/path/to/krb5-config``. It is strongly recommended
+to use the default installation locations provided by the packages.
+
+The ``./configure`` script should complete with a successful GSS-API
+detection, similar to this:
+
+::
+
+ GSS-API support:
+ GSSAPI_CFLAGS: -isystem /usr/include/mit-krb5
+ GSSAPI_LIBS: -L/usr/lib/x86_64-linux-gnu/mit-krb5 -Wl,-Bsymbolic-functions -Wl,-z,relro -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err
+
+6. Compile ``make -jX``, where X is the number of CPU cores
+ available.
+
+7. After compilation, :ischooklib:`libddns_gss_tsig.so` is available in the
+ ``premium/src/hooks/d2/gss_tsig`` directory. It can be loaded by :iscman:`kea-dhcp-ddns`.
+
+:ischooklib:`libddns_gss_tsig.so` was developed using the MIT Kerberos 5 implementation, but
+Heimdal is also supported. Note that Heimdal is picky about
+security-sensitive file permissions and is known to emit an unclear error message.
+It is a good idea to keep these files plain, with one link and no
+access for the group or other users.
+
+The ``krb5-config`` script should provide an ``--all`` option which
+identifies the implementation.
+
+.. _gss-tsig-deployment:
+
+GSS-TSIG Deployment
+-------------------
+
+Before using GSS-TSIG, a GSS-TSIG capable DNS server, such as BIND 9
+or Microsoft Active Directory (AD), must be deployed. Other
+GSS-TSIG capable implementations may work, but have not been tested.
+
+Kerberos 5 Setup
+~~~~~~~~~~~~~~~~
+
+There are two kinds of key tables (keytab files): the system one used
+by servers, and client tables used by clients. For Kerberos 5, Kea is a
+**client**.
+
+Install the Kerberos 5 client library and ``kadmin`` tool:
+
+.. code-block:: console
+
+ sudo apt install krb5-kdc krb5-admin-server
+
+The following examples use the ``EXAMPLE.ORG`` realm to demonstrate required
+configuration steps and settings.
+
+The Kerberos 5 client library must be configured to accept incoming requests
+for the realm ``EXAMPLE.ORG`` by updating the ``krb5.conf`` file
+(e.g. on Linux: /etc/krb5.conf):
+
+.. code-block:: ini
+
+ [libdefaults]
+ default_realm = EXAMPLE.ORG
+ kdc_timesync = 1
+ ccache_type = 4
+ forwardable = true
+ proxiable = true
+
+ [realms]
+ EXAMPLE.ORG = {
+ kdc = kdc.example.org
+ admin_server = kdc.example.org
+ }
+
+In addition to the ``krb5.conf`` file, the ``kdc.conf`` file can be used
+(e.g. on Linux: /etc/krb5kdc/kdc.conf):
+
+.. code-block:: ini
+
+ [kdcdefaults]
+ kdc_ports = 750,88
+
+ [realms]
+ EXAMPLE.ORG = {
+ database_name = /var/lib/krb5kdc/principal
+ admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab
+ acl_file = /etc/krb5kdc/kadm5.acl
+ key_stash_file = /etc/krb5kdc/stash
+ kdc_ports = 750,88
+ max_life = 10h 0m 0s
+ max_renewable_life = 7d 0h 0m 0s
+ master_key_type = des3-hmac-sha1
+ #supported_enctypes = aes256-cts:normal aes128-cts:normal
+ default_principal_flags = +preauth
+ }
+
+The ``kadmind`` daemon Access Control List (ACL) must be configured to give
+permissions to the DNS client principal to access the Kerberos 5 database
+(e.g. on Linux: /etc/krb5kdc/kadm5.acl):
+
+.. code-block:: ini
+
+ DHCP/admin.example.org@EXAMPLE.ORG *
+
+The administrator password for the default realm must be set:
+
+.. code-block:: console
+
+ krb5_newrealm
+
+After the following message is displayed, enter
+the password for the default realm:
+
+.. code-block:: console
+
+ This script should be run on the master KDC/admin server to initialize
+ a Kerberos realm. It will ask you to type in a master key password.
+ This password will be used to generate a key that is stored in
+ /etc/krb5kdc/stash. You should try to remember this password, but it
+ is much more important that it be a strong password than that it be
+ remembered. However, if you lose the password and /etc/krb5kdc/stash,
+ you cannot decrypt your Kerberos database.
+ Loading random data
+ Initializing database '/var/lib/krb5kdc/principal' for realm 'EXAMPLE.ORG',
+ master key name 'K/M@EXAMPLE.ORG'
+ You will be prompted for the database Master Password.
+ It is important that you NOT FORGET this password.
+ Enter KDC database master key:
+
+Then retype the password:
+
+.. code-block:: console
+
+ Re-enter KDC database master key to verify:
+
+If successfully applied, the following message is displayed:
+
+.. code-block:: console
+
+ Now that your realm is set up you may wish to create an administrative
+ principal using the addprinc subcommand of the kadmin.local program.
+ Then, this principal can be added to /etc/krb5kdc/kadm5.acl so that
+ you can use the kadmin program on other computers. Kerberos admin
+ principals usually belong to a single user and end in /admin. For
+ example, if jruser is a Kerberos administrator, then in addition to
+ the normal jruser principal, a jruser/admin principal should be
+ created.
+
+ Don't forget to set up DNS information so your clients can find your
+ KDC and admin servers. Doing so is documented in the administration
+ guide.
+
+The next step is to create the principals for the BIND 9 DNS server
+(the service protected by the GSS-TSIG TKEY) and for the DNS client
+(the Kea DHCP-DDNS server).
+
+The BIND 9 DNS server principal (used for authentication) is created the
+following way:
+
+.. code-block:: console
+
+ kadmin.local -q "addprinc -randkey DNS/server.example.org"
+
+If successfully created, the following message is displayed:
+
+.. code-block:: console
+
+ No policy specified for DNS/server.example.org@EXAMPLE.ORG; defaulting to no policy
+ Authenticating as principal root/admin@EXAMPLE.ORG with password.
+ Principal "DNS/server.example.org@EXAMPLE.ORG" created.
+
+The DNS server principal must be exported so that it can be used by the BIND 9
+DNS server. Only this principal is required, and it is exported to the keytab
+file with the name ``dns.keytab``.
+
+.. code-block:: console
+
+ kadmin.local -q "ktadd -k /tmp/dns.keytab DNS/server.example.org"
+
+If successfully exported, the following message is displayed:
+
+.. code-block:: console
+
+ Authenticating as principal root/admin@EXAMPLE.ORG with password.
+ Entry for principal DNS/server.example.org with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/tmp/dns.keytab.
+ Entry for principal DNS/server.example.org with kvno 2, encryption type aes128-cts-hmac-sha1-96 added to keytab WRFILE:/tmp/dns.keytab.
+
+The DHCP client principal (used by the Kea DHCP-DDNS server) is created the
+following way:
+
+.. code-block:: console
+
+ kadmin.local -q "addprinc -randkey DHCP/admin.example.org"
+
+If successfully created, the following message is displayed:
+
+.. code-block:: console
+
+ No policy specified for DHCP/admin.example.org@EXAMPLE.ORG; defaulting to no policy
+ Authenticating as principal root/admin@EXAMPLE.ORG with password.
+ Principal "DHCP/admin.example.org@EXAMPLE.ORG" created.
+
+The DHCP client principal must be exported so that it can be used by the
+Kea DHCP-DDNS server and the GSS-TSIG hook library. It is exported to the client
+keytab file with the name ``dhcp.keytab``.
+
+.. code-block:: console
+
+ kadmin.local -q "ktadd -k /tmp/dhcp.keytab DHCP/admin.example.org"
+
+Finally, the ``krb5-admin-server`` must be restarted:
+
+.. code-block:: console
+
+ systemctl restart krb5-admin-server.service
+
+BIND 9 with GSS-TSIG Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The BIND 9 DNS server must be configured to use GSS-TSIG, and to use the
+previously exported DNS server principal from the keytab file ``dns.keytab``.
+Updating the ``named.conf`` file is required:
+
+.. code-block:: console
+
+ options {
+ ...
+ directory "/var/cache/bind";
+ dnssec-validation auto;
+ listen-on-v6 { any; };
+ tkey-gssapi-keytab "/etc/bind/dns.keytab";
+ };
+ zone "example.org" {
+ type master;
+ file "/var/lib/bind/db.example.org";
+ update-policy {
+ grant "DHCP/admin.example.org@EXAMPLE.ORG" zonesub any;
+ };
+ };
+ zone "84.102.10.in-addr.arpa" {
+ type master;
+ file "/etc/bind/db.10";
+ };
+
+The zone files should have an entry for the server principal FQDN
+``server.example.org``.
+
+The ``/etc/bind/db.10`` file needs to be created or updated:
+
+.. code-block:: console
+
+ ;
+ ; BIND reverse data file for local loopback interface
+ ;
+ $TTL 604800 ; 1 week
+ @ IN SOA server.example.org. root.example.org. (
+ 2 ; Serial
+ 604800 ; Refresh
+ 86400 ; Retry
+ 2419200 ; Expire
+ 604800 ; Negative Cache TTL
+ )
+ ;
+ @ IN NS ns.
+ 40 IN PTR ns.example.org.
+
+The ``/var/lib/bind/db.example.org`` file needs to be created or updated:
+
+.. code-block:: console
+
+ $ORIGIN .
+ $TTL 604800 ; 1 week
+ example.org IN SOA server.example.org. root.example.org. (
+ 8 ; serial
+ 604800 ; refresh (1 week)
+ 86400 ; retry (1 day)
+ 2419200 ; expire (4 weeks)
+ 604800 ; minimum (1 week)
+ )
+ NS example.org.
+ A ${BIND9_IP_ADDR}
+ AAAA ::1
+ $ORIGIN example.org.
+ kdc A ${KDC_IP_ADDR}
+ server A ${BIND9_IP_ADDR}
+
+After any configuration change the server must be reloaded or
+restarted:
+
+.. code-block:: console
+
+ systemctl restart named.service
+
+It is possible to get the status or restart the logs:
+
+.. code-block:: console
+
+ systemctl status named.service
+ journalctl -u named | tail -n 30
+
+Windows Active Directory Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This sub-section is based on an Amazon AWS provided Microsoft Windows Server
+2016 with Active Directory pre-installed, so it describes only the steps used
+for GSS-TSIG deployment. (For the complete configuration process, please refer to
+Microsoft's documentation or other external resources. We found `this <https://www.tenforums.com/tutorials/51456-windows-server-2016-setup-local-domain-controller.html>`__ tutorial very
+useful during configuration of our internal QA testing systems.)
+
+Two Active Directory (AD) user accounts are needed:
+ - the first account is used to download AD information, such as
+ the client key table of Kea
+ - the second account is mapped to the Kea DHCP client principal
+
+Kea needs to know:
+ - the server IP address
+ - the domain/realm name: the domain is in lower case, the realm in upper
+ case, both without a final dot
+ - the server name
+
+The second account (named ``kea`` below) is used to create a Service
+Principal Name (SPN):
+
+.. code-block:: console
+
+ setspn -S DHCP/kea.<domain> kea
+
+After a shared secret key is generated and put in a key table file:
+
+.. code-block:: console
+
+ ktpass -princ DHCP/kea.<domain>@<REALM> -mapuser kea +rndpass -mapop set -ptype KRB5_NT_PRINCIPAL -out dhcp.keytab
+
+The ``dhcp.keytab`` takes the same usage as for UNIX Kerberos.
+
+GSS-TSIG Troubleshooting
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+While testing GSS-TSIG integration with Active Directory we came across
+one very cryptic error:
+
+.. code-block:: console
+
+ INFO [kea-dhcp-ddns.gss-tsig-hooks/4678.139690935890624] GSS_TSIG_VERIFY_FAILED GSS-TSIG verify failed: gss_verify_mic failed with GSSAPI error:
+ Major = 'A token had an invalid Message Integrity Check (MIC)' (393216), Minor = 'Packet was replayed in wrong direction' (100002).
+
+In our case, the problem was that the Kea D2 server was trying to perform an update of a reverse
+DNS zone while it was not configured. An easy solution is to add a reverse DNS
+zone similar to the one configured in Kea. To do that, open the "DNS Manager" and choose
+"DNS" from the list; from the dropdown list, choose "Reverse Lookup Zones"; then
+click "Action" and "New Zone"; finally, follow the New Zone Wizard to add a new zone.
+
+The standard requires both anti-replay and sequence services. Experiences with the BIND 9 nsupdate
+showed the sequence service led to problems so it is disabled by default in the hook. It seems
+the anti-replay service can also lead to problems with Microsoft DNS servers so it is now
+configurable. Note that these security services are useless for DNS dynamic update which was
+designed to run over UDP so with out of order and duplicated messages.
+
+.. _gss-tsig-using:
+
+Using GSS-TSIG
+--------------
+
+There are a number of steps required to enable the GSS-TSIG mechanism:
+
+1. :ischooklib:`libddns_gss_tsig.so` must be loaded by :iscman:`kea-dhcp-ddns`.
+2. The GSS-TSIG-capable DNS servers must be specified with their parameters.
+
+An excerpt from a D2 server configuration is provided below; more examples are available in the
+``doc/examples/ddns`` directory in the Kea sources.
+
+.. code-block:: javascript
+ :linenos:
+ :emphasize-lines: 57-117
+
+
+ {
+ "DhcpDdns": {
+ // The following parameters are used to receive NCRs (NameChangeRequests)
+ // from the local Kea DHCP server. Make sure your kea-dhcp4 and kea-dhcp6
+ // matches this.
+ "ip-address": "127.0.0.1",
+ "port": 53001,
+ "dns-server-timeout" : 1000,
+
+ // Forward zone: secure.example.org. It uses GSS-TSIG. It is served
+ // by two DNS servers, which listen for DDNS requests at 192.0.2.1
+ // and 192.0.2.2.
+ "forward-ddns":
+ {
+ "ddns-domains":
+ [
+ // DdnsDomain for zone "secure.example.org."
+ {
+ "name": "secure.example.org.",
+ "comment": "DdnsDomain example",
+ "dns-servers":
+ [
+ { // This server has an entry in gss/servers and
+ // thus will use GSS-TSIG.
+ "ip-address": "192.0.2.1"
+ },
+ { // This server also has an entry there, so will
+ // use GSS-TSIG, too.
+ "ip-address": "192.0.2.2",
+ "port": 5300
+ }
+ ]
+ }
+ ]
+ },
+
+ // Reverse zone: we want to update the reverse zone "2.0.192.in-addr.arpa".
+ "reverse-ddns":
+ {
+ "ddns-domains":
+ [
+ {
+ "name": "2.0.192.in-addr.arpa.",
+ "dns-servers":
+ [
+ {
+ // There is a GSS-TSIG definition for this server (see
+ // DhcpDdns/gss-tsig/servers), so it will use
+ // Krb/GSS-TSIG.
+ "ip-address": "192.0.2.1"
+ }
+ ]
+ }
+ ]
+ },
+
+ // The GSS-TSIG hook is loaded and its configuration is specified here.
+ "hooks-libraries": [
+ {
+ "library": "/opt/lib/libddns_gss_tsig.so",
+ "parameters": {
+ // This section governs the GSS-TSIG integration. Each server
+ // mentioned in forward-ddns and/or reverse-ddns needs to have
+ // an entry here to be able to use GSS-TSIG defaults (optional,
+ // if specified they apply to all the GSS-TSIG servers, unless
+ // overwritten on specific server level).
+
+ "server-principal": "DNS/server.example.org@EXAMPLE.ORG",
+ "client-principal": "DHCP/admin.example.org@EXAMPLE.ORG",
+
+ // client-keytab and credentials-cache can both be used to
+ // store client keys. As credentials cache is more flexible,
+ // it is recommended to use it. Typically, using both at the
+ // same time may cause problems.
+ // "client-keytab": "FILE:/etc/dhcp.keytab", // toplevel only
+ "credentials-cache": "FILE:/etc/ccache", // toplevel only
+ "gss-replay-flag": true, // GSS anti replay service
+ "gss-sequence-flag": false, // no GSS sequence service
+ "tkey-lifetime": 3600, // 1 hour
+ "rekey-interval": 2700, // 45 minutes
+ "retry-interval": 120, // 2 minutes
+ "tkey-protocol": "TCP",
+ "fallback": false,
+
+ // The list of GSS-TSIG capable servers
+ "servers": [
+ {
+ // First server (identification is required)
+ "id": "server1",
+ "domain-names": [ ], // if not specified or empty, will
+ // match all domains that want to
+ // use this IP+port pair
+ "ip-address": "192.0.2.1",
+ "port": 53,
+ "server-principal": "DNS/server1.example.org@EXAMPLE.ORG",
+ "client-principal": "DHCP/admin1.example.org@EXAMPLE.ORG",
+ "gss-replay-flag": false, // no GSS anti replay service
+ "gss-sequence-flag": false, // no GSS sequence service
+ "tkey-lifetime": 7200, // 2 hours
+ "rekey-interval": 5400, // 90 minutes
+ "retry-interval": 240, // 4 minutes
+ "tkey-protocol": "TCP",
+ "fallback": true // if no key is available fallback to the
+ // standard behavior (vs skip this server)
+ },
+ {
+ // The second server (it has most of the parameters missing
+ // as those are using the defaults specified above)
+ "id": "server2",
+ "ip-address": "192.0.2.2",
+ "port": 5300
+ }
+ ]
+ }
+ }
+ ]
+
+ // Additional parameters, such as logging, control socket and
+ // others omitted for clarity.
+ }
+
+ }
+
+This configuration file contains a number of extra elements.
+
+First, a list of forward and/or reverse domains with related DNS servers
+identified by their IP+port pairs is defined. If the port is not
+specified, the default of 53 is assumed. This is similar to basic mode, with no
+authentication done using TSIG keys, with the
+exception that static TSIG keys are not referenced by name.
+
+Second, :ischooklib:`libddns_gss_tsig.so` must be specified on the
+``hooks-libraries`` list. This hook takes many parameters. The most important
+one is ``servers``, which is a list of GSS-TSIG-capable servers. If there are
+several servers and they share some characteristics, the values can be specified
+in the ``parameters`` scope as defaults. In the example above, the defaults that apply
+to all servers, unless otherwise specified on a per-server scope, are defined in
+lines 63 through 68. The defaults can be skipped if there is only one server
+defined, or if all servers have different values.
+
+.. table:: List of available parameters
+
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | Name | Scope | Type | Default value | Description |
+ | | | | | |
+ +===================+==========+=========+=====================+================================+
+ | client-keytab | global / | string | empty | the Kerberos **client** key |
+ | | server | | | table |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | credentials-cache | global / | string | empty | the Kerberos credentials cache |
+ | | server | | | |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | server-principal | global / | string | empty | the Kerberos principal name of |
+ | | server | | | the DNS server that will |
+ | | | | | receive updates |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | client-principal | global / | string | empty | the Kerberos principal name of |
+ | | server | | | the Kea D2 service |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | gss-replay-flag | global / | true / | true | require the GSS anti replay |
+ | | server | false | | service (GSS_C_REPLAY_FLAG) |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | gss-sequence-flag | global / | true / | false | require the GSS sequence |
+ | | server | false | | service (GSS_C_SEQUENCE_FLAG) |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | tkey-protocol | global / | string | "TCP" | the protocol used to establish |
+ | | server | "TCP" / | | the security context with the |
+ | | | "UDP" | | DNS servers |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | tkey-lifetime | global / | uint32 | | 3600 seconds | the lifetime of GSS-TSIG keys |
+ | | server | | | ( 1 hour ) | |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | rekey-interval | global / | uint32 | | 2700 seconds | the time interval the keys are |
+ | | server | | | ( 45 minutes ) | checked for rekeying |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | retry-interval | global / | uint32 | | 120 seconds | the time interval to retry to |
+ | | server | | | ( 2 minutes ) | create a key if any error |
+ | | | | | occurred previously |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | fallback | global / | true / | false | the behavior to fallback to |
+ | | server | false | | non-GSS-TSIG when GSS-TSIG |
+ | | | | | should be used but no GSS-TSIG |
+ | | | | | key is available. |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | exchange-timeout | global / | uint32 | | 3000 milliseconds | the time used to wait for the |
+ | | server | | | ( 3 seconds ) | GSS-TSIG TKEY exchange to |
+ | | | | | finish before it timeouts |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | user-context | global / | string | empty | the user-provided data in JSON |
+ | | server | | | format (not used by |
+ | | | | | the GSS-TSIG hook) |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | comment | global / | string | empty | ignored |
+ | | server | | | |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | id | server | string | empty | identifier to a DNS server |
+ | | | | | (required) |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | domain-names | server | list of | empty | the many-to-one relationship |
+ | | | strings | | between D2 DNS servers and |
+ | | | | | GSS-TSIG DNS servers |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | ip-address | server | IPv4 / | empty | the IP address at which the |
+ | | | IPv6 | | GSS-TSIG DNS server listens |
+ | | | address | | for DDNS and TKEY requests |
+ | | | | | (required) |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+ | port | server | uint16 | 53 | the DNS transport port at |
+ | | | | | which the GSS-TSIG DNS server |
+ | | | | | listens for DDNS and TKEY |
+ | | | | | requests |
+ +-------------------+----------+---------+---------------------+--------------------------------+
+
+The global parameters are described below:
+
+- ``client-keytab`` specifies the Kerberos **client** key table.
+ For instance, ``FILE:<filename>`` can be used to point to a specific file.
+ This parameter can be specified only once, in the parameters scope,
+ and is the equivalent of setting the ``KRB5_CLIENT_KTNAME`` environment
+ variable. An empty value is silently ignored.
+
+- ``credentials-cache`` specifies the Kerberos credentials cache.
+ For instance, ``FILE:<filename>`` can be used to point to a file or,
+ if using a directory which supports more than one principal,
+ ``DIR:<directory-path>``.
+ This parameter can be specified only once, in the parameters scope,
+ and is the equivalent of setting the ``KRB5CCNAME`` environment
+ variable. An empty value is silently ignored.
+
+- ``server-principal`` is the Kerberos principal name of the DNS
+ server that receives updates. In other words, this is the
+ DNS server's name in the Kerberos system. This parameter is
+ mandatory, and uses the typical Kerberos notation:
+ ``<SERVICE-NAME>/<server-domain-name>@<REALM>``.
+
+- ``client-principal`` is the Kerberos principal name of the Kea D2
+ service. It is optional, and uses the typical Kerberos notation:
+ ``<SERVICE-NAME>/<server-domain-name>@<REALM>``.
+
+- ``gss-replay-flag`` determines if the GSS anti replay service is
+ required. It is by default but this can be disabled.
+
+- ``gss-sequence-flag`` determines if the GSS sequence service is
+ required. It is not by default but is required by the standard
+ so it can be enabled.
+
+- ``tkey-protocol`` determines which protocol is used to establish the
+ security context with the DNS servers. Currently, the only supported
+ values are TCP (the default) and UDP.
+
+- ``tkey-lifetime`` determines the lifetime of GSS-TSIG keys in the
+ TKEY protocol. The value must be greater than the ``rekey-interval``
+ value. It is expressed in seconds and defaults to 3600 (one hour).
+
+- ``rekey-interval`` governs the time interval at which the keys for each configured
+ server are checked for rekeying, i.e. when a new key is created to replace the
+ current usable one if its age is greater than the ``rekey-interval`` value.
+ The value must be smaller than the ``tkey-lifetime`` value (it is recommended
+ to be set between 50% and 80% of the ``tkey-lifetime`` value). It is expressed in
+ seconds and defaults to 2700 (45 minutes, or 75% of one hour).
+
+- ``retry-interval`` governs the time interval at which to retry to create a key if any
+ error occurred previously for any configured server. The value must be smaller
+ than the ``rekey-interval`` value, and should be at most 1/3 of the difference
+ between ``tkey-lifetime`` and ``rekey-interval``. It is expressed in seconds
+ and defaults to 120 (2 minutes).
+
+- ``fallback`` governs the behavior when GSS-TSIG should be used (a
+ matching DNS server is configured) but no GSS-TSIG key is available.
+ If set to ``false`` (the default), this server is skipped; if
+ set to ``true``, the DNS server is ignored and the DNS update
+ is sent with the configured DHCP-DDNS protection (e.g. TSIG key), or
+ without any protection when none was configured.
+
+- ``exchange-timeout`` governs the amount of time to wait for the GSS-TSIG TKEY
+ exchange to finish before the process times out. It is expressed in milliseconds and
+ defaults to 3000 (3 seconds).
+
+- ``user-context`` is an optional parameter (see :ref:`user-context`
+ for a general description of user contexts in Kea).
+
+- ``comment`` is allowed but currently ignored.
+
+- ``servers`` specifies the list of DNS servers where GSS-TSIG is enabled.
+
+The server map parameters are described below:
+
+- ``id`` assigns an identifier to a DNS server. It is used for statistics
+ and commands. It is required, and must be both not empty and unique.
+
+- ``domain-names`` governs the many-to-one relationship between D2 DNS
+ servers and GSS-TSIG DNS servers: for each domain name on this list,
+ Kea looks for a D2 DNS server for this domain with the specified IP address
+ and port. An empty list (the default) means that all domains
+ match.
+
+- ``ip-address`` specifies the IP address at which the GSS-TSIG DNS server
+ listens for DDNS and TKEY requests. It is a mandatory parameter.
+
+- ``port`` specifies the DNS transport port on which the GSS-TSIG DNS server
+ listens for DDNS and TKEY requests. It defaults to 53.
+
+- ``server-principal`` is the Kerberos principal name of the DNS server
+ that receives updates. The ``server-principal`` parameter set at the per-server
+ level takes precedence over one set at the global level. It is a mandatory parameter which must be specified at
+ either the global or the server level.
+
+- ``client-principal`` is the Kerberos principal name of the Kea D2
+ service for this DNS server. The ``client-principal`` parameter set at the per-server
+ level takes precedence over one set at the global level. It is an optional parameter.
+
+- ``gss-replay-flag`` determines if the GSS anti replay service is
+ required. The ``gss-replay-flag`` parameter set at the per-server
+ level takes precedence over one set at the global level. It is an optional parameter
+ which defaults to true.
+
+- ``gss-sequence-flag`` determines if the GSS sequence service is
+ required. The ``gss-sequence-flag`` parameter set at the per-server
+ level takes precedence over one set at the global level. It is an optional parameter
+ which defaults to false.
+
+- ``tkey-protocol`` determines which protocol is used to establish the
+ security context with the DNS server. The ``tkey-protocol`` parameter set at the per-server
+ level takes precedence over one set at the global level. The default and supported values
+ for the per-server level parameter are the same as
+ for the global-level parameter.
+
+- ``tkey-lifetime`` determines the lifetime of GSS-TSIG keys in the
+ TKEY protocol for the DNS server. The ``tkey-lifetime`` parameter set at the per-server
+ level takes precedence over one set at the global level. The default and supported values
+ for the per-server level parameter are the same as
+ for the global-level parameter.
+
+- ``rekey-interval`` governs the time interval at which the keys for this particular
+ server are checked for rekeying, i.e. when a new key is created to replace the
+ current usable one if its age is greater than the ``rekey-interval`` value.
+ The value must be smaller than the ``tkey-lifetime`` value (it is recommended
+ to be set between 50% and 80% of the ``tkey-lifetime`` value). The ``rekey-interval``
+ parameter set at the per-server level takes precedence over one set at the global
+ level. The default and supported values for the per-server level parameter are the same as
+ for the global-level parameter.
+
+- ``retry-interval`` governs the time interval at which to retry to create a key if any
+ error occurred previously for this particular server. The value must be
+ smaller than the ``rekey-interval`` value, and should be at most 1/3 of the
+ difference between ``tkey-lifetime`` and ``rekey-interval``. The
+ ``retry-interval`` parameter set at the per-server level takes precedence over one set at the global
+ level. The default and supported values for the per-server level parameter are the same as
+ for the global-level parameter.
+
+- ``fallback`` governs the behavior when GSS-TSIG should be used (a
+ matching DNS server is configured) but no GSS-TSIG key is available.
+ The ``fallback`` parameter set at the per-server level takes precedence over one set at the global
+ level. The default and supported values for the per-server level parameter are the same as
+ for the global-level parameter..
+
+- ``exchange-timeout`` governs the amount of time to wait for the GSS-TSIG TKEY
+ exchange to finish before the process times out. The ``exchange-timeout`` parameter
+ set at the per-server level takes precedence over one set at the global
+ level. The default and supported values for the per-server level parameter are the same as
+ for the global-level parameter.
+
+- ``user-context`` is an optional parameter (see :ref:`user-context`
+ for a general description of user contexts in Kea).
+
+- ``comment`` is allowed but currently ignored.
+
+.. note::
+
+ Generally it is not recommended to specify both the client keytab (``client-keytab``)
+ and the credentials cache (``credentials-cache``), although this may
+ differ between Kerberos implementations. The client keytab is just for
+ the client key and is typically used to specify the key explicitly in more
+ static manner, while the credentials cache can be used to store multiple
+ credentials and can be dynamically updated by the Kerberos library. As such,
+ the credentials-cache is more flexible and thus the recommended alternative.
+
+ Also note that only the read access right is needed to use the cache.
+ Fetching credentials and updating the cache requires the write access
+ right.
+
+
+GSS-TSIG Automatic Key Removal
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The server periodically deletes keys after they have been expired more than three times the
+length of the maximum key lifetime (the ``tkey-lifetime`` parameter).
+The user has the option to purge keys on demand by using the :isccmd:`gss-tsig-purge-all`
+command or the :isccmd:`gss-tsig-purge` command.
+
+
+GSS-TSIG Configuration for Deployment
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When using Kerberos 5 and BIND 9 as described in :ref:`gss-tsig-deployment`,
+the local resolver must point to the BIND 9 ``named`` server address. The
+local Kerberos must also be configured by putting the following text into the ``krb5.conf`` file:
+
+.. code-block:: ini
+
+ [libdefaults]
+ default_realm = EXAMPLE.ORG
+ kdc_timesync = 1
+ ccache_type = 4
+ forwardable = true
+ proxiable = true
+ [realms]
+ EXAMPLE.ORG = {
+ kdc = kdc.example.org
+ admin_server = kdc.example.org
+ }
+
+With Windows AD, the DNS service is provided by AD, which also provides
+the Kerberos service. The required text in the ``krb5.conf`` file becomes:
+
+.. code-block:: ini
+
+ [libdefaults]
+ default_realm = <REALM>
+ kdc_timesync = 1
+ ccache_type = 4
+ forwardable = true
+ proxiable = true
+ [realms]
+ ${REALM} = {
+ kdc = <AD_IP_ADDR>
+ admin_server = <AD_IP_ADDR>
+ }
+
+Even when the GSS-API library can use the secret from the client key
+table, it is far better for performance to get and cache credentials.
+
+This can be done manually via the command:
+
+.. code-block:: console
+
+ kinit -k -t /tmp/dhcp.keytab DHCP/admin.example.org
+
+or, when using AD:
+
+.. code-block:: console
+
+ kinit -k -t /tmp/dhcp.keytab DHCP/kea.<domain>
+
+The credential cache can be displayed using ``klist``.
+
+In production, it is better to rely on a Kerberos Credential Manager as
+the System Security Services Daemon (``sssd``).
+
+When using BIND 9, the server principal is in the form "DNS/server.example.org@EXAMPLE.ORG¨;
+with AD, the format is "DNS/<server>.<domain>@<REALM>".
+
+.. _stats-gss-tsig:
+
+GSS-TSIG Statistics
+-------------------
+
+The GSS-TSIG hook library introduces new statistics at global and
+per-DNS-server levels:
+
+- ``gss-tsig-key-created`` - the number of created GSS-TSIG keys
+- ``tkey-sent`` - the number of sent TKEY exchange initial requests
+- ``tkey-success`` - the number of TKEY exchanges which completed with a success
+- ``tkey-timeout`` - the number of TKEY exchanges which completed on timeout
+- ``tkey-error`` - the number of TKEY exchanges which completed with an error other than
+ a timeout
+
+The relationship between keys and DNS servers is very different between
+the D2 code and static TSIG keys, and GSS-TSIG keys and DNS servers:
+
+ - a static TSIG key can be shared between many DNS servers;
+ - a GSS-TSIG key is only used by one DNS server inside a dedicated
+ set of keys.
+
+.. _commands-gss-tsig:
+
+GSS-TSIG Commands
+-----------------
+
+The GSS-TSIG hook library supports some commands, which are described below.
+
+.. isccmd:: gss-tsig-get-all
+.. _command-gss-tsig-get-all:
+
+The ``gss-tsig-get-all`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command lists all the GSS-TSIG servers and keys.
+
+An example command invocation looks like this:
+
+.. code-block:: json
+
+ {
+ "command": "gss-tsig-get-all"
+ }
+
+Here is an example of a response returning one GSS-TSIG server and one key:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "1 GSS-TSIG servers and 1 keys",
+ "arguments": {
+ "gss-tsig-servers": [
+ {
+ "id": "foo",
+ "ip-address": "192.1.2.3",
+ "port": 53,
+ "server-principal": "DNS/foo.com@FOO.COM",
+ "key-name-suffix": "foo.com.",
+ "tkey-lifetime": 3600,
+ "tkey-protocol": "TCP",
+ "keys": [
+ {
+ "name": "1234.sig-foo.com.",
+ "inception-date": "2021-09-05 12:23:36.281176",
+ "server-id": "foo",
+ "expire-date": "2021-09-05 13:23:36.281176",
+ "status": "not yet ready",
+ "tkey-exchange": true
+ }
+ ]
+ },
+ {
+ "id": "bar",
+ "ip-address": "192.1.2.4",
+ "port": 53,
+ "server-principal": "DNS/bar.com@FOO.COM",
+ "key-name-suffix": "bar.com.",
+ "tkey-lifetime": 7200,
+ "tkey-protocol": "UDP",
+ "keys": [ ]
+ }
+ ]
+ }
+ }
+
+.. isccmd:: gss-tsig-get
+.. _command-gss-tsig-get:
+
+The ``gss-tsig-get`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command retrieves information about the specified GSS-TSIG server.
+
+An example command invocation looks like this:
+
+.. code-block:: json
+
+ {
+ "command": "gss-tsig-get",
+ "arguments": {
+ "server-id": "foo"
+ }
+ }
+
+Here is an example of a response returning information about the server "foo":
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "GSS-TSIG server[foo] found",
+ "arguments": {
+ "id": "foo",
+ "ip-address": "192.1.2.3",
+ "port": 53,
+ "server-principal": "DNS/foo.com@FOO.COM",
+ "key-name-suffix": "foo.com.",
+ "tkey-lifetime": 3600,
+ "tkey-protocol": "TCP",
+ "keys": [
+ {
+ "name": "1234.sig-foo.com.",
+ "server-id": "foo",
+ "inception-date": "2021-09-05 12:23:36.281176",
+ "expire-date": "2021-09-05 13:23:36.281176",
+ "status": "not yet ready",
+ "tkey-exchange": true
+ }
+ ]
+ }
+ }
+
+.. isccmd:: gss-tsig-list
+.. _command-gss-tsig-list:
+
+The ``gss-tsig-list`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command generates a list of GSS-TSIG server IDs and key names.
+
+An example command invocation looks like this:
+
+.. code-block:: json
+
+ {
+ "command": "gss-tsig-list"
+ }
+
+Here is an example of a response returning two GSS-TSIG servers and three keys:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "2 GSS-TSIG servers and 3 keys",
+ "arguments": {
+ "gss-tsig-servers": [
+ "foo",
+ "bar"
+ ],
+ "gss-tsig-keys": [
+ "1234.example.com.",
+ "5678.example.com.",
+ "43888.example.org."
+ ]
+ }
+ }
+
+.. isccmd:: gss-tsig-key-get
+.. _command-gss-tsig-key-get:
+
+The ``gss-tsig-key-get`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command retrieves information about the specified GSS-TSIG key.
+
+An example command invocation looks like this:
+
+.. code-block:: json
+
+ {
+ "command": "gss-tsig-key-get",
+ "arguments": {
+ "key-name": "1234.sig-foo.com."
+ }
+ }
+
+Here is an example of a response returning information about GSS-TSIG key "1234.sig-foo.com.":
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "GSS-TSIG key '1234.sig-foo.com.' found",
+ "arguments": {
+ "name": "1234.sig-foo.com.",
+ "server-id": "foo",
+ "inception-date": "2021-09-05 12:23:36.281176",
+ "expire-date": "2021-09-05 13:23:36.281176",
+ "status": "not yet ready",
+ "tkey-exchange": true
+ }
+ }
+
+.. isccmd:: gss-tsig-key-expire
+.. _command-gss-tsig-key-expire:
+
+The ``gss-tsig-key-expire`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command expires the specified GSS-TSIG key.
+
+An example command invocation looks like this:
+
+.. code-block:: json
+
+ {
+ "command": "gss-tsig-key-expire",
+ "arguments": {
+ "key-name": "1234.sig-foo.com."
+ }
+ }
+
+Here is an example of a response indicating that GSS-TSIG key "1234.sig-foo.com." has been expired:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "GSS-TSIG key '1234.sig-foo.com.' expired"
+ }
+
+.. isccmd:: gss-tsig-key-del
+.. _command-gss-tsig-key-del:
+
+The ``gss-tsig-key-del`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command deletes the specified GSS-TSIG key.
+
+An example command invocation looks like this:
+
+.. code-block:: json
+
+ {
+ "command": "gss-tsig-key-del",
+ "arguments": {
+ "key-name": "1234.sig-foo.com."
+ }
+ }
+
+Here is an example of a response indicating that GSS-TSIG key "1234.sig-foo.com." has been deleted:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "GSS-TSIG key '1234.sig-foo.com.' deleted"
+ }
+
+.. isccmd:: gss-tsig-purge-all
+.. _command-gss-tsig-purge-all:
+
+The ``gss-tsig-purge-all`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command removes all unusable GSS-TSIG keys.
+
+An example command invocation looks like this:
+
+.. code-block:: json
+
+ {
+ "command": "gss-tsig-purge-all"
+ }
+
+Here is an example of a response indicating that two GSS-TSIG keys have been purged:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "2 purged GSS-TSIG keys"
+ }
+
+.. isccmd:: gss-tsig-purge
+.. _command-gss-tsig-purge:
+
+The ``gss-tsig-purge`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command removes unusable GSS-TSIG keys for the specified server.
+
+An example command invocation looks like this:
+
+.. code-block:: json
+
+ {
+ "command": "gss-tsig-purge",
+ "arguments": {
+ "server-id": "foo"
+ }
+ }
+
+Here is an example of a response indicating that two GSS-TSIG keys for server "foo" have been purged:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "2 purged keys for GSS-TSIG server[foo]"
+ }
+
+.. isccmd:: gss-tsig-rekey-all
+.. _command-gss-tsig-rekey-all:
+
+The ``gss-tsig-rekey-all`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command unconditionally creates new GSS-TSIG keys (rekeys) for
+all DNS servers.
+
+An example command invocation looks like this:
+
+.. code-block:: json
+
+ {
+ "command": "gss-tsig-rekey-all"
+ }
+
+Here is an example of a response indicating that a rekey was performed:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "rekeyed"
+ }
+
+This command is useful when, for instance, the DHCP-DDNS server is
+reconnected to the network.
+
+.. isccmd:: gss-tsig-rekey
+.. _command-gss-tsig-rekey:
+
+The ``gss-tsig-rekey`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command unconditionally creates new GSS-TSIG keys (rekeys) for
+a specified DNS server.
+
+An example command invocation looks like this:
+
+.. code-block:: json
+
+ {
+ "command": "gss-tsig-rekey",
+ "arguments": {
+ "server-id": "foo"
+ }
+ }
+
+Here is an example of a response indicating that a rekey was performed:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "GSS-TSIG server[foo] rekeyed"
+ }
+
+This command is typically used when a DNS server has been rebooted, so
+that existing GSS-TSIG keys shared with this server can no longer be used.
diff --git a/doc/sphinx/arm/ext-netconf.rst b/doc/sphinx/arm/ext-netconf.rst
new file mode 100644
index 0000000..b21e7bb
--- /dev/null
+++ b/doc/sphinx/arm/ext-netconf.rst
@@ -0,0 +1,1159 @@
+.. _netconf:
+
+YANG/NETCONF
+============
+
+.. _netconf-overview:
+
+Overview
+--------
+
+The Network Configuration Protocol, or NETCONF, is a network management protocol defined
+in `RFC 4741 <https://tools.ietf.org/html/rfc4741>`__. It uses the YANG modeling language,
+defined in `RFC 6020 <https://tools.ietf.org/html/rfc6020>`__, to provide a uniform way
+of handling the configuration syntax of various networking devices. Kea provides optional
+support for a YANG/NETCONF interface with the :iscman:`kea-netconf` agent.
+
+.. _netconf-install:
+
+Installing NETCONF
+------------------
+
+To get its NETCONF capabilities, Kea requires the v2 versions of libyang and
+Sysrepo. The specific versions that have been thoroughly tested with Kea are:
+
+* libyang v2.1.4
+* sysrepo v2.2.12
+* libyang-cpp v1.1.0 (ae7d649ea75da081725c119dd553b2ef3121a6f8)
+* sysrepo-cpp v1.1.0 (02634174ffc60568301c3d9b9b7cf710cff6a586)
+
+.. note::
+
+ For users who are unable to upgrade to one of the versions of libyang
+ and Sysrepo listed above, these are the oldest versions known to work
+ reliably with current Kea releases:
+
+ * libyang v2.0.256 (56d4e07ef1cdeab3eb2e6700247f83ec9148edcc)
+ * sysrepo v2.1.84
+ * libyang-cpp v1.1.0 (7824d9a862f2dc1d8ad4f6a90ab6cee9200f7c81)
+ * sysrepo-cpp v1.1.0 (e66b2f0c53a428eeb743d355cf86fb30e8e491f1)
+
+.. note::
+
+ :iscman:`kea-netconf` may be compatible with later versions of libyang and
+ Sysrepo, but only the versions listed above have been thoroughly
+ tested by ISC.
+
+Installing from packages is recommended, if they are provided by the system. If
+not, users can build from sources following the directions below, which
+should work on all popular operating systems.
+
+.. _libyang-install-sources:
+
+Installing ``libyang`` From Sources
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: console
+
+ $ git clone https://github.com/CESNET/libyang.git
+ $ cd libyang
+ $ git checkout v2.1.4
+ $ mkdir build
+ $ cd build
+ $ cmake ..
+ $ make
+ $ make install
+
+.. _sysrepo-install-sources:
+
+Installing ``sysrepo`` From Sources
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: console
+
+ $ git clone https://github.com/sysrepo/sysrepo.git
+ $ cd sysrepo
+ $ git checkout v2.2.12
+ $ mkdir build
+ $ cd build
+ $ cmake -DREPO_PATH=/etc/sysrepo ..
+ $ make
+ $ make install # without sudo if you're doing development and want to run unit tests
+
+.. _libyang-cpp-install-sources:
+
+Installing ``libyang-cpp`` From Sources
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: console
+
+ $ git clone https://github.com/CESNET/libyang-cpp.git
+ $ cd libyang-cpp
+ $ git checkout ae7d649ea75da081725c119dd553b2ef3121a6f8
+ $ mkdir build
+ $ cd build
+ $ cmake -DBUILD_TESTING=OFF ..
+ $ make
+ $ make install
+
+.. _sysrepo-cpp-install-sources:
+
+Installing ``sysrepo-cpp`` From Sources
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: console
+
+ $ git clone https://github.com/sysrepo/sysrepo-cpp.git
+ $ cd sysrepo-cpp
+ $ git checkout 02634174ffc60568301c3d9b9b7cf710cff6a586
+ $ mkdir build
+ $ cd build
+ $ cmake -DBUILD_TESTING=OFF ..
+ $ make
+ $ make install
+
+.. _compiling-with-netconf:
+
+Compiling With NETCONF
+----------------------
+
+1. Obtain the Kea sources.
+
+.. code-block:: console
+
+ $ git clone gitlab.isc.org/isc-projects/kea.git
+ $ cd kea
+
+2. Configure the build.
+
+.. code-block:: console
+
+ $ autoreconf -f -i
+ $ ./configure --with-libyang --with-libyang-cpp --with-sysrepo --with-sysrepo-cpp
+
+.. note::
+
+ If any of the libraries are installed in a custom location, the
+ ``--with`` flags accept the installations paths as values.
+
+3. Check ``config.report`` to verify NETCONF support.
+
+::
+
+ NETCONF:
+ yes
+
+ libyang:
+ LIBYANG_CPPFLAGS:
+ LIBYANG_INCLUDEDIR: -I/usr/local/include
+ LIBYANG_LIBS: -L/usr/local/lib -lyang -Wl,-R/usr/local/lib -lyang
+ LIBYANG_PREFIX: /usr/local
+ LIBYANG_VERSION: 2.1.4
+
+ libyang-cpp:
+ LIBYANGCPP_CPPFLAGS:
+ LIBYANGCPP_INCLUDEDIR: -I/usr/local/include
+ LIBYANGCPP_LIBS: -L/usr/local/lib -lyang-cpp -Wl,-R/usr/local/lib -lyang-cpp
+ LIBYANGCPP_PREFIX: /usr/local
+ LIBYANGCPP_VERSION: 1.1.0
+
+ sysrepo:
+ SYSREPO_CPPFLAGS:
+ SYSREPO_INCLUDEDIR: -I/usr/local/include
+ SYSREPO_LIBS: -L/usr/local/lib -lsysrepo -Wl,-R/usr/local/lib -lsysrepo
+ SYSREPO_PREFIX: /usr/local
+ SYSREPO_VERSION: 2.2.12
+
+ SR_REPO_PATH: /etc/sysrepo
+ SR_PLUGINS_PATH: /usr/local/lib/sysrepo/plugins
+ SRPD_PLUGINS_PATH: /usr/local/lib/sysrepo-plugind/plugins
+
+ sysrepo-cpp:
+ SYSREPOCPP_CPPFLAGS:
+ SYSREPOCPP_INCLUDEDIR: -I/usr/local/include
+ SYSREPOCPP_LIBS: -L/usr/local/lib -lsysrepo-cpp -Wl,-R/usr/local/lib -lsysrepo-cpp
+ SYSREPOCPP_PREFIX : /usr/local
+ SYSREPOCPP_VERSION: 1.1.0
+
+4. Build as usual.
+
+ $ make
+
+.. _sysrepo-overview:
+
+Quick Sysrepo Overview
+----------------------
+
+This section offers a brief overview of a subset of available
+functions in Sysrepo. For more complete information, see the
+`Sysrepo homepage <https://www.sysrepo.org>`__.
+
+In YANG, configurations and state data are described in YANG syntax
+in module files named ``<module-name>[@<revision>].yang``
+
+The revision part is optional and follows the ``YYYY-MM-DD`` format. An alternate
+XML syntax YIN is defined but less user-friendly. Top-level modules are
+named in Kea models (a short version of schema models).
+
+There are two major modules that Kea is able to support: ``kea-dhcp4-server`` and
+``kea-dhcp6-server``. While there is an active effort in the DHC working group at
+IETF to develop a DHCPv6 YANG model, a similar initiative in the past for DHCPv4
+failed. Therefore, Kea uses its own dedicated models for DHCPv4 and DHCPv6 but
+partially supports the IETF model for DHCPv6.
+
+All of the models have extra modules as dependencies, which are also provided.
+All of the modules can be found in ``src/share/yang/modules`` in sources and in
+``share/kea/yang/modules`` in the installation directory. This directory is
+referred to as `${share_directory}` in the commands below.
+
+To install modules from sources or upgrade them from older revisions,
+run the following command. In the case of a revision upgrade, YANG
+data will be migrated automatically to the new module schema.
+
+.. code-block:: console
+
+ $ ${share_directory}/yang/modules/utils/reinstall.sh
+
+However, if there are any issues during the upgrade process, and data can be recreated from a NETCONF
+client or through other means, Kea modules can be easily uninstalled before installing again
+via this command:
+
+.. code-block:: console
+
+ $ ${share_directory}/yang/modules/utils/reinstall.sh -u
+
+This script should be able to reinstall Sysrepo. However, the ``-s``
+flag can also be used to specify a path:
+
+.. code-block:: console
+
+ $ ./src/share/yang/modules/utils/reinstall.sh -s /path/to/sysrepo
+
+To individually install all modules:
+
+.. code-block:: console
+
+ $ cd ./src/share/yang/modules
+ $ sysrepoctl -i ./ietf-dhcpv6-server*.yang
+ $ sysrepoctl -i ./kea-dhcp4-server*.yang
+ $ sysrepoctl -i ./kea-dhcp6-server*.yang
+ ...
+
+The installation should look similar to the following:
+
+.. code-block:: console
+
+ $ ./src/share/yang/modules/utils/reinstall.sh
+ [INF] Connection 2 created.
+ [INF] Module "keatest-module" was installed.
+ [INF] File "keatest-module@2022-11-30.yang" was installed.
+ [INF] No datastore changes to apply.
+ [INF] Connection 4 created.
+ [ERR] Module "ietf-interfaces@2018-02-20" already installed.
+ [INF] No datastore changes to apply.
+ [INF] Connection 7 created.
+ [ERR] Module "ietf-dhcpv6-client" is already in sysrepo.
+ [INF] No datastore changes to apply.
+ [INF] Connection 9 created.
+ [ERR] Module "ietf-dhcpv6-relay" is already in sysrepo.
+ [INF] No datastore changes to apply.
+ [INF] Connection 11 created.
+ [ERR] Module "ietf-dhcpv6-server" is already in sysrepo.
+ [INF] No datastore changes to apply.
+ [INF] Connection 13 created.
+ [ERR] Write permission "ietf-yang-types" check failed.
+ [INF] No datastore changes to apply.
+ [INF] Connection 15 created.
+ [ERR] Module "ietf-dhcpv6-options" is already in sysrepo.
+ [INF] No datastore changes to apply.
+ [INF] Connection 17 created.
+ [ERR] Module "ietf-dhcpv6-types" is already in sysrepo.
+ [INF] No datastore changes to apply.
+ [INF] Connection 21 created.
+ [INF] Module "kea-types" was installed.
+ [INF] File "kea-types@2019-08-12.yang" was installed.
+ [INF] No datastore changes to apply.
+ [INF] Connection 23 created.
+ [INF] Module "kea-dhcp-types" was installed.
+ [INF] File "kea-dhcp-types@2022-11-30.yang" was installed.
+ [INF] No datastore changes to apply.
+ [INF] Connection 25 created.
+ [INF] Module "kea-dhcp-ddns" was installed.
+ [INF] File "kea-dhcp-ddns@2022-07-27.yang" was installed.
+ [INF] No datastore changes to apply.
+ [INF] Connection 27 created.
+ [INF] Module "kea-ctrl-agent" was installed.
+ [INF] File "kea-ctrl-agent@2019-08-12.yang" was installed.
+ [INF] No datastore changes to apply.
+ [INF] Connection 29 created.
+ [INF] Module "kea-dhcp4-server" was installed.
+ [INF] File "kea-dhcp4-server@2022-11-30.yang" was installed.
+ [INF] No datastore changes to apply.
+ [INF] Connection 31 created.
+ [INF] Module "kea-dhcp6-server" was installed.
+ [INF] File "kea-dhcp6-server@2022-11-30.yang" was installed.
+ [INF] No datastore changes to apply.
+
+To confirm whether the modules have been imported correctly, check the list of
+currently installed YANG modules. It should be similar to this:
+
+.. code-block:: console
+
+ $ sysrepoctl -l
+ Sysrepo repository: /etc/sysrepo
+
+ Module Name | Revision | Flags | Owner | Startup Perms | Submodules | Features
+ ---------------------------------------------------------------------------------------------------
+ ietf-datastores | 2018-02-14 | I | user:user | 444 | |
+ ietf-dhcpv6-client | 2018-09-04 | I | user:user | 600 | |
+ ietf-dhcpv6-options | 2018-09-04 | I | user:user | 600 | |
+ ietf-dhcpv6-relay | 2018-09-04 | I | user:user | 600 | |
+ ietf-dhcpv6-server | 2018-09-04 | I | user:user | 600 | |
+ ietf-dhcpv6-types | 2018-09-04 | I | user:user | 600 | |
+ ietf-inet-types | 2013-07-15 | I | user:user | 444 | |
+ ietf-interfaces | 2018-02-20 | I | user:user | 600 | |
+ ietf-netconf | 2013-09-29 | I | user:user | 644 | |
+ ietf-netconf-acm | 2018-02-14 | I | user:user | 600 | |
+ ietf-netconf-notifications | 2012-02-06 | I | user:user | 644 | |
+ ietf-netconf-with-defaults | 2011-06-01 | I | user:user | 444 | |
+ ietf-origin | 2018-02-14 | I | user:user | 444 | |
+ ietf-yang-library | 2019-01-04 | I | user:user | 644 | |
+ ietf-yang-metadata | 2016-08-05 | i | | | |
+ ietf-yang-schema-mount | 2019-01-14 | I | user:user | 644 | |
+ ietf-yang-types | 2013-07-15 | I | user:user | 444 | |
+ kea-ctrl-agent | 2019-08-12 | I | user:user | 600 | |
+ kea-dhcp-ddns | 2022-07-27 | I | user:user | 600 | |
+ kea-dhcp-types | 2022-11-30 | I | user:user | 600 | |
+ kea-dhcp4-server | 2022-11-30 | I | user:user | 600 | |
+ kea-dhcp6-server | 2022-11-30 | I | user:user | 600 | |
+ kea-types | 2019-08-12 | I | user:user | 600 | |
+ keatest-module | 2022-11-30 | I | user:user | 600 | |
+ sysrepo-monitoring | 2022-04-08 | I | user:user | 600 | |
+ sysrepo-plugind | 2022-03-10 | I | user:user | 644 | |
+ yang | 2022-06-16 | I | user:user | 444 | |
+
+ Flags meaning: I - Installed/i - Imported; R - Replay support
+
+To reinstall a module, if the revision YANG entry was bumped, simply installing
+it will update it automatically. Otherwise, it must first be uninstalled:
+
+.. code-block:: console
+
+ $ sysrepoctl -u kea-dhcp4-server
+
+If the module is used (i.e. imported) by other modules, it can be uninstalled
+only after the dependent modules have first been uninstalled.
+Installation and uninstallation must be done in dependency order and
+reverse-dependency order, as appropriate.
+
+.. _netconf-models:
+
+Supported YANG Models
+---------------------
+
+The currently supported models are ``kea-dhcp4-server`` and
+``kea-dhcp6-server``. There is partial support for
+``ietf-dhcpv6-server``, but the primary focus of testing has been on Kea DHCP
+servers. Other models (:iscman:`kea-dhcp-ddns` and :iscman:`kea-ctrl-agent`)
+are currently not supported.
+
+.. _using-netconf:
+
+Using the NETCONF Agent
+-----------------------
+
+The NETCONF agent follows this algorithm:
+
+- For each managed server, get the initial configuration from the
+ server through the control socket.
+
+- Open a connection with the Sysrepo environment and establish two
+ sessions with the startup and running datastores.
+
+- Check that the used (not-essential) and required (essential) modules are
+ installed in the Sysrepo repository at the right revision. If an
+ essential module - that is, a module where the configuration schema for a
+ managed server is defined - is not installed, raise a fatal error.
+
+- For each managed server, get the YANG configuration from the startup
+ datastore, translate it to JSON, and load it onto the server being
+ configured.
+
+- For each managed server, subscribe a module change callback using its
+ model name.
+
+- When a running configuration is changed, try to validate or load the
+ updated configuration via the callback to the managed server.
+
+.. _netconf-configuration:
+
+Configuration
+-------------
+
+The behavior described in :ref:`using-netconf`
+is controlled by several configuration flags, which can be set in the
+global scope or in a specific managed-server scope. If the latter,
+the value defined in the managed-server scope takes precedence. These
+flags are:
+
+- ``boot-update`` - controls the initial configuration phase; when
+ ``true`` (the default), the initial configuration retrieved from the
+ classic Kea server JSON configuration file is loaded first, and then
+ the startup YANG model is loaded. This setting lets administrators
+ define a control socket in the local JSON file and then download the
+ configuration from YANG. When set to ``false``, this phase is skipped.
+
+- ``subscribe-changes`` - controls the module change
+ subscription; when ``true`` (the default), a module change callback is
+ subscribed, but when ``false`` the phase is skipped and running
+ configuration updates are disabled. When set to ``true``, the running
+ datastore is used to subscribe for changes.
+
+- ``validate-changes`` - controls how Kea monitors changes in
+ the Sysrepo configuration. Sysrepo offers two stages where Kea can
+ interact: validation and application. At the validation (or
+ ``SR_EV_CHANGE`` event, in the Sysrepo naming convention) stage, Kea
+ retrieves the newly committed configuration and verifies it. If the
+ configuration is incorrect for any reason, the Kea servers reject it
+ and the error is propagated back to the Sysrepo, which then returns
+ an error. This step only takes place if ``validate-changes`` is set to
+ ``true``. In the application (or ``SR_EV_UPDATE`` event in the Sysrepo naming
+ convention) stage, the actual configuration is applied. At this stage
+ Kea can receive the configuration, but it is too late to signal back
+ any errors as the configuration has already been committed.
+
+The idea behind the initial configuration phase is to boot Kea servers
+with a minimal configuration which includes only a control socket,
+making them manageable. For instance, for the DHCPv4 server:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "control-socket": {
+ "socket-name": "/tmp/kea-dhcp4-ctrl.sock",
+ "socket-type": "unix"
+ }
+ }
+ }
+
+With module change subscriptions enabled, the :iscman:`kea-netconf` daemon
+monitors any configuration changes as they appear in the Sysrepo. Such
+changes can be done using the ``sysrepocfg`` tool or remotely using any
+NETCONF client. For details, please see the Sysrepo documentation or
+:ref:`operation-example`.
+Those tools can be used to modify YANG configurations in the running
+datastore. Note that committed configurations are only updated in the
+running datastore; to keep them between server reboots they must be
+copied to the startup datastore.
+
+When module changes are tracked (using ``subscribe-changes`` set to
+``true``) and the running configuration has changed (e.g. using
+``sysrepocfg`` or any NETCONF client), the callback validates the
+modified configuration (if ``validate-changes`` was not set to ``false``)
+and runs a second time to apply the new configuration. If the validation
+fails, the callback is still called again but with an ``SR_EV_ABORT``
+(vs. ``SR_EV_DONE``) event with rollback changes.
+
+The returned code of the callback on an ``SR_EV_DONE`` event is ignored, as it is
+too late to refuse a bad configuration.
+
+There are four ways in which a modified YANG configuration might
+be incorrect:
+
+1. It could be non-compliant with the schema, e.g. an unknown entry, missing a
+ mandatory entry, a value with a bad type, or not matching a constraint.
+
+2. It could fail to be translated from YANG to JSON, e.g. an invalid user
+ context.
+
+3. It could fail Kea server sanity checks, e.g. an out-of-subnet-pool range
+ or an unsupported database type.
+
+4. The syntax may be correct and pass server sanity checks but the
+ configuration could fail to run, e.g. the configuration specifies database
+ credentials but the database refuses the connection.
+
+The first case is handled by Sysrepo. The second and third cases are
+handled by :iscman:`kea-netconf` in the validation phase (if not disabled by
+setting ``validate-changes`` to ``true``). The last case causes the
+application phase to fail without a sensible report to Sysrepo.
+
+The managed Kea servers and agents are described in the
+``managed-servers`` section. Each sub-section begins with the service
+name: ``dhcp4``, ``dhcp6``, ``d2`` (the DHCP-DDNS server does not
+support the control-channel feature yet), and ``ca`` (the control
+agent).
+
+Each managed server entry may contain:
+
+- control flags - ``boot-update``, ``subscribe-changes``, and/or ``validate-changes``.
+
+- ``model`` - specifies the YANG model/module name. For each service,
+ the default is the corresponding Kea YANG model, e.g. for ``"dhcp4"``
+ it is ``"kea-dhcp4-server"``.
+
+- ``control-socket`` - specifies the control socket for managing the
+ service configuration.
+
+A control socket is specified by:
+
+- ``socket-type`` - the socket type is either ``stdout``, ``unix``, or ``http``.
+ ``stdout`` is the default;
+ it is not really a socket, but it allows :iscman:`kea-netconf` to run in
+ debugging mode where everything is printed on stdout, and it can also be
+ used to redirect commands easily. ``unix`` is the standard direct
+ server control channel, which uses UNIX sockets; ``http`` uses
+ a control agent, which accepts HTTP connections.
+
+- ``socket-name`` - the local socket name for the ``unix`` socket type
+ (default empty string).
+
+- ``socket-url`` - the HTTP URL for the ``http`` socket type (default
+ ``http://127.0.0.1:8000/``).
+
+User contexts can store arbitrary data as long as they are in valid JSON
+syntax and their top-level element is a map (i.e. the data must be
+enclosed in curly brackets). They are accepted at the NETCONF entry,
+i.e. below the top-level, managed-service entry, and control-socket
+entry scopes.
+
+Hook libraries can be loaded by the NETCONF agent just as with other
+servers or agents; however, currently no hook points are defined. The
+``hooks-libraries`` list contains the list of hook libraries that
+should be loaded by :iscman:`kea-netconf`, along with their configuration
+information specified with ``parameters``.
+
+Please consult :ref:`logging` for details on how to configure
+logging. The name of the NETCONF agent's main logger is :iscman:`kea-netconf`, as
+given in the example above.
+
+.. _netconf-example:
+
+A :iscman:`kea-netconf` Configuration Example
+---------------------------------------------
+
+The following example demonstrates the basic NETCONF configuration. More
+examples are available in the ``doc/examples/netconf`` directory in the
+Kea sources.
+
+.. code-block:: javascript
+
+ // This is a simple example of a configuration for the NETCONF agent.
+ // This server provides a YANG interface for all Kea servers and the agent.
+ {
+ "Netconf":
+ {
+ // Control flags can be defined in the global scope or
+ // in a managed server scope. Precedences are:
+ // - use the default value (true)
+ // - use the global value
+ // - use the local value.
+ // So this overwrites the default value:
+ "boot-update": false,
+
+ // This map specifies how each server is managed. For each server there
+ // is a name of the YANG model to be used and the control channel.
+ // Currently three control channel types are supported:
+ // "stdout" which outputs the configuration on the standard output,
+ // "unix" which uses the local control channel supported by the
+ // "dhcp4" and "dhcp6" servers ("d2" support is not yet available),
+ // and "http" which uses the Control Agent "ca" to manage itself or
+ // to forward commands to "dhcp4" or "dhcp6".
+ "managed-servers":
+ {
+ // This is how kea-netconf can communicate with the DHCPv4 server.
+ "dhcp4":
+ {
+ "comment": "DHCPv4 server",
+ "model": "kea-dhcp4-server",
+ "control-socket":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea4-ctrl-socket"
+ }
+ },
+
+ // DHCPv6 parameters.
+ "dhcp6":
+ {
+ "model": "kea-dhcp6-server",
+ "control-socket":
+ {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea6-ctrl-socket"
+ }
+ },
+
+ // Currently the DHCP-DDNS (nicknamed D2) server does not support
+ // a command channel.
+ "d2":
+ {
+ "model": "kea-dhcp-ddns",
+ "control-socket":
+ {
+ "socket-type": "stdout",
+ "user-context": { "in-use": false }
+ }
+ },
+
+ // Of course the Control Agent (CA) supports HTTP.
+ "ca":
+ {
+ "model": "kea-ctrl-agent",
+ "control-socket":
+ {
+ "socket-type": "http",
+ "socket-url": "http://127.0.0.1:8000/"
+ }
+ }
+ },
+
+ // kea-netconf is able to load hook libraries that augment its operation.
+ // Currently there are no hook points defined in kea-netconf
+ // processing.
+ "hooks-libraries": [
+ // The hook libraries list may contain more than one library.
+ {
+ // The only necessary parameter is the library filename.
+ "library": "/opt/local/custom_hooks_example.so",
+
+ // Some libraries may support parameters. Make sure you
+ // type this section carefully, as kea-netconf does not
+ // validate it (because the format is library-specific).
+ "parameters": {
+ "param1": "foo"
+ }
+ }
+ ],
+
+ // Similar to other Kea components, NETCONF also uses logging.
+ "loggers": [
+ {
+ "name": "kea-netconf",
+ "output-options": [
+ {
+ "output": "/var/log/kea-netconf.log",
+ // Several additional parameters are possible in
+ // addition to the typical output.
+ // Flush determines whether logger flushes output
+ // to a file.
+ // Maxsize determines maximum filesize before
+ // the file is rotated.
+ // Maxver specifies the maximum number of
+ // rotated files to be kept.
+ "flush": true,
+ "maxsize": 204800,
+ "maxver": 4
+ }
+ ],
+ "severity": "INFO",
+ "debuglevel": 0
+ }
+ ]
+ }
+ }
+
+.. _netconf-start-stop:
+
+Starting and Stopping the NETCONF Agent
+---------------------------------------
+
+:iscman:`kea-netconf` accepts the following command-line switches:
+
+- ``-c file`` - specifies the configuration file.
+
+- ``-d`` - specifies whether the agent logging should be switched to
+ debug/verbose mode. In verbose mode, the logging severity and
+ debuglevel specified in the configuration file are ignored and
+ "debug" severity and the maximum debuglevel (99) are assumed. The
+ flag is convenient for temporarily switching the server into maximum
+ verbosity, e.g. when debugging.
+
+- ``-t file`` - specifies the configuration file to be tested.
+ :iscman:`kea-netconf` attempts to load it and conducts sanity checks;
+ certain checks are possible only while running the actual server. The
+ actual status is reported with exit code (0 = configuration appears valid,
+ 1 = error encountered). Kea prints out log messages to standard
+ output and error to standard error when testing the configuration.
+
+- ``-v`` - displays the version of :iscman:`kea-netconf` and exits.
+
+- ``-V`` - displays the extended version information for :iscman:`kea-netconf`
+ and exits. The listing includes the versions of the libraries
+ dynamically linked to Kea.
+
+- ``-W`` - displays the Kea configuration report and exits. The report
+ is a copy of the ``config.report`` file produced by ``./configure``;
+ it is embedded in the executable binary.
+
+ The contents of the ``config.report`` file may also be accessed by examining
+ certain libraries in the installation tree or in the source tree.
+
+ .. code-block:: shell
+
+ # from installation using libkea-process.so
+ $ strings ${prefix}/lib/libkea-process.so | sed -n 's/;;;; //p'
+
+ # from sources using libkea-process.so
+ $ strings src/lib/process/.libs/libkea-process.so | sed -n 's/;;;; //p'
+
+ # from sources using libkea-process.a
+ $ strings src/lib/process/.libs/libkea-process.a | sed -n 's/;;;; //p'
+
+ # from sources using libcfgrpt.a
+ $ strings src/lib/process/cfgrpt/.libs/libcfgrpt.a | sed -n 's/;;;; //p'
+
+.. _operation-example:
+
+A Step-by-Step NETCONF Agent Operation Example
+----------------------------------------------
+
+.. note::
+
+ Copies of example configurations presented within this section can be
+ found in the Kea source code, under
+ ``doc/examples/netconf/kea-dhcp6-operations``.
+
+.. _operation-example-setup:
+
+Setup of NETCONF Agent Operation Example
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The test box has an Ethernet interface named eth1. On some systems it is
+possible to rename interfaces; for instance, on Linux with an ens38
+interface:
+
+.. code-block:: console
+
+ # ip link set down dev ens38
+ # ip link set name eth1 dev ens38
+ # ip link set up dev eth1
+
+The interface must have an address in the test prefix:
+
+.. code-block:: console
+
+ # ip -6 addr add 2001:db8::1/64 dev eth1
+
+The Kea DHCPv6 server must be launched with the configuration specifying
+a control socket used to receive control commands. The :iscman:`kea-netconf`
+process uses this socket to communicate with the DHCPv6 server, i.e. it
+pushes translated configurations to that server using control commands.
+The following is an example control socket specification for the Kea
+DHCPv6 server:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "control-socket": {
+ "socket-name": "/tmp/kea-dhcp6-ctrl.sock",
+ "socket-type": "unix"
+ }
+ }
+ }
+
+In order to launch the Kea DHCPv6 server using the configuration
+contained within the ``boot.json`` file, run:
+
+.. code-block:: console
+
+ # kea-dhcp6 -d -c boot.json
+
+The current configuration of the server can be fetched via a control
+socket by running:
+
+.. code-block:: console
+
+ # echo '{ "command": "config-get" }' | socat UNIX:/tmp/kea-dhcp6-ctrl.sock '-,ignoreeof'
+
+The following is the example ``netconf.json`` configuration for
+:iscman:`kea-netconf`, to manage the Kea DHCPv6 server:
+
+.. code-block:: json
+
+ {
+ "Netconf": {
+ "loggers": [
+ {
+ "debuglevel": 99,
+ "name": "kea-netconf",
+ "output-options": [
+ {
+ "output": "stderr"
+ }
+ ],
+ "severity": "DEBUG"
+ }
+ ],
+ "managed-servers": {
+ "dhcp6": {
+ "control-socket": {
+ "socket-name": "/tmp/kea-dhcp6-ctrl.sock",
+ "socket-type": "unix"
+ }
+ }
+ }
+ }
+ }
+
+Note that in production there should not be a need to log at the DEBUG level.
+
+The Kea NETCONF agent is launched by:
+
+.. code-block:: console
+
+ # kea-netconf -d -c netconf.json
+
+Now that both :iscman:`kea-netconf` and :iscman:`kea-dhcp6` are running, it is
+possible to populate updates to the configuration to the DHCPv6 server.
+The following is the configuration extracted from ``startup.xml``:
+
+.. code-block:: xml
+
+ <config xmlns="urn:ietf:params:xml:ns:yang:kea-dhcp6-server">
+ <subnet6>
+ <id>1</id>
+ <pool>
+ <start-address>2001:db8::1:0</start-address>
+ <end-address>2001:db8::1:ffff</end-address>
+ <prefix>2001:db8::1:0/112</prefix>
+ </pool>
+ <subnet>2001:db8::/64</subnet>
+ </subnet6>
+ <interfaces-config>
+ <interfaces>eth1</interfaces>
+ </interfaces-config>
+ <control-socket>
+ <socket-name>/tmp/kea-dhcp6-ctrl.sock</socket-name>
+ <socket-type>unix</socket-type>
+ </control-socket>
+ </config>
+
+To populate this new configuration:
+
+.. code-block:: console
+
+ $ sysrepocfg -d startup -f xml -m kea-dhcp6-server --import=startup.xml
+
+:iscman:`kea-netconf` pushes the configuration found in the Sysrepo startup
+datastore to all Kea servers during its initialization phase, after it
+subscribes to module changes in the Sysrepo running datastore. This
+action copies the configuration from the startup datastore to the
+running datastore and enables the running datastore, making it
+available.
+
+Changes to the running datastore are applied after validation to the Kea
+servers. Note that they are not by default copied back to the startup
+datastore, i.e. changes are not permanent.
+
+.. note::
+
+ :iscman:`kea-netconf` fetches the entire configuration from any Sysrepo datastore in a
+ single ``get-config`` NETCONF operation. Prior to Kea 2.3.2, a ``get-config`` operation
+ was done for each leaf and leaf-list node. Because of the significant changes,
+ :iscman:`kea-netconf` is considered experimental.
+
+.. _operation-example-errors:
+
+Example of Error Handling in NETCONF Operation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are four classes of issues with configurations applied via
+NETCONF:
+
+1. The configuration does not comply with the YANG schema.
+
+2. The configuration cannot be translated from YANG to the Kea JSON.
+
+3. The configuration is rejected by the Kea server.
+
+4. The configuration was validated by the Kea server but cannot be
+ applied.
+
+In the first case, consider the following ``BAD-schema.xml``
+configuration file:
+
+.. code-block:: xml
+
+ <config xmlns="urn:ietf:params:xml:ns:yang:kea-dhcp6-server">
+ <subnet4>
+ <id>1</id>
+ <pool>
+ <start-address>2001:db8::1:0</start-address>
+ <end-address>2001:db8::1:ffff</end-address>
+ <prefix>2001:db8::1:0/112</prefix>
+ </pool>
+ <subnet>2001:db8::/64</subnet>
+ </subnet6>
+ <interfaces-config>
+ <interfaces>eth1</interfaces>
+ </interfaces-config>
+ <control-socket>
+ <socket-name>/tmp/kea-dhcp6-ctrl.sock</socket-name>
+ <socket-type>unix</socket-type>
+ </control-socket>
+ </config>
+
+It is directly rejected by ``sysrepocfg``:
+
+.. code-block:: console
+
+ $ sysrepocfg -d running -f xml -m kea-dhcp6-server --import=BAD-schema.xml
+
+In the second case, the configuration is rejected by :iscman:`kea-netconf`.
+For example, consider this ``BAD-translator.xml`` file:
+
+.. code-block:: xml
+
+ <config xmlns="urn:ietf:params:xml:ns:yang:kea-dhcp6-server">
+ <subnet6>
+ <id>1</id>
+ <pool>
+ <start-address>2001:db8::1:0</start-address>
+ <end-address>2001:db8::1:ffff</end-address>
+ <prefix>2001:db8::1:0/112</prefix>
+ </pool>
+ <subnet>2001:db8::/64</subnet>
+ </subnet6>
+ <interfaces-config>
+ <interfaces>eth1</interfaces>
+ </interfaces-config>
+ <control-socket>
+ <socket-name>/tmp/kea-dhcp6-ctrl.sock</socket-name>
+ <socket-type>unix</socket-type>
+ </control-socket>
+ <user-context>bad</user-context>
+ </config>
+
+In the third case, the configuration is presented to the Kea DHCPv6
+server and fails to validate, as in this ``BAD-config.xml`` file:
+
+.. code-block:: xml
+
+ <config xmlns="urn:ietf:params:xml:ns:yang:kea-dhcp6-server">
+ <subnet6>
+ <id>1</id>
+ <pool>
+ <start-address>2001:db8:1::0</start-address>
+ <end-address>2001:db8:1::ffff</end-address>
+ <prefix>2001:db8:1::0/112</prefix>
+ </pool>
+ <subnet>2001:db8::/64</subnet>
+ </subnet6>
+ <interfaces-config>
+ <interfaces>eth1</interfaces>
+ </interfaces-config>
+ <control-socket>
+ <socket-name>/tmp/kea-dhcp6-ctrl.sock</socket-name>
+ <socket-type>unix</socket-type>
+ </control-socket>
+ </config>
+
+In the last case, the misconfiguration is detected too late and the
+change must be reverted in Sysrepo, e.g. using the startup datastore as
+a backup.
+
+.. _operation-example-2pools:
+
+NETCONF Operation Example with Two Pools
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This example adds a second pool to the initial (i.e. startup)
+configuration in the ``twopools.xml`` file:
+
+.. code-block:: xml
+
+ <config xmlns="urn:ietf:params:xml:ns:yang:kea-dhcp6-server">
+ <subnet6>
+ <id>1</id>
+ <pool>
+ <start-address>2001:db8::1:0</start-address>
+ <end-address>2001:db8::1:ffff</end-address>
+ <prefix>2001:db8::1:0/112</prefix>
+ </pool>
+ <pool>
+ <start-address>2001:db8::2:0</start-address>
+ <end-address>2001:db8::2:ffff</end-address>
+ <prefix>2001:db8::2:0/112</prefix>
+ </pool>
+ <subnet>2001:db8::/64</subnet>
+ </subnet6>
+ <interfaces-config>
+ <interfaces>eth1</interfaces>
+ </interfaces-config>
+ <control-socket>
+ <socket-name>/tmp/kea-dhcp6-ctrl.sock</socket-name>
+ <socket-type>unix</socket-type>
+ </control-socket>
+ </config>
+
+This configuration is installed by:
+
+.. code-block:: console
+
+ $ sysrepocfg -d running -f xml -m kea-dhcp6-server --import=twopools.xml
+
+.. _operation-example-2subnets:
+
+NETCONF Operation Example with Two Subnets
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This example specifies two subnets in the ``twosubnets.xml`` file:
+
+.. code-block:: xml
+
+ <config xmlns="urn:ietf:params:xml:ns:yang:kea-dhcp6-server">
+ <subnet6>
+ <id>1</id>
+ <pool>
+ <start-address>2001:db8:1::</start-address>
+ <end-address>2001:db8:1::ffff</end-address>
+ <prefix>2001:db8:1::/112</prefix>
+ </pool>
+ <subnet>2001:db8:1::/64</subnet>
+ </subnet6>
+ <subnet6>
+ <id>2</id>
+ <pool>
+ <start-address>2001:db8:2::</start-address>
+ <end-address>2001:db8:2::ffff</end-address>
+ <prefix>2001:db8:2::/112</prefix>
+ </pool>
+ <subnet>2001:db8:2::/64</subnet>
+ </subnet6>
+ <interfaces-config>
+ <interfaces>eth1</interfaces>
+ </interfaces-config>
+ <control-socket>
+ <socket-name>/tmp/kea-dhcp6-ctrl.sock</socket-name>
+ <socket-type>unix</socket-type>
+ </control-socket>
+ </config>
+
+This configuration is installed by:
+
+.. code-block:: console
+
+ $ sysrepocfg -d running -f xml -m kea-dhcp6-server --import=twosubnets.xml
+
+.. _operation-example-logging:
+
+NETCONF Operation Example With Logging
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This example adds a logger entry to the initial (i.e. startup)
+configuration in the ``logging.xml`` file:
+
+.. code-block:: xml
+
+ <config xmlns="urn:ietf:params:xml:ns:yang:kea-dhcp6-server">
+ <interfaces-config>
+ <interfaces>eth1</interfaces>
+ </interfaces-config>
+ <subnet6>
+ <id>1</id>
+ <pool>
+ <start-address>2001:db8::1:0</start-address>
+ <end-address>2001:db8::1:ffff</end-address>
+ <prefix>2001:db8::1:0/112</prefix>
+ </pool>
+ <subnet>2001:db8::/64</subnet>
+ </subnet6>
+ <control-socket>
+ <socket-name>/tmp/kea-dhcp6-ctrl.sock</socket-name>
+ <socket-type>unix</socket-type>
+ </control-socket>
+ <logger>
+ <name>kea-dhcp6</name>
+ <output-option>
+ <output>stderr</output>
+ </output-option>
+ <debuglevel>99</debuglevel>
+ <severity>DEBUG</severity>
+ </logger>
+ </config>
+
+The corresponding Kea configuration in JSON is:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "control-socket": {
+ "socket-name": "/tmp/kea-dhcp6-ctrl.sock",
+ "socket-type": "unix"
+ },
+ "interfaces-config": {
+ "interfaces": [ "eth1" ]
+ },
+ "subnet6": [
+ {
+ "id": 1,
+ "pools": [
+ {
+ "pool": "2001:db8::1:0/112"
+ }
+ ],
+ "subnet": "2001:db8::/64"
+ }
+ ],
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "stderr"
+ }
+ ],
+ "severity": "DEBUG",
+ "debuglevel": 99
+ }
+ ]
+ }
+ }
+
+Finally, any of the previous examples can be replayed by using
+``sysrepocfg`` in edit mode as follows:
+
+.. code-block:: console
+
+ $ sysrepocfg -d running -f xml -m kea-dhcp6-server --edit
+
+or by using a NETCONF client like ``netopeer2-cli`` from the
+`Netopeer2 <https://github.com/CESNET/Netopeer2>`__ NETCONF Toolset.
+
+.. _migrating-yang-data:
+
+Migrating YANG Data From a Prior Sysrepo Version
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Shut down :iscman:`kea-netconf`. This ensures that backups for both datastores
+are done at the same configuration state and that no change happens between exporting them.
+
+2. Make data backups for all YANG modules, with one XML for each datastore.
+
+.. code-block:: console
+
+ $ sysrepocfg --datastore running --export=save.xml --format=xml
+ $ sysrepocfg --datastore startup --export=save.xml --format=xml
+
+.. note::
+
+ Sysrepo v0 does not support import/export of all YANG modules; this capability was added in
+ Sysrepo v1. Users that are migrating from Sysrepo v0 will need to do per-module backups. This has
+ the added benefit of isolating potential failures and preventing them from affecting all
+ modules. The command is the same, except it has the module name added to it at the end.
+
+ .. code-block:: console
+
+ $ sysrepocfg --datastore running --export=save.xml --format=xml kea-dhcp6-server
+ $ sysrepocfg --datastore startup --export=save.xml --format=xml kea-dhcp6-server
+
+3. Upgrade Sysrepo to the newer version and then run:
+
+.. code-block:: console
+
+ $ sysrepocfg --datastore running --import=save.xml
+ $ sysrepocfg --datastore startup --import=save.xml
diff --git a/doc/sphinx/arm/ext-radius.rst b/doc/sphinx/arm/ext-radius.rst
new file mode 100644
index 0000000..08ca631
--- /dev/null
+++ b/doc/sphinx/arm/ext-radius.rst
@@ -0,0 +1,559 @@
+.. _radius:
+
+RADIUS
+======
+
+.. _radius-overview:
+
+RADIUS Overview
+---------------
+
+This hook library allows Kea to interact with two types of RADIUS
+services: access and accounting. Although the most common DHCP and RADIUS
+integration is done on the DHCP relay-agent level (DHCP clients send
+DHCP packets to DHCP relays; those relays contact the RADIUS server and
+depending on the response either send the packet to the DHCP server or
+drop it), it does require DHCP relay hardware to support RADIUS
+communication. Also, even if the relay has the necessary support, it is
+often not flexible enough to send and receive additional RADIUS
+attributes. As such, the alternative looks more appealing: to extend the
+DHCP server to talk to RADIUS directly. That is the goal of this library.
+
+.. note::
+
+ This library can only be loaded by the :iscman:`kea-dhcp4` or the
+ :iscman:`kea-dhcp6` process.
+
+The major feature of this hook library is the ability to use RADIUS
+authorization. When a DHCP packet is received, the Kea server sends an
+Access-Request to the RADIUS server and waits for a response. The server
+then sends back either an Access-Accept with specific client attributes,
+or an Access-Reject. There are two cases supported here: first, the
+Access-Accept includes a Framed-IP-Address attribute (for DHCPv4) or a
+Framed-IPv6-Address attribute (for DHCPv6), which are interpreted by Kea as
+instructions to assign the specified IPv4 or IPv6 address. This
+effectively means RADIUS can act as an address-reservation database.
+
+The second supported case is the ability to assign clients to specific
+pools based on a RADIUS response. In this case, the RADIUS server sends
+back an Access-Accept with a Framed-Pool attribute.
+For both DHCPv4 and DHCPv6, Kea interprets this attribute as a client class.
+With the addition of the ability to limit access to pools to
+specific classes (see :ref:`classification-pools`), RADIUS can be
+used to force the client to be assigned a dynamic address from a
+specific pool. Furthermore, the same mechanism can be used to control
+what kind of options the client gets if there are DHCP options
+specified for a particular class.
+
+.. _radius-config:
+
+RADIUS Hook Library Configuration
+---------------------------------
+
+The RADIUS hook is a library that must be loaded by either :iscman:`kea-dhcp4` or
+:iscman:`kea-dhcp6` servers. Unlike some other available hook libraries, this one
+takes many parameters. For example, this configuration can be used:
+
+.. parsed-literal::
+
+ {
+ "Dhcp4": {
+
+ // Your regular DHCPv4 configuration parameters goes here.
+
+ "hooks-libraries": [
+ {
+ // Note that RADIUS requires host-cache for proper operation,
+ // so that library is loaded as well.
+ "library": "/usr/local/lib/kea/hooks/libdhcp_host_cache.so"
+ },
+ {
+ "library": "/usr/local/lib/kea/hooks/libdhcp_radius.so",
+ "parameters": {
+
+ // Specify where the dictionary is located.
+ "dictionary": "/etc/kea/radius/dictionary",
+
+ // Specify which address to use to communicate with RADIUS servers
+ "bindaddr": "*"
+
+ // More RADIUS parameters go here.
+ }
+ }
+ ]
+ }
+ }
+
+RADIUS is a complicated environment. As such, it is not feasible
+to provide a default configuration that works for everyone.
+However, we do have an example that showcases some of the more common
+features. Please see ``doc/examples/kea4/hooks-radius.json`` in the Kea
+sources.
+
+The RADIUS hook library supports the following global configuration
+flags:
+
+- ``bindaddr`` (default ``"*"``) - specifies the address to be used by the
+ hook library in communication with RADIUS servers. The ``"*"`` special
+ value tells the kernel to choose the address at hook library load time.
+
+- ``canonical-mac-address`` (default ``false``) - specifies whether MAC
+ addresses in attributes follow the canonical RADIUS format (lowercase
+ pairs of hexadecimal digits separated by ``-``).
+
+- ``client-id-pop0`` (default ``false``) - is used with
+ :ischooklib:`libdhcp_flex_id.so`. Removes the leading zero (or pair of zeroes
+ in DHCPv6) type in the client id (duid in DHCPv6). See
+ ``client-id-printable`` for any value implications when used in conjunction
+ with it.
+
+- ``client-id-printable`` (default ``false``) - checks whether the
+ ``client-id``/``duid`` content is printable and uses it as is instead of in
+ hexadecimal. Implies ``client-id-pop0`` and ``extract-duid`` as 0 and 255 are
+ not printable.
+
+- ``deadtime`` (default ``0``) - is a mechanism that helps in sorting the
+ servers such that the servers that have proved responsive so far are inquired
+ first, and the servers that have proved unresponsive are left at the end. The
+ deadtime value specifies the number of seconds after which a server is
+ considered unresponsive. 0 disables the mechanism.
+
+- ``dictionary`` (default ``"/etc/kea/radius/dictionary"``) - is the
+ attribute and value dictionary. Note that it is a critical parameter.
+ A dictionary is provided by Kea and is set by default.
+
+- ``extract-duid`` (default ``true``) - extracts the embedded duid from an
+ RFC-4361-compliant DHCPv4 client id. See ``client-id-printable`` for any
+ value implications when used in conjunction with it.
+
+- ``identifier-type4`` (default ``"client-id"``) - specifies the identifier
+ type to build the User-Name attribute. It should be the same as the
+ host identifier. When :ischooklib:`libdhcp_flex_id.so` is used, then
+ ``replace-client-id`` must be set to ``true`` and ``client-id`` must be used
+ with ``client-id-pop0`` enabled.
+
+- ``identifier-type6`` (default ``"duid"``) - specifies the identifier type to
+ build the User-Name attribute. It should be the same as the host
+ identifier. When :ischooklib:`libdhcp_flex_id.so` is used, then
+ ``replace-client-id`` must be set to ``true`` and ``duid`` must be used with
+ ``client-id-pop0`` enabled.
+
+- ``nas-ports`` (default ``[]``), specifies the NAS port to use in place of
+ a subnet ID (default behavior). It is an array of maps, each map having two
+ elements at most: the mandatory NAS port value, and, optionally, a selector
+ consisting of either a subnet id, a subnet prefix, or a shared-network name.
+ If the selector is applied to the packet, the NAS port is used instead of the
+ subnet id. When the subnet id is 0 or missing, the specified NAS port acts as
+ a default. Its substition happens for all packets that did not match a
+ selector.
+
+- ``realm`` (default ``""``) - is the default realm.
+
+- ``reselect-subnet-address`` (default ``false``) - enables subnet reselection
+ according to the value of the Framed-IP-Address or, respectively,
+ the Framed-IPv6-Address attribute from the RADIUS access response. With this
+ flag enabled, if the IP address is not in range of the currently selected
+ subnet, but is in range of another subnet that is selectable with regards to
+ other criteria, the latter subnet is selected and used further in the lease
+ assignment process.
+
+- ``reselect-subnet-pool`` (default ``false``) - enables subnet reselection
+ according to the value of the Framed-Pool attribute from the RADIUS access
+ response. With this flag enabled, if the currently selected subnet is not
+ guarded by the client class represented by the attribute value, but there is
+ another selectable subnet that is guarded by it, the latter subnet is
+ selected and used further in the lease assignment process.
+ This reselection is attempted first, and if successful, it prevents the
+ function of reselect-subnet-address from coming into effect.
+
+- ``retries`` (default ``3``) - is the number of retries before trying the
+ next server.
+
+- ``session-history`` (default ``""``) - is the name of the file providing
+ persistent storage for accounting session history.
+
+ - ``thread-pool-size`` (default ``0``) indicates the number of threads that
+ is used for sending RADIUS requests and processing RADIUS responses for both
+ access and accounting services before passing it to the core thread pool. A
+ value of ``0`` instructs the RADIUS hook library to use the same number of
+ threads used for core DHCP processing. This value is only relevant if Kea
+ core is configured as multi-threaded. Single-threaded Kea core results in
+ single-threaded RADIUS processing.
+
+- ``timeout`` (default ``10``) - is the number of seconds during which a
+ response is awaited.
+
+Two services are supported:
+
+- ``access`` - the authorization service.
+
+- ``accounting`` - the accounting service.
+
+At the service level, three sections can be configured:
+
+- Servers that define RADIUS services that the library is expected to
+ contact. Each server may have the following items specified:
+
+ - ``name`` - specifies the IP address of the server. A domain name may be
+ used and will be resolved at hook library load time.
+
+ - ``port`` - specifies the UDP port of the server. By default, it is 1812
+ for access and 1813 for accounting.
+
+ - ``secret`` - authenticates messages.
+
+ When no server is specified, the service is disabled.
+
+- Attributes which define additional information that the Kea server
+ sends to a RADIUS server. The parameter must be identified either
+ by a name or type. Its value can be specified in one of three
+ possible ways: ``data`` (which defines a plain text value), ``raw`` (which
+ defines the value in hex), or ``expr`` (which defines an expression
+ that is evaluated for each incoming packet independently).
+
+ - ``name`` - the name of the attribute.
+
+ - ``type`` - the type of the attribute. Either the type or the name must be
+ provided, and the attribute must be defined in the dictionary.
+
+ - ``data`` - the first of three ways to specify the attribute content.
+ It specifies the textual representation of the attribute content.
+
+ - ``raw`` - the second of three ways to specify the attribute content.
+ It specifies the content in hexadecimal.
+
+ - ``expr`` - the last of the three ways to specify the attribute content.
+ It specifies an evaluation expression on the DHCP query packet.
+ Currently this is restricted to the access service.
+
+ Attributes are supported only for the access service.
+
+- The ``peer-updates`` boolean flag (default ``true``) controls whether lease
+ updates coming from an active High-Availability (HA) partner should result in
+ an accounting request. This may be desirable to remove duplicates if HA
+ partners are configured to send request to the same RADIUS server. The flag is
+ only supported by the accounting service. The lease synchronization process at
+ the startup of an HA node does not trigger a RADIUS accounting request,
+ regardless of the value of this flag.
+
+- The ``max-pending-requests`` positive integer (default ``0``) limits the
+ number of pending RADIUS requests. The value ``0`` means no limit. It is
+ supported only by the access service. ``64`` can be a good value to set it to.
+
+For example, to specify a single access server available on localhost
+that uses ``"xyz123"`` as a secret, and tell Kea to send three additional
+attributes (``User-Password``, ``Connect-Info``, and ``Configuration-Token``),
+the following snippet could be used:
+
+.. parsed-literal::
+
+ {
+ "parameters": {
+
+ // Other RADIUS parameters here
+
+ "access": {
+
+ // This starts the list of access servers.
+ "servers": [
+ {
+ // These are parameters for the first (and only) access server
+ "name": "127.0.0.1",
+ "port": 1812,
+ "secret": "xyz123"
+ }
+ // Additional access servers could be specified here.
+ ],
+
+ // This defines a list of additional attributes Kea will send to each
+ // access server in Access-Request.
+ "attributes": [
+ {
+ // This attribute is identified by name (must be present in the
+ // dictionary) and has static value (i.e. the same value will be
+ // sent to every server for every packet).
+ "name": "User-Password",
+ "data": "mysecretpassword"
+ },
+ {
+ // It is also possible to specify an attribute using its type,
+ // rather than a name. 77 is Connect-Info. The value is specified
+ // using hex. Again, this is a static value. It will be sent the
+ // same for every packet and to every server.
+ "type": 77,
+ "raw": "65666a6a71"
+ },
+ {
+ // This example shows how an expression can be used to send dynamic value.
+ // The expression (see :ref:`classification-using-expressions`) may take any
+ // value from the incoming packet or even its metadata e.g. the
+ // interface it was received over from.
+ "name": "Configuration-Token",
+ "expr": "hexstring(pkt4.mac,':')"
+ }
+ ] // End of attributes
+ }, // End of access
+
+ // Accounting parameters.
+ "accounting": {
+ // This starts the list of accounting servers.
+ "servers": [
+ {
+ // These are parameters for the first (and only) accounting server
+ "name": "127.0.0.1",
+ "port": 1813,
+ "secret": "sekret"
+ }
+ // Additional accounting servers could be specified here.
+ ]
+ }
+ }
+ }
+
+Customization is sometimes required for certain attributes by devices belonging
+to various vendors. This is a great way to leverage the expression evaluation
+mechanism. For example, MAC addresses which might be used as a convenience
+value for the ``User-Password`` attribute are most likely to appear in colon-hexadecimal
+notation (``de:ad:be:ef:ca:fe``), but they might need to be expressed in
+hyphen-hexadecimal notation (``de-ad-be-ef-ca-fe``). Here's how to specify that:
+
+.. code-block:: json
+
+ {
+ "parameters": {
+ "access": {
+ "attributes": [
+ {
+ "name": "User-Password",
+ "expr": "hexstring(pkt4.mac, '-')"
+ }
+ ]
+ }
+ }
+ }
+
+And here's how to specify period-separated hexadecimal notation (``dead.beef.cafe``), preferred by Cisco devices:
+
+.. code-block:: json
+
+ {
+ "parameters": {
+ "access": {
+ "attributes": [
+ {
+ "name": "User-Password",
+ "expr": "substring(hexstring(pkt4.mac, ''), 0, 4) + '.' + substring(hexstring(pkt4.mac, ''), 4, 4) + '.' + substring(hexstring(pkt4.mac, ''), 8, 4)"
+ }
+ ]
+ }
+ }
+ }
+
+
+For :ischooklib:`libdhcp_radius.so` to operate properly in DHCPv4,
+:ischooklib:`libdhcp_host_cache.so` must also be loaded. The reason for this
+is somewhat complex. In a typical deployment, the DHCP clients send
+their packets via DHCP relay, which inserts certain Relay Agent
+Information options, such as ``circuit-id`` or ``remote-id``. The values of
+those options are then used by the Kea DHCP server to formulate the
+necessary attributes in the Access-Request message sent to the RADIUS
+server. However, once the DHCP client gets its address, it then renews
+by sending packets directly to the DHCP server. As a result, the relays
+are not able to insert their RAI options, and the DHCP server cannot send
+the Access-Request queries to the RADIUS server by using just the
+information from incoming packets. Kea needs to keep the information
+received during the initial Discover/Offer exchanges and use it again
+later when sending accounting messages.
+
+This mechanism is implemented based on user context in host
+reservations. (See :ref:`user-context` and :ref:`user-context-hooks` for
+details.) The host-cache mechanism allows the information retrieved by
+RADIUS to be stored and later used for sending access and accounting
+queries to the RADIUS server. In other words, the host-cache mechanism
+is mandatory, unless administrators do not want RADIUS communication for messages
+other than Discover and the first Request from each client.
+
+.. note::
+
+ Currently the RADIUS hook library is incompatible with the
+ ``early-global-reservations-lookup`` global parameter i.e.
+ setting the parameter to ``true`` raises an error when the
+ hook library is loaded.
+
+.. note::
+
+ Currently the RADIUS hook library is incompatible with the
+ multi-subnet shared networks that have host reservations other
+ than global. Loading the RADIUS hook library in a Kea DHCP server
+ that has this configuration raises an error.
+
+.. _radius-server-example:
+
+RADIUS Server Setup Example
+---------------------------
+
+The RADIUS hook library requires at least one RADIUS server to function. One
+popular open-source implementation is FreeRADIUS. This is how it can be
+set up to enable basic functionality in Kea.
+
+1. Install FreeRADIUS through the package manager or from the tarballs available
+ on [the freeradius.org download page](https://freeradius.org/releases/).
+
+2. Establish the FreeRADIUS configuration directory. It's commonly
+ ``/etc/freeradius``, but it may be ``/etc/raddb``.
+
+3. Generate certificates. Go to ``/etc/freeradius/certs``.
+ Run ``./bootstrap`` or ``make``.
+ Wait until finished. It should take a few seconds.
+
+4. Check that the server is able to start.
+ Running with the ``-X`` flag is a good way to display potential errors.
+ Run ``radiusd -X`` or ``freeradius -X``, whichever is available.
+ It should display ``Ready to process requests`` on the final line.
+
+5. If the Kea DHCP server and the RADIUS server are on different machines,
+ edit ``/etc/freeradius/clients.conf`` with the proper address under
+ ``ipadddr``. This file is also where the secret is set, which needs to match
+ the one set in the hook library's configuration.
+
+6. If RADIUS is used for the purpose of authorizing DHCP clients, each DHCP
+ client needs to have an entry in the authorize file, which can be commonly
+ found at:
+
+ - ``/etc/raddb/mods-config/files/authorize``
+ - ``/etc/freeradius/3.0/mods-config/files/authorize``
+ - ``/etc/freeradius/users`` (for RADIUS 2.x series)
+
+ Entries need to have the password set which needs to match the password
+ configured in the configuration of the RADIUS hook library under the
+ ``User-Password`` attribute. Each entry can have zero, one or multiple
+ attributes.
+
+ In the following example, there are 6 entries with the password set to the
+ client ID, which would need to be dynamically set in the hook library's
+ configuration. Here's how the entries can look like:
+
+ ::
+
+ 01:00:0c:01:02:03:04 Cleartext-password := "00:0c:01:02:03:04"
+
+ 01:00:0c:01:02:03:05 Cleartext-password := "00:0c:01:02:03:05"
+ Framed-IP-Address = "192.0.2.5"
+
+ 01:00:0c:01:02:03:06 Cleartext-password := "00:0c:01:02:03:06"
+ Framed-IP-Address = "192.0.2.6",
+ Framed-Pool = "classical"
+
+ 00:03:00:01:00:0c:01:02:03:07 Cleartext-password := "00:0c:01:02:03:07"
+
+ 00:03:00:01:00:0c:01:02:03:08 Cleartext-password := "00:0c:01:02:03:08"
+ Framed-IPv6-Address = "2001:db8::8"
+
+ 00:03:00:01:00:0c:01:02:03:09 Cleartext-password := "00:0c:01:02:03:09"
+ Framed-IPv6-Address = "2001:db8::9",
+ Framed-Pool = "classroom"
+
+7. Accounting should work out of the box with Kea, but customizations are
+ possible in the accounting file, which can be commonly found at:
+
+ - ``/etc/radius-config/mods-config/files/accounting``
+ - ``/etc/freeradius/3.0/mods-config/files/accounting``
+
+.. _radius-lease-allocation:
+
+RADIUS Workflows for Lease Allocation
+-------------------------------------
+
+The following diagrams show a high level view of how RADIUS assists with the
+lease allocation process in :iscman:`kea-dhcp4` and :iscman:`kea-dhcp6`.
+
+.. figure:: ../uml/radius.*
+
+Somewhat tangential to lease allocation, and not shown in the diagrams above,
+is the ``command_processed`` callout, which sends Accounting-Request messages
+when a lease command is received.
+
+.. _radius-differences:
+
+Differences Between RADIUS Hook Libraries Prior To 2.4.0 and As Of 2.6.0
+------------------------------------------------------------------------
+
+The RADIUS hook library in 2.4.0 and prior versions relied on the FreeRADIUS
+client library to function. Starting with 2.6.0 and onwards, the RADIUS hook
+library is standalone with its own RADIUS client implementation and its own
+RADIUS dictionary. There are differences:
+
+.. list-table::
+ :header-rows: 1
+
+ * - Feature
+
+ - Old
+
+ - New
+
+ * - Support for Attribute Data Types
+
+ - string, ipaddr, ipv4prefix, integer, integer64, date, ifid, ipv6addr, ipv6prefix, tlv, abinary, byte, ether, short, signed, octets
+
+ - string (can simulate any other unsupported data type too), ipaddr, integer, date (interpreted as integer), ipv6addr, ipv6prefix
+
+ * - Names of Standard Attributes
+
+ - Taken from the FreeRADIUS dictionary.
+
+ - Taken from the Kea RADIUS dictionary and the IANA registry. There is an aliasing mechanism built into the library that ensures backward compatibility e.g. ``Password`` translates to the standard name of the attribute ``User-Password``.
+
+ * - Resolution of RADIUS Server Domain Names
+
+ - At run time.
+
+ - At hook library load time.
+
+ * - Automatic Deduction of Source Address for Reaching RADIUS Servers (configured with ``bindaddr: "*"``)
+
+ - At run time.
+
+ - At hook library load time.
+
+ * - RADIUS Server Limit per Service
+
+ - 8
+
+ - Unlimited
+
+ * - Support for Including Dictionaries Inside Dictionaries
+
+ - Yes
+
+ - No
+
+ * - Support for Vendor Attributes
+
+ - Yes
+
+ - No
+
+ * - Attribute Names and Attribute Values
+
+ - Case-insensitive
+
+ - Case-sensitive
+
+ * - Integer Values
+
+ - Do not require an attribute definition.
+
+ - Must have an associated attribute definition in the dictionary.
+
+ * - Reply-Message Presence in the Kea Logs
+
+ - Only as part of the aggregated list of attributes in ``RADIUS_AUTHENTICATION_ACCEPTED``, ``RADIUS_ACCESS_CACHE_INSERT``, ``RADIUS_ACCESS_CACHE_GET`` log messages.
+
+ - Also has a dedicated ``RADIUS_REPLY_MESSAGE_ATTRIBUTE`` message per each Reply-Message attribute logged after a valid RADIUS reply is received.
+
+ * - Behavior of Multiple Attributes of the Same Type (except Reply-Message)
+
+ - Experimentally, only the **first** attribute on the wire from an Access-Accept message is considered.
+
+ - Experimentally, only the **last** attribute on the wire from an Access-Accept message is considered.
diff --git a/doc/sphinx/arm/hammer.rst b/doc/sphinx/arm/hammer.rst
new file mode 100644
index 0000000..2d25a52
--- /dev/null
+++ b/doc/sphinx/arm/hammer.rst
@@ -0,0 +1,137 @@
+.. _hammer:
+
+Hammer Building Tool
+====================
+
+Hammer is a Python 3 script that lets users automate tasks related to building
+Kea, such as setting up virtual machines, installing Kea dependencies,
+compiling Kea with various options, running unit-tests and more. This
+tool was created primarily for internal QA purposes at ISC and it is not
+included in the Kea distribution; however, it is available in the Kea
+git repository. This tool was developed primarily for internal purposes
+and ISC cannot guarantee its proper operation. Administrators who decide to use it
+should do so with care.
+
+.. note::
+
+ Use of this tool is completely optional. Everything it does can be
+ done manually.
+
+The first-time user is strongly encouraged to look at Hammer's built-in
+help:
+
+.. code-block:: console
+
+ $ ./hammer.py --help
+
+It will list available parameters.
+
+Hammer is able to set up various operating systems running either in LXC
+or in VirtualBox. For a list of supported systems, use the
+``supported-systems`` command:
+
+.. code-block:: console
+
+ $ ./hammer.py supported-systems
+ fedora:
+ - 37: lxc
+ - 38:
+ centos:
+ - 8: lxc, virtualbox
+ - 9:
+ rhel:
+ - 8: virtualbox
+ - 9:
+ ubuntu:
+ - 18.04: lxc, virtualbox
+ - 20.04: lxc
+ - 22.04: lxc
+ debian:
+ - 10: lxc, virtualbox
+ - 11: lxc
+ - 12: lxc
+ freebsd:
+ - 12.0: virtualbox
+ - 12.1:
+ - 13.0: virtualbox
+ alpine:
+ - 3.15: lxc
+ - 3.16: lxc
+ - 3.17: lxc
+
+It is also possible to run the build locally, in the current system (if the OS
+is supported).
+
+First, the Hammer dependencies must be installed: Vagrant
+and either VirtualBox or LXC. Hammer can install
+Vagrant and the required Vagrant plugins using the command:
+
+.. code-block:: console
+
+ $ ./hammer.py ensure-hammer-deps
+
+VirtualBox and LXC must be installed manually.
+
+The basic functions provided by Hammer are to prepare the build environment
+and perform the actual build, and to run the unit tests locally in the current
+system. This can be achieved by running the command:
+
+.. code-block:: console
+
+ $ ./hammer.py build -p local
+
+The scope of the process can be defined using the ``--with`` (``-w``) and ``--without``
+(``-x``) options. By default, the ``build`` command builds Kea with
+documentation, installs it locally, and runs unit tests.
+
+To exclude the installation and generation of docs, type:
+
+.. code-block:: console
+
+ $ ./hammer.py build -p local -x install docs
+
+The basic scope can be extended by mysql, pgsql, native-pkg, shell, and forge.
+Please check ``./hammer.py build --help`` for more details.
+
+.. note::
+
+ If building Kea locally, Hammer dependencies like Vagrant are
+ not needed.
+
+Hammer can be told to set up a new virtual machine with a specified
+operating system, without the build:
+
+.. code-block:: console
+
+ $ ./hammer.py prepare-system -p virtualbox -s freebsd -r 12.0
+
+This way, a system can be prepared for our own use. To get to such a system
+using SSH, invoke:
+
+.. code-block:: console
+
+ $ ./hammer.py ssh -p virtualbox -s freebsd -r 12.0
+
+It is possible to speed up subsequent Hammer builds via
+`ccache <https://ccache.samba.org/>`__. During
+compilation, ccache stores objects in a shared folder. In subsequent runs,
+instead of doing an actual compilation, ccache returns the stored earlier
+objects. The cache with these objects for reuse must be stored outside of VM
+or LXC. To indicate the folder, the ``--ccache-dir``
+parameter for Hammer must be included. In the indicated folder, there are separate stored objects for each target
+operating system.
+
+.. code-block:: console
+
+ $ ./hammer.py build -p lxc -s ubuntu -r 18.04 --ccache-dir ~/kea-ccache
+
+.. note::
+
+ ccache is currently only supported for LXC in Hammer; support
+ for VirtualBox may be added later.
+
+For more information check:
+
+.. code-block:: console
+
+ $ ./hammer.py --help
diff --git a/doc/sphinx/arm/hooks-bootp.rst b/doc/sphinx/arm/hooks-bootp.rst
new file mode 100644
index 0000000..7f8a749
--- /dev/null
+++ b/doc/sphinx/arm/hooks-bootp.rst
@@ -0,0 +1,91 @@
+.. ischooklib:: libdhcp_bootp.so
+.. _hooks-bootp:
+
+``libdhcp_bootp.so``: Support for BOOTP Clients
+===============================================
+
+This hook library adds support for BOOTP with vendor-information extensions
+(`RFC 1497 <https://tools.ietf.org/html/rfc1497>`__). Received BOOTP
+requests are recognized, translated into DHCPREQUEST packets by adding
+a ``dhcp-message-type`` option, and put into the "BOOTP" client class.
+Members of this class get infinite lifetime leases but the class can
+also be used to guard a pool of addresses.
+
+The DHCP-specific options, such as ``dhcp-message-type``, are removed from
+the server's responses; responses shorter than the BOOTP minimum
+size of 300 octets are padded to this size.
+
+.. note::
+
+ :ischooklib:`libdhcp_bootp.so` is part of the open source code and is
+ available to every Kea user.
+
+.. note::
+
+ This library can only be loaded by the :iscman:`kea-dhcp4` process,
+ as there is no BOOTP protocol for IPv6.
+
+This library is loaded similarly to other hook libraries, and
+it takes no parameters.
+
+::
+
+ "Dhcp4": {
+ "hooks-libraries": [
+ { "library": "/usr/local/lib/libdhcp_bootp.so" },
+ ...
+ ]
+ }
+
+.. note::
+
+ A host reservation for a BOOTP client should use the hardware address
+ as the identifier (the ``client-id`` option is a DHCP-specific option).
+
+.. _hooks-bootp-config:
+
+Incoming BOOTP packets are added to the BOOTP class, allowing administrators
+to segregate BOOTP clients into separate pools. For example:
+
+::
+
+ "Dhcp4": {
+ "client-classes": [
+ {
+ // The DHCP class is the complement of the BOOTP class
+ "name": "DHCP",
+ "test": "not member('BOOTP')"
+ }
+ ],
+ "subnet4": [
+ {
+ "subnet": "192.0.2.0/24",
+ "pools": [
+ {
+ // BOOTP clients will be handled here
+ "pool": "192.0.2.200 - 192.0.2.254",
+ "client-class": "BOOTP"
+ },
+ {
+ // Regular DHCP clients will be handled here
+ "pool": "192.0.2.1 - 192.0.2.199",
+ "client-class": "DHCP"
+ }],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+
+
+.. _hooks-bootp-limitations:
+
+BOOTP Hooks Limitations
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Currently the BOOTP library has the following limitation:
+
+- Basic BOOTP, as defined in `RFC 951
+ <https://tools.ietf.org/html/rfc951>`__, is not supported. Kea only
+ supports BOOTP with vendor-information extensions.
diff --git a/doc/sphinx/arm/hooks-cb-cmds.rst b/doc/sphinx/arm/hooks-cb-cmds.rst
new file mode 100644
index 0000000..237f911
--- /dev/null
+++ b/doc/sphinx/arm/hooks-cb-cmds.rst
@@ -0,0 +1,2248 @@
+.. ischooklib:: libdhcp_cb_cmds.so
+.. _hooks-cb-cmds:
+
+``libdhcp_cb_cmds.so``: Configuration Backend Commands
+======================================================
+
+This hook library is used to manage Kea
+servers' configurations in a configuration backend database. This library must
+be used in conjunction with the available CB hook libraries implementing
+the common APIs to create, read, update, and delete (CRUD) the
+configuration information in the respective databases. For example:
+:ischooklib:`libdhcp_mysql_cb.so` implements this API for MySQL while
+:ischooklib:`libdhcp_pgsql_cb.so` implements this API for PostgreSQL.
+To manage the configuration information in a MySQL database, both
+:ischooklib:`libdhcp_mysql_cb.so` and :ischooklib:`libdhcp_cb_cmds.so`
+must be loaded by the server used for the configuration management.
+To manage the configuration information in a PostgreSQL database, both
+:ischooklib:`libdhcp_pgsql_cb.so` and :ischooklib:`libdhcp_cb_cmds.so`
+must be loaded by the server used for the configuration management.
+
+More information on how to configure the Configuration Backend hook library for
+use with a MySQL or PostgreSQL database can be found in the :ref:`dhcp4-cb`
+and :ref:`dhcp6-cb` sections.
+
+.. note::
+
+ :ischooklib:`libdhcp_cb_cmds.so` is available only to ISC customers with
+ a paid support contract. For more information on subscription options,
+ please complete the form at https://www.isc.org/contact.
+
+.. note::
+
+ This library can only be loaded by the :iscman:`kea-dhcp4` or
+ :iscman:`kea-dhcp6` process.
+
+.. note::
+
+ Please read about :ref:`cb-limitations` before using the commands
+ described in this section.
+
+Command Structure
+~~~~~~~~~~~~~~~~~
+
+There are 5 types of commands supported by this library:
+
+- ``del`` - delete the selected object from the database, e.g.
+ :isccmd:`remote-global-parameter4-del`.
+
+- ``get`` - fetch the selected object from the database, e.g.
+ :isccmd:`remote-subnet4-get-by-id`.
+
+- ``get-all`` - fetch all objects of the particular type from the
+ database, e.g. :isccmd:`remote-option-def4-get-all`.
+
+- ``list`` - list all objects of the particular type in the database,
+ e.g. :isccmd:`remote-network4-list`; this class of commands returns brief
+ information about each object compared to the output of ``get-all``.
+
+- ``set`` - creates or replaces an object of the given type in the
+ database, e.g. :isccmd:`remote-option4-global-set`.
+
+All types of commands accept an optional ``remote`` map which selects the
+database instance to which the command refers. For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-subnet4-list",
+ "arguments": {
+ "remote": {
+ "type": "mysql",
+ "host": "192.0.2.33",
+ "port": 3302
+ }
+ }
+ }
+
+selects the MySQL database, running on host 192.0.2.33 and port 3302, to
+fetch the list of subnets from. All parameters in the ``remote`` argument are
+optional. The ``port`` parameter can be only specified in conjunction
+with the ``host``. If no options in the ``remote`` parameter are to
+be specified, the parameter should be omitted. In this case, the server
+will use the first backend listed in the ``config-control`` map within
+the configuration of the server receiving the command.
+
+.. note::
+
+ In the current Kea release, it is only possible to configure the Kea server
+ to use a single configuration backend. Strictly speaking, it is
+ possible to point the Kea server to at most one database (either MySQL or
+ PostgreSQL) using the ``config-control`` parameter. Therefore, the ``remote``
+ parameter may be omitted in the commands and :ischooklib:`libdhcp_cb_cmds.so`
+ uses the sole backend by default. The example commands below most often show a
+ value of "mysql" for the ``type`` parameter; it should be assumed that the
+ value is "postgresql" for installations using a PostgreSQL database.
+
+.. _cb-cmds-dhcp:
+
+Control Commands for DHCP Servers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section describes and gives some examples of the control commands
+implemented by :ischooklib:`libdhcp_cb_cmds.so`, to manage the
+configuration information of the DHCPv4 and DHCPv6 servers. Many of the
+commands are almost identical between DHCPv4 and DHCPv6; they only
+differ by the command name. Other commands differ slightly by the
+structure of the inserted data; for example, the structure of the IPv4 subnet
+information is different than that of the IPv6 subnet.
+Nevertheless, they still share the structure of their command arguments
+and thus it makes sense to describe them together.
+
+In addition, whenever the text in the subsequent sections refers to a
+DHCP command or DHCP parameter, it refers to both DHCPv4 and DHCPv6
+variants. The text specific to the particular server type refers to them
+as: DHCPv4 command, DHCPv4 parameter, DHCPv6 command, DHCPv6 parameter,
+etc.
+
+.. _cb-cmds-metadata:
+
+Metadata
+~~~~~~~~
+
+The typical response to the ``get`` or ``list`` command includes a list
+of returned objects (e.g. subnets), and each such object contains the
+``metadata`` map with some database-specific information describing this
+object. In other words, the metadata contains any information about the
+fetched object which may be useful for an administrator but which is not
+part of the object specification from the DHCP server standpoint. In the
+present Kea release, the metadata is limited to the ``server-tag``. It
+describes the association of the object with a particular server or
+all servers.
+
+The following is the example response to the :isccmd:`remote-network4-list`
+command, which includes the metadata:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "1 IPv4 shared network(s) found.",
+ "arguments": {
+ "shared-networks": [
+ {
+ "name": "level3",
+ "metadata": {
+ "server-tags": [ "all" ]
+ }
+ }
+ ],
+ "count": 1
+ }
+ }
+
+
+Client implementations must not assume that the metadata contains only
+the ``server-tags`` parameter. In future releases, it is expected that this
+map will be extended with additional information, e.g. object modification
+time, log message created during the last modification, etc.
+
+.. isccmd:: remote-server4-del
+.. _command-remote-server4-del:
+.. isccmd:: remote-server6-del
+.. _command-remote-server6-del:
+
+The ``remote-server4-del``, ``remote-server6-del`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to delete the information about a selected DHCP server from
+the configuration database. The server is identified by a unique case
+insensitive server tag. For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-server4-del",
+ "arguments": {
+ "servers": [
+ {
+ "server-tag": "server1"
+ }
+ ],
+ "remote": {
+ "type": "postgresql"
+ }
+ }
+ }
+
+As a result of this command, all associations of the configuration for the
+user-defined server called "server1" are removed from the database, including
+non-shareable configuration information, such as global parameters, option
+definitions, and global options. Any shareable configuration information,
+i.e. the configuration elements which may
+be associated with more than one server, is preserved. In particular, the
+subnets and shared networks associated with the deleted servers are
+preserved. If any of the shareable configuration elements was associated only
+with the deleted server, this object becomes unassigned (orphaned). For
+example: if a subnet has been created and associated with "server1" using
+the :isccmd:`remote-subnet4-set` command and "server1" is subsequently deleted, the
+subnet remains in the database but no servers can use this subnet. The
+subnet can be updated using the :isccmd:`remote-subnet4-set` command, and can be
+associated with either another server or with all servers, using the special
+server tag "all". Such a subnet can be also deleted from the database
+using the :isccmd:`remote-subnet4-del-by-id` or
+:isccmd:`remote-subnet4-del-by-prefix` command, if it is no longer needed.
+
+The following is the successful response to the :isccmd:`remote-server4-del` command:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "1 DHCPv4 server(s) deleted.",
+ "arguments": {
+ "count": 1
+ }
+ }
+
+
+.. warning::
+
+ The :isccmd:`remote-server4-del` and
+ :isccmd:`remote-server6-del` commands must be used with
+ care, because an accidental deletion of the server can cause some parts of the
+ existing configurations to be lost permanently from the database. This
+ operation is not reversible. Re-creation of the accidentally deleted server
+ does not revert the lost configuration for that server and such configuration
+ must be re-created manually by the user.
+
+.. isccmd:: remote-server4-get
+.. _command-remote-server4-get:
+.. isccmd:: remote-server6-get
+.. _command-remote-server6-get:
+
+The ``remote-server4-get``, ``remote-server6-get`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to fetch the information about the selected DHCP server
+from the configuration database. For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-server6-get",
+ "arguments": {
+ "servers": [
+ {
+ "server-tag": "server1"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+
+This command fetches the information about the DHCPv6 server identified by the
+server tag "server1". The server tag is case-insensitive. A successful response
+returns basic information about the server, such as the server tag and the user's
+description of the server:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "DHCP server server1 found.",
+ "arguments": {
+ "servers": [
+ {
+ "server-tag": "server1",
+ "description": "A DHCPv6 server located on the first floor."
+ }
+ ],
+ "count": 1
+ }
+ }
+
+.. isccmd:: remote-server4-get-all
+.. _command-remote-server4-get-all:
+.. isccmd:: remote-server6-get-all
+.. _command-remote-server6-get-all:
+
+The ``remote-server4-get-all``, ``remote-server6-get-all`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to fetch all user-defined DHCPv4 or DHCPv6 servers from the
+database. The command structure is very simple:
+
+.. code-block:: json
+
+ {
+ "command": "remote-server4-get-all",
+ "arguments": {
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The response includes basic information about each server, such as its server
+tag and description:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "DHCPv4 servers found.",
+ "arguments": {
+ "servers": [
+ {
+ "server-tag": "server1",
+ "description": "A DHCP server located on the first floor."
+ },
+ {
+ "server-tag": "server2",
+ "description": "An old DHCP server to be soon replaced."
+ }
+ ],
+ "count": 2
+ }
+ }
+
+.. isccmd:: remote-server4-set
+.. _command-remote-server4-set:
+.. isccmd:: remote-server6-set
+.. _command-remote-server6-set:
+
+The ``remote-server4-set``, ``remote-server6-set`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to create or replace an information about a DHCP server in
+the database. The information about the server must be created when there is a
+need to differentiate the configurations used by various Kea instances
+connecting to the same database. Various configuration elements, e.g. global
+parameters, subnets, etc. may be explicitly associated with the selected servers
+(using server tags as identifiers), allowing only these servers to use the
+respective configuration elements. Using the particular server tag to make such
+associations is only possible when the server information has been stored in the
+database via the :isccmd:`remote-server4-set` or
+:isccmd:`remote-server6-set` commands. The
+following command creates a new (or updates an existing) DHCPv6 server in the
+database:
+
+.. code-block:: json
+
+ {
+ "command": "remote-server6-set",
+ "arguments": {
+ "servers": [
+ {
+ "server-tag": "server1",
+ "description": "A DHCP server on the ground floor."
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The server tag must be unique across all servers in the database. When the
+server information under the given server tag already exists, it is replaced
+with the new information. The specified server tag is case-insensitive, and the
+maximum length of the server tag is 256 characters. The following keywords are
+reserved and cannot be used as server tags: "all" and "any".
+
+The following is the example response to the above command:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "DHCPv6 server successfully set.",
+ "arguments": {
+ "servers": [
+ {
+ "server-tag": "server1",
+ "description": "A DHCP server on the ground floor."
+ }
+ ]
+ }
+ }
+
+
+.. isccmd:: remote-global-parameter4-del
+.. _command-remote-global-parameter4-del:
+
+.. isccmd:: remote-global-parameter6-del
+.. _command-remote-global-parameter6-del:
+
+The ``remote-global-parameter4-del``, ``remote-global-parameter6-del`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to delete a global DHCP parameter from the
+configuration database. When the parameter is deleted from the database,
+the server uses the value specified in the configuration file for
+this parameter, or a default value if the parameter is not specified in
+the configuration file.
+
+The following command attempts to delete the DHCPv4 ``renew-timer``
+parameter common for all servers from the database:
+
+.. code-block:: json
+
+ {
+ "command": "remote-global-parameter4-del",
+ "arguments": {
+ "parameters": [ "renew-timer" ],
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "all" ]
+ }
+ }
+
+If a server-specific parameter is to be deleted, the
+``server-tags`` list must contain the tag of the appropriate
+server. There must be exactly one server tag specified in this list.
+
+.. isccmd:: remote-global-parameter4-get
+.. _command-remote-global-parameter4-get:
+
+.. isccmd:: remote-global-parameter6-get
+.. _command-remote-global-parameter6-get:
+
+The ``remote-global-parameter4-get``, ``remote-global-parameter6-get`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to fetch a scalar global DHCP parameter from the
+configuration database.
+
+The following command attempts to fetch the ``boot-file-name``
+parameter for "server1":
+
+.. code-block:: json
+
+ {
+ "command": "remote-global-parameter4-get",
+ "arguments": {
+ "parameters": [ "boot-file-name" ],
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "server1" ]
+ }
+ }
+
+
+The returned value has one of the four scalar types: string, integer,
+real, or boolean. Non-scalar global configuration parameters, such as map
+or list, are not returned by this command.
+
+In the case of the example above, the string value is returned, e.g.:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "1 DHCPv4 global parameter found.",
+ "arguments": {
+ "parameters": {
+ "boot-file-name": "/dev/null",
+ "metadata": {
+ "server-tags": [ "all" ]
+ }
+ },
+ "count": 1
+ }
+ }
+
+
+Note that the response above indicates that the returned parameter is associated
+with "all" servers rather than "server1", used in the command. This indicates
+that there is no "server1"-specific value in the database and therefore, the value
+shared by all servers is returned. If there were a "server1"-specific value
+in the database, that value would be returned instead.
+
+The example response for the integer value is:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "1 DHCPv4 global parameter found.",
+ "arguments": {
+ "parameters": {
+ "renew-timer": 2000,
+ "metadata": {
+ "server-tags": [ "server1" ]
+ }
+ },
+ "count": 1
+ }
+ }
+
+
+The real value:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "1 DHCPv4 global parameter found.",
+ "arguments": {
+ "parameters": {
+ "t1-percent": 0.85,
+ "metadata": {
+ "server-tags": [ "all" ]
+ }
+ },
+ "count": 1
+ }
+ }
+
+
+Finally, the boolean value:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "1 DHCPv4 global parameter found.",
+ "arguments": {
+ "parameters": {
+ "match-client-id": true,
+ "metadata": {
+ "server-tags": [ "server2" ]
+ }
+ },
+ "count": 1
+ }
+ }
+
+
+.. isccmd:: remote-global-parameter4-get-all
+.. _command-remote-global-parameter4-get-all:
+
+.. isccmd:: remote-global-parameter6-get-all
+.. _command-remote-global-parameter6-get-all:
+
+The ``remote-global-parameter4-get-all``, ``remote-global-parameter6-get-all`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to fetch all global DHCP parameters from the database
+for the specified server. The following example demonstrates how to fetch all
+global parameters to be used by the server "server1":
+
+.. code-block:: json
+
+ {
+ "command": "remote-global-parameter4-get-all",
+ "arguments": {
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "server1" ]
+ }
+ }
+
+The example response may look as follows:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "DHCPv4 global parameters found.",
+ "arguments": {
+ "parameters": [
+ {
+ "boot-file-name": "/dev/null",
+ "metadata": {
+ "server-tags": [ "server1" ]
+ }
+ },
+ {
+ "match-client-id": true,
+ "metadata": {
+ "server-tags": [ "all" ]
+ }
+ }
+ ],
+ "count": 2
+ }
+ }
+
+
+The example response contains two parameters: one string parameter and one
+boolean parameter. The metadata returned for each parameter indicates
+whether this parameter is specific to "server1" or applies to all servers. Since the
+``match-client-id`` value is associated with "all" servers,
+it indicates that there is no "server1"-specific setting for this parameter.
+Each parameter always has exactly one server tag associated with it, because
+global parameters are non-shareable configuration elements.
+
+.. note::
+
+ If the server tag is set to "all" in the command, the response will
+ contain only the global parameters associated with the logical server
+ "all". When the server tag points to the specific server (as in the
+ example above), the returned list combines parameters associated with
+ this server and all servers, but the former take precedence.
+
+.. isccmd:: remote-global-parameter4-set
+.. _command-remote-global-parameter4-set:
+
+.. isccmd:: remote-global-parameter6-set
+.. _command-remote-global-parameter6-set:
+
+The ``remote-global-parameter4-set``, ``remote-global-parameter6-set`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to create scalar global DHCP parameters in the
+database. If any of the parameters already exists, its value is replaced
+as a result of this command. It is possible to set multiple parameters
+within a single command, each having one of the four types: string,
+integer, real, or boolean. For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-global-parameter4-set",
+ "arguments": {
+ "parameters": {
+ "boot-file-name": "/dev/null",
+ "renew-timer": 2000,
+ "t1-percent": 0.85,
+ "match-client-id": true
+ },
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "server1" ]
+ }
+ }
+
+An error is returned if any of the parameters is not supported by the DHCP
+server or its type does not match. Care should be taken when multiple parameters
+are specified in a single command, because it is possible that only some of the
+parameters will be stored successfully and some will fail. If an error occurs when
+processing this command, it is recommended to use
+:isccmd:`remote-global-parameter4-get-all` or
+:isccmd:`remote-global-parameter6-get-all`
+to check which of the parameters have
+been stored/updated successfully and which have failed.
+
+The ``server-tags`` list is mandatory and must contain a single server tag or
+the keyword "all". In the example above, all specified parameters are associated
+with the "server1" server.
+
+.. isccmd:: remote-network4-del
+.. _command-remote-network4-del:
+
+.. isccmd:: remote-network6-del
+.. _command-remote-network6-del:
+
+The ``remote-network4-del``, ``remote-network6-del`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to delete an IPv4 or IPv6 shared network from
+the database. The optional parameter ``subnets-action`` determines
+whether the subnets belonging to the deleted shared network should also
+be deleted or preserved. The ``subnets-action`` parameter defaults to ``keep``,
+which preserves the subnets. If it is set to ``delete``, the subnets are
+deleted along with the shared network.
+
+The following command:
+
+.. code-block:: json
+
+ {
+ "command": "remote-network6-del",
+ "arguments": {
+ "shared-networks": [
+ {
+ "name": "level3"
+ }
+ ],
+ "subnets-action": "keep",
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+
+deletes the "level3" IPv6 shared network. The subnets are preserved, but
+they are disassociated from the deleted shared network and become
+global. This behavior corresponds to the behavior of the
+:isccmd:`network4-del`, :isccmd:`network6-del` commands with respect to the
+``subnets-action`` parameter.
+
+Note that the ``server-tags`` parameter cannot be used for this command.
+
+.. isccmd:: remote-network4-get
+.. _command-remote-network4-get:
+
+.. isccmd:: remote-network6-get
+.. _command-remote-network6-get:
+
+The ``remote-network4-get``, ``remote-network6-get`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to retrieve information about an IPv4 or
+IPv6 shared network. The optional parameter ``subnets-include`` denotes
+whether the subnets belonging to the shared network should also be
+returned. This parameter defaults to ``no``, in which case the subnets
+are not returned. If this parameter is set to ``full``, the subnets are
+returned together with the shared network.
+
+The following command fetches the "level3" IPv6 shared network along
+with the full information about the subnets belonging to it:
+
+.. code-block:: json
+
+ {
+ "command": "remote-network6-get",
+ "arguments": {
+ "shared-networks": [
+ {
+ "name": "level3"
+ }
+ ],
+ "subnets-include": "full",
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+Note that the ``server-tags`` parameter cannot be used for this command.
+
+.. isccmd:: remote-network4-list
+.. _command-remote-network4-list:
+
+.. isccmd:: remote-network6-list
+.. _command-remote-network6-list:
+
+The ``remote-network4-list``, ``remote-network6-list`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to list all IPv4 or IPv6 shared networks for a server.
+
+The following command retrieves all shared networks to be used by
+"server1" and "server2":
+
+.. code-block:: json
+
+ {
+ "command": "remote-network4-list",
+ "arguments": {
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "server1", "server2" ]
+ }
+ }
+
+The ``server-tags`` parameter is mandatory and contains one or more server
+tags. It may contain the keyword "all" to fetch the shared networks associated
+with all servers. When the ``server-tags`` list contains the
+``null`` value, the returned response contains a list of unassigned shared
+networks, i.e. the networks which are associated with no servers. For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-network4-list",
+ "arguments": {
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ null ]
+ }
+ }
+
+The example response to this command when non-null server tags are specified
+looks similar to this:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "3 IPv4 shared network(s) found.",
+ "arguments": {
+ "shared-networks": [
+ {
+ "name": "ground floor",
+ "metadata": {
+ "server-tags": [ "all" ]
+ }
+ },
+ {
+ "name": "floor2",
+ "metadata": {
+ "server-tags": [ "server1" ]
+ }
+ },
+ {
+ "name": "floor3",
+ "metadata": {
+ "server-tags": [ "server2" ]
+ }
+ }
+ ],
+ "count": 3
+ }
+ }
+
+The returned information about each shared network merely contains the shared
+network name and the metadata. To fetch detailed information about
+the selected shared network, use the :isccmd:`remote-network4-get` or
+:isccmd:`remote-network6-get` command.
+
+The example response above contains three shared networks. One of the
+shared networks is associated with all servers, so it is included in
+the list of shared networks to be used by "server1" and "server2".
+The remaining two shared networks are returned because one of them
+is associated with "server1" and another one is associated with
+"server2".
+
+When listing unassigned shared networks, the response looks similar
+to this:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "1 IPv4 shared network(s) found.",
+ "arguments": {
+ "shared-networks": [
+ {
+ "name": "fancy",
+ "metadata": {
+ "server-tags": [ null ]
+ }
+ }
+ ],
+ "count": 1
+ }
+ }
+
+The ``null`` value in the metadata indicates that the
+returned shared network is unassigned.
+
+.. isccmd:: remote-network4-set
+.. _command-remote-network4-set:
+
+.. isccmd:: remote-network6-set
+.. _command-remote-network6-set:
+
+The ``remote-network4-set``, ``remote-network6-set`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands create a new or replace an existing IPv4 or IPv6 shared
+network in the database. The structure of the shared network information
+is the same as in the Kea configuration file (see
+:ref:`shared-network4` and :ref:`shared-network6` for details),
+except that specifying subnets along with the shared
+network information is not allowed. Including the ``subnet4`` or ``subnet6`` parameter
+within the shared network information results in an error.
+
+These commands are intended to be used for managing the shared
+network-specific information and DHCP options. To associate and
+disassociate the subnets with the shared networks, the
+:isccmd:`remote-subnet4-set`, :isccmd:`remote-subnet6-set`
+commands should be used.
+
+The following command adds the IPv6 shared network "level3" to the
+database:
+
+.. code-block:: json
+
+ {
+ "command": "remote-network6-set",
+ "arguments": {
+ "shared-networks": [
+ {
+ "name": "level3",
+ "interface": "eth0",
+ "option-data": [ {
+ "name": "sntp-servers",
+ "data": "2001:db8:1::1"
+ } ]
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "all" ]
+ }
+ }
+
+
+This command includes the ``interface`` parameter, which sets the shared
+network-level interface name. Any remaining shared-network level parameters,
+which are not specified with the command, will be marked as
+"unspecified" in the database. The DHCP server uses the global
+values for unspecified parameters or, if the global values are not
+specified, the default values are used.
+
+The ``server-tags`` list is mandatory for this command and must include one or
+more server tags. As a result, the shared network is associated with all listed
+servers. The shared network may be associated with all servers connecting to the
+database when the keyword "all" is included.
+
+.. note::
+
+ As with other "set" commands, this command replaces all the
+ information about the given shared network in the database, if the
+ shared network already exists. Therefore, when sending this command,
+ make sure to always include all parameters that must be specified for
+ the updated shared-network instance. Any unspecified parameter will
+ be marked unspecified in the database, even if its value was present
+ prior to sending the command.
+
+.. isccmd:: remote-option-def4-del
+.. _command-remote-option-def4-del:
+
+.. isccmd:: remote-option-def6-del
+.. _command-remote-option-def6-del:
+
+The ``remote-option-def4-del``, ``remote-option-def6-del`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to delete a DHCP option definition from the
+database. The option definition is identified by an option code and
+option space. For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-option-def6-del",
+ "arguments": {
+ "option-defs": [
+ {
+ "code": 1,
+ "space": "isc"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "server1" ]
+ }
+ }
+
+
+deletes the definition of the option associated with "server1", having the
+code of 1 and belonging to the option space "isc". The default option spaces are
+"dhcp4" and "dhcp6" for the DHCPv4 and DHCPv6 top-level options, respectively. If
+there is no such option explicitly associated with "server1", no option is
+deleted. To delete an option belonging to "all" servers, the keyword
+"all" must be used as the server tag. The ``server-tags`` list must contain exactly
+one tag and cannot include the ``null`` value.
+
+.. isccmd:: remote-option-def4-get
+.. _command-remote-option-def4-get:
+
+.. isccmd:: remote-option-def6-get
+.. _command-remote-option-def6-get:
+
+The ``remote-option-def4-get``, ``remote-option-def6-get`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to fetch a specified DHCP option definition from
+the database. The option definition is identified by the option code and
+option space. The default option spaces are "dhcp4" and "dhcp6" for the
+DHCPv4 and DHCPv6 top-level options, respectively.
+
+The following command retrieves a DHCPv4 option definition associated with all
+servers, having the code of 1 and belonging to the option space "isc":
+
+.. code-block:: json
+
+ {
+ "command": "remote-option-def4-get",
+ "arguments": {
+ "option-defs": [
+ {
+ "code": 1,
+ "space": "isc"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "all" ]
+ }
+ }
+
+The ``server-tags`` list must include exactly one server tag or the keyword
+"all", and cannot contain the `null` value.
+
+.. isccmd:: remote-option-def4-get-all
+.. _command-remote-option-def4-get-all:
+
+.. isccmd:: remote-option-def6-get-all
+.. _command-remote-option-def6-get-all:
+
+The ``remote-option-def4-get-all``, ``remote-option-def6-get-all`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to fetch all DHCP option definitions from the database
+for the given server or all servers. For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-option-def6-get-all",
+ "arguments": {
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "all" ]
+ }
+ }
+
+This command attempts to fetch all DHCPv6 option definitions associated
+with "all" servers. The ``server-tags`` list is mandatory for
+this command and must include exactly one server tag or the keyword "all".
+It cannot include the ``null`` value.
+
+The following is the example response to this command:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "1 DHCPv6 option definition(s) found.",
+ "arguments": {
+ "option-defs": [
+ {
+ "name": "bar",
+ "code": 1012,
+ "space": "dhcp6",
+ "type": "record",
+ "array": true,
+ "record-types": "ipv6-address, uint16",
+ "encapsulate": "",
+ "metadata": {
+ "server-tags": [ "all" ]
+ }
+ }
+ ],
+ "count": 1
+ }
+ }
+
+The response contains an option definition associated with all servers, as
+indicated by the metadata.
+
+.. isccmd:: remote-option-def4-set
+.. _command-remote-option-def4-set:
+
+.. isccmd:: remote-option-def6-set
+.. _command-remote-option-def6-set:
+
+The ``remote-option-def4-set``, ``remote-option-def6-set`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands create a new DHCP option definition or replace an
+existing option definition in the database. The structure of the option
+definition information is the same as in the Kea configuration file (see
+:ref:`dhcp4-custom-options` and :ref:`dhcp6-custom-options`).
+The following command creates the DHCPv4 option definition at the
+top-level "dhcp4" option space and associates it with "server1":
+
+.. code-block:: json
+
+ {
+ "command": "remote-option-def4-set",
+ "arguments": {
+ "option-defs": [
+ {
+ "name": "foo",
+ "code": 222,
+ "type": "uint32",
+ "array": false,
+ "record-types": "",
+ "space": "dhcp4",
+ "encapsulate": ""
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "server1" ]
+ }
+ }
+
+The ``server-tags`` list must include exactly one
+server tag or the keyword "all", and cannot contain the
+``null`` value.
+
+.. isccmd:: remote-option4-global-del
+.. _command-remote-option4-global-del:
+
+.. isccmd:: remote-option6-global-del
+.. _command-remote-option6-global-del:
+
+The ``remote-option4-global-del``, ``remote-option6-global-del`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to delete a global DHCP option from the
+database. The option is identified by an option code and option space.
+For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-option4-global-del",
+ "arguments": {
+ "options": [
+ {
+ "code": 5,
+ "space": "dhcp4"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "server1" ]
+ }
+ }
+
+The "dhcp4" value represents the top-level option space where the standard DHCPv4
+options belong. The ``server-tags`` parameter is mandatory and must include a
+single option tag or the keyword "all". If the explicit server tag is specified,
+this command attempts to delete a global option associated with this
+server. If there is no such option associated with the given server, no option
+is deleted. To delete an option associated with all servers, the
+keyword "all" must be specified.
+
+.. isccmd:: remote-option4-global-get
+.. _command-remote-option4-global-get:
+
+.. isccmd:: remote-option6-global-get
+.. _command-remote-option6-global-get:
+
+The ``remote-option4-global-get``, ``remote-option6-global-get`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to fetch a global DHCP option from the database.
+The option is identified by the code and option space. The top-level
+option spaces where DHCP standard options belong are called "dhcp4" and
+"dhcp6" for the DHCPv4 and DHCPv6 servers, respectively.
+
+The following command retrieves the IPv6 "DNS Servers" (code 23) option
+associated with all servers:
+
+.. code-block:: json
+
+ {
+ "command": "remote-option6-global-get",
+ "arguments": {
+ "options": [
+ {
+ "code": 23,
+ "space": "dhcp6"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "all" ]
+ }
+ }
+
+The ``server-tags`` parameter is mandatory and must include exactly one
+server tag or the keyword "all". It cannot contain the ``null``
+value.
+
+.. isccmd:: remote-option4-global-get-all
+.. _command-remote-option4-global-get-all:
+
+.. isccmd:: remote-option6-global-get-all
+.. _command-remote-option6-global-get-all:
+
+The ``remote-option4-global-get-all``, ``remote-option6-global-get-all`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to fetch all global DHCP options from the configuration
+database for the given server or for all servers. The following command
+fetches all global DHCPv4 options for "server1":
+
+.. code-block:: json
+
+ {
+ "command": "remote-option6-global-get-all",
+ "arguments": {
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "server1" ]
+ }
+ }
+
+The ``server-tags`` list is mandatory for this command and
+must contain exactly one server tag or a keyword "all"; it cannot contain
+the ``null`` value.
+
+The following is a example response to this
+command with a single option being associated with "server1" returned:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "DHCPv4 options found.",
+ "arguments": {
+ "options": [
+ {
+ "name": "domain-name-servers",
+ "code": 6,
+ "space": "dhcp4",
+ "csv-format": false,
+ "data": "192.0.2.3",
+ "metadata": {
+ "server-tags": [ "server1" ]
+ }
+ }
+ ],
+ "count": 1
+ }
+ }
+
+.. isccmd:: remote-option4-global-set
+.. _command-remote-option4-global-set:
+
+.. isccmd:: remote-option6-global-set
+.. _command-remote-option6-global-set:
+
+The ``remote-option4-global-set``, ``remote-option6-global-set`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands create a new global DHCP option or replace an existing
+option in the database. The structure of the option information is the
+same as in the Kea configuration file (see :ref:`dhcp4-std-options`
+and :ref:`dhcp6-std-options`). For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-option6-global-set",
+ "arguments": {
+ "options": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8:1::1"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "server1" ]
+ }
+ }
+
+The ``server-tags`` list is mandatory for this command
+and must include exactly one server tag or the keyword "all"; it cannot
+include the ``null`` value. The command above associates
+the option with the "server1" server.
+
+Note that specifying an option name instead of the option code only
+works reliably for standard DHCP options. When specifying a value
+for a user-defined DHCP option, the option code should be indicated
+instead of the name. For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-option6-global-set",
+ "arguments": {
+ "options": [
+ {
+ "code": 1,
+ "space": "isc",
+ "data": "2001:db8:1::1"
+ }
+ ],
+ "server-tags": [ "server1" ]
+ }
+ }
+
+.. isccmd:: remote-option4-network-del
+.. _command-remote-option4-network-del:
+
+.. isccmd:: remote-option6-network-del
+.. _command-remote-option6-network-del:
+
+The ``remote-option4-network-del``, ``remote-option6-network-del`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to delete a shared-network-specific DHCP
+option from the database. The option is identified by an option code
+and option space and these two parameters are passed within the
+``options`` list. Another list, ``shared-networks``, contains a map
+with the name of the shared network from which the option is to
+be deleted. If the option is not explicitly specified for this
+shared network, no option is deleted. In particular, the given
+option may be present for a subnet belonging to the shared network.
+Such an option instance is not affected by this command as this
+command merely deletes the shared-network level option. To
+delete a subnet-level option, the :isccmd:`remote-option4-subnet-del`,
+:isccmd:`remote-option6-subnet-del` commands must be used instead.
+
+The following command attempts to delete an option having the
+option code 5 in the top-level option space from the shared
+network "fancy".
+
+.. code-block:: json
+
+ {
+ "command": "remote-option4-network-del",
+ "arguments": {
+ "shared-networks": [
+ {
+ "name": "fancy"
+ }
+ ],
+ "options": [
+ {
+ "code": 5,
+ "space": "dhcp4"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The "dhcp4" value represents the top-level option space where the standard DHCPv4
+options belong. The ``server-tags`` parameter cannot be specified for this command.
+
+.. isccmd:: remote-option4-network-set
+.. _command-remote-option4-network-set:
+
+.. isccmd:: remote-option6-network-set
+.. _command-remote-option6-network-set:
+
+The ``remote-option4-network-set``, ``remote-option6-network-set`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands create a new shared-network-specific DHCP option or replace
+an existing option in the database. The structure of the option information
+is the same as in the Kea configuration file (see :ref:`dhcp4-std-options`
+and :ref:`dhcp6-std-options`). The option information is carried in the
+``options`` list. Another list, ``shared-networks``, contains a map with the
+name of the shared network for which the option is to be set. If such an option
+already exists for the shared network, it is replaced with the new instance.
+
+.. code-block:: json
+
+ {
+ "command": "remote-option6-network-set",
+ "arguments": {
+ "shared-networks": [
+ {
+ "name": "fancy"
+ }
+ ],
+ "options": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8:1::1"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The ``server-tags`` parameter cannot be specified for this command.
+
+Specifying an option name instead of the option code only works reliably
+for standard DHCP options. When specifying a value for a user-defined
+DHCP option, the option code should be indicated instead of the name.
+
+.. isccmd:: remote-option6-pd-pool-del
+.. _command-remote-option6-pd-pool-del:
+
+The ``remote-option6-pd-pool-del`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to delete a prefix delegation pool-specific DHCPv6
+option from the database. The option is identified by an option code
+and option space, and these two parameters are passed within the
+``options`` list. Another list, ``pd-pools``, contains a map with the
+prefix-delegation-pool prefix and length identifying the pool. If the
+option is not explicitly specified for this pool, no option is deleted.
+In particular, the given option may exist for a subnet containing
+the specified pool. Such an option instance is not affected by this
+command, as this command merely deletes a prefix delegation pool-level
+option. To delete a subnet level option, the
+:isccmd:`remote-option6-subnet-del` command must be used instead.
+
+.. code-block:: json
+
+ {
+ "command": "remote-option6-pd-pool-del",
+ "arguments": {
+ "pd-pools": [
+ {
+ "prefix": "3000::",
+ "prefix-len": 64
+ }
+ ],
+ "options": [
+ {
+ "code": 23,
+ "space": "dhcp6"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The "dhcp6" value represents the top-level option space where the standard DHCPv6
+options belong. The ``server-tags`` parameter cannot be specified for this command.
+
+.. isccmd:: remote-option6-pd-pool-set
+.. _command-remote-option6-pd-pool-set:
+
+The ``remote-option6-pd-pool-set`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command creates a new prefix delegation pool-specific DHCPv6 option or
+replaces an existing option in the database. The structure of the option
+information is the same as in the Kea configuration file (see :ref:`dhcp4-std-options`
+and :ref:`dhcp6-std-options`). The option information is carried in the
+``options`` list. Another list, ``pd-pools``, contains a map with the
+prefix-delegation-pool prefix and the prefix length identifying the pool. If such an
+option already exists for the prefix delegation pool, it is replaced with
+the new instance.
+
+For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-option6-pd-pool-set",
+ "arguments": {
+ "pd-pools": [
+ {
+ "prefix": "3001:1::",
+ "length": 64
+ }
+ ],
+ "options": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8:1::1"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The ``server-tags`` parameter cannot be specified for this command.
+
+Specifying an option name instead of the option code only works reliably
+for standard DHCP options. When specifying a value for a user-defined
+DHCP option, the option code should be indicated instead of the name.
+
+.. isccmd:: remote-option4-pool-del
+.. _command-remote-option4-pool-del:
+
+.. isccmd:: remote-option6-pool-del
+.. _command-remote-option6-pool-del:
+
+The ``remote-option4-pool-del``, ``remote-option6-pool-del`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to delete an address-pool-specific DHCP
+option from the database. The option is identified by an option code
+and option space, and these two parameters are passed within the
+``options`` list. Another list, ``pools``, contains a map with the
+IP address range or prefix identifying the pool. If the option
+is not explicitly specified for this pool, no option is deleted.
+In particular, the given option may exist for a subnet containing
+the specified pool. Such an option instance is not affected by this
+command, as this command merely deletes a pool-level option. To
+delete a subnet-level option, the :isccmd:`remote-option4-subnet-del`,
+:isccmd:`remote-option6-subnet-del` commands must be used instead.
+
+The following command attempts to delete an option having the
+option code 5 in the top-level option space from an IPv4 address
+pool:
+
+.. code-block:: json
+
+ {
+ "command": "remote-option4-pool-del",
+ "arguments": {
+ "pools": [
+ {
+ "pool": "192.0.2.10 - 192.0.2.100"
+ }
+ ],
+ "options": [
+ {
+ "code": 5,
+ "space": "dhcp4"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The "dhcp4" value represents the top-level option space where the standard DHCPv4
+options belong. The ``server-tags`` parameter cannot be specified for this command.
+
+.. isccmd:: remote-option4-pool-set
+.. _command-remote-option4-pool-set:
+
+.. isccmd:: remote-option6-pool-set
+.. _command-remote-option6-pool-set:
+
+The ``remote-option4-pool-set``, ``remote-option6-pool-set`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands create a new address-pool-specific DHCP option or replace
+an existing option in the database. The structure of the option information
+is the same as in the Kea configuration file (see :ref:`dhcp4-std-options`
+and :ref:`dhcp6-std-options`). The option information is carried in the
+``options`` list. Another list, ``pools``, contains a map with the IP address
+range or prefix identifying the pool. If such an option already exists for
+the pool, it is replaced with the new instance.
+
+For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-option4-pool-set",
+ "arguments": {
+ "pools": [
+ {
+ "pool": "192.0.2.10 - 192.0.2.100"
+ }
+ ],
+ "options": [
+ {
+ "name": "domain-name-servers",
+ "data": "10.0.0.1"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The ``server-tags`` parameter cannot be specified for this command.
+
+Specifying an option name instead of the option code only works reliably
+for standard DHCP options. When specifying a value for a user-defined
+DHCP option, the option code should be indicated instead of the name.
+
+.. isccmd:: remote-option4-subnet-del
+.. _command-remote-option4-subnet-del:
+
+.. isccmd:: remote-option6-subnet-del
+.. _command-remote-option6-subnet-del:
+
+The ``remote-option4-subnet-del``, ``remote-option6-subnet-del`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to delete a subnet-specific DHCP option
+from the database. The option is identified by an option code
+and option space, and these two parameters are passed within the
+``options`` list. Another list, ``subnets``, contains a map with the
+identifier of the subnet from which the option is to be deleted.
+If the option is not explicitly specified for this subnet, no
+option is deleted.
+
+The following command attempts to delete an option having the
+option code 5 in the top-level option space from the subnet
+having an identifier of 123.
+
+.. code-block:: json
+
+ {
+ "command": "remote-option4-subnet-del",
+ "arguments": {
+ "subnets": [
+ {
+ "id": 123
+ }
+ ],
+ "options": [
+ {
+ "code": 5,
+ "space": "dhcp4"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The "dhcp4" value represents the top-level option space where the standard DHCPv4
+options belong. The ``server-tags`` parameter cannot be specified for this command.
+
+.. isccmd:: remote-option4-subnet-set
+.. _command-remote-option4-subnet-set:
+
+.. isccmd:: remote-option6-subnet-set
+.. _command-remote-option6-subnet-set:
+
+The ``remote-option4-subnet-set``, ``remote-option6-subnet-set`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands create a new subnet-specific DHCP option or replace an existing
+option in the database. The structure of the option information is the same as
+in the Kea configuration file (see :ref:`dhcp4-std-options`
+and :ref:`dhcp6-std-options`). The option information is carried in the
+``options`` list. Another list, ``subnets``, contains a map with the identifier of
+the subnet for which the option is to be set. If such an option already exists
+for the subnet, it is replaced with the new instance.
+
+.. code-block:: json
+
+ {
+ "command": "remote-option6-subnet-set",
+ "arguments": {
+ "subnets": [
+ {
+ "id": 123
+ }
+ ],
+ "options": [
+ {
+ "name": "dns-servers",
+ "data": "2001:db8:1::1"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The ``server-tags`` parameter cannot be specified for this command.
+
+Specifying an option name instead of the option code only works reliably
+for the standard DHCP options. When specifying a value for the user-defined
+DHCP option, the option code should be indicated instead of the name.
+
+.. isccmd:: remote-subnet4-del-by-id
+.. _command-remote-subnet4-del-by-id:
+
+.. isccmd:: remote-subnet6-del-by-id
+.. _command-remote-subnet6-del-by-id:
+
+The ``remote-subnet4-del-by-id``, ``remote-subnet6-del-by-id`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This is the first variant of the commands used to delete an IPv4 or IPv6
+subnet from the database. It uses the subnet ID to identify the subnet. For
+example, to delete the IPv4 subnet with an ID of 5:
+
+.. code-block:: json
+
+ {
+ "command": "remote-subnet4-del-by-id",
+ "arguments": {
+ "subnets": [
+ {
+ "id": 5
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The ``server-tags`` parameter cannot be used with this command.
+
+.. isccmd:: remote-subnet4-del-by-prefix
+.. _command-remote-subnet4-del-by-prefix:
+
+.. isccmd:: remote-subnet6-del-by-prefix
+.. _command-remote-subnet6-del-by-prefix:
+
+The ``remote-subnet4-del-by-prefix``, ``remote-subnet6-del-by-prefix`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This is the second variant of the commands used to delete an IPv4 or
+IPv6 subnet from the database. It uses the subnet prefix to identify the
+subnet. For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-subnet6-del-by-prefix",
+ "arguments": {
+ "subnets": [
+ {
+ "subnet": "2001:db8:1::/64"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The ``server-tags`` parameter cannot be used with this command.
+
+.. isccmd:: remote-subnet4-get-by-id
+.. _command-remote-subnet4-get-by-id:
+
+.. isccmd:: remote-subnet6-get-by-id
+.. _command-remote-subnet6-get-by-id:
+
+The ``remote-subnet4-get-by-id``, ``remote-subnet6-get-by-id`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This is the first variant of the commands used to fetch an IPv4 or IPv6
+subnet from the database. It uses a subnet ID to identify the subnet.
+For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-subnet4-get-by-id",
+ "arguments": {
+ "subnets": [
+ {
+ "id": 5
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The ``server-tags`` parameter cannot be used with this command.
+
+.. isccmd:: remote-subnet4-get-by-prefix
+.. _command-remote-subnet4-get-by-prefix:
+
+.. isccmd:: remote-subnet6-get-by-prefix
+.. _command-remote-subnet6-get-by-prefix:
+
+The ``remote-subnet4-get-by-prefix``, ``remote-subnet6-get-by-prefix`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This is the second variant of the commands used to fetch an IPv4 or IPv6
+subnet from the database. It uses a subnet prefix to identify the
+subnet. For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-subnet6-get-by-prefix",
+ "arguments": {
+ "subnets": [
+ {
+ "subnet": "2001:db8:1::/64"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The ``server-tags`` parameter cannot be used with this command.
+
+.. isccmd:: remote-subnet4-list
+.. _command-remote-subnet4-list:
+
+.. isccmd:: remote-subnet6-list
+.. _command-remote-subnet6-list:
+
+The ``remote-subnet4-list``, ``remote-subnet6-list`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to list all IPv4 or IPv6 subnets from the database for
+selected servers or all servers. The following command retrieves all servers to
+be used by "server1" and "server2":
+
+.. code-block:: json
+
+ {
+ "command": "remote-subnet4-list",
+ "arguments": {
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "server1", "server2" ]
+ }
+ }
+
+The ``server-tags`` parameter is mandatory and contains one or
+more server tags. It may contain the keyword "all", to fetch the subnets
+associated with all servers. When the ``server-tags`` list
+contains the ``null`` value, the returned response contains a list
+of unassigned subnets, i.e. the subnets which are associated with no servers.
+For example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-subnet4-list",
+ "arguments": {
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ null ]
+ }
+ }
+
+The example response to this command when non-null server tags are specified
+looks similar to this:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "2 IPv4 subnet(s) found.",
+ "arguments": {
+ "subnets": [
+ {
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "shared-network-name": null,
+ "metadata": {
+ "server-tags": [ "server1", "server2" ]
+ }
+ },
+ {
+ "id": 2,
+ "subnet": "192.0.3.0/24",
+ "shared-network-name": null,
+ "metadata": {
+ "server-tags": [ "all" ]
+ }
+ }
+ ],
+ "count": 2
+ }
+ }
+
+The returned information about each subnet is limited to the subnet identifier,
+prefix, and associated shared network name. To retrieve full
+information about the selected subnet, use
+the :isccmd:`remote-subnet4-get-by-id`, :isccmd:`remote-subnet6-get-by-id` commands
+or the :isccmd:`remote-subnet4-get-by-prefix`, :isccmd:`remote-subnet6-get-by-prefix` commands.
+
+The example response above contains two subnets. One of the subnets is
+associated with both servers: "server1" and "server2". The second subnet is
+associated with all servers, so it is also present in the configurations for
+"server1" and "server2".
+
+When listing unassigned subnets, the response will look similar to this:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "1 IPv4 subnet(s) found.",
+ "arguments": {
+ "subnets": [
+ {
+ "id": 3,
+ "subnet": "192.0.4.0/24",
+ "shared-network-name": null,
+ "metadata": {
+ "server-tags": [ null ]
+ }
+ }
+ ],
+ "count": 1
+ }
+ }
+
+The ``null`` value in the metadata indicates that the
+returned subnet is unassigned.
+
+.. isccmd:: remote-subnet4-set
+.. _command-remote-subnet4-set:
+
+.. isccmd:: remote-subnet6-set
+.. _command-remote-subnet6-set:
+
+The ``remote-subnet4-set``, ``remote-subnet6-set`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to create a new IPv4 or IPv6 subnet or replace
+an existing subnet in the database. Setting the subnet also associates
+or disassociates the subnet with a shared network.
+
+The structure of the subnet information is similar to the structure used
+in the configuration file (see :ref:`dhcp4-configuration` and
+:ref:`dhcp6-configuration`). The subnet information conveyed in the
+:isccmd:`remote-subnet4-set`, :isccmd:`remote-subnet6-set` commands
+must include the additional parameter
+``shared-network-name``, which denotes whether the subnet belongs to a
+shared network.
+
+Consider the following example:
+
+.. code-block:: json
+
+ {
+ "command": "remote-subnet4-set",
+ "arguments": {
+ "subnets": [
+ {
+ "id": 5,
+ "subnet": "192.0.2.0/24",
+ "shared-network-name": "level3",
+ "pools": [ { "pool": "192.0.2.100-192.0.2.200" } ],
+ "option-data": [ {
+ "name": "routers",
+ "data": "192.0.2.1"
+ } ]
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "all" ]
+ }
+ }
+
+It creates the subnet and associates it with the "level3" shared
+network. The "level3" shared network must be created with the :isccmd:`remote-network4-set`
+command prior to creating the subnet.
+
+If the created subnet must be global - that is, not associated with any shared
+network - the ``shared-network-name`` must be explicitly set to
+``null``:
+
+.. code-block:: json
+
+ {
+ "command": "remote-subnet4-set",
+ "arguments": {
+ "subnets": [
+ {
+ "id": 5,
+ "subnet": "192.0.2.0/24",
+ "shared-network-name": null,
+ "pools": [ { "pool": "192.0.2.100-192.0.2.200" } ],
+ "option-data": [ {
+ "name": "routers",
+ "data": "192.0.2.1"
+ } ]
+ }
+ ],
+ "server-tags": [ "all" ]
+ }
+ }
+
+The subnet created in the previous example is replaced with the new
+subnet having the same parameters, but it becomes global.
+
+The ``shared-network-name`` parameter is mandatory for the
+:isccmd:`remote-subnet4-set` command. The ``server-tags`` list is mandatory and must
+include one or more server tags. As a result, the subnet is associated with all
+of the listed servers. It may also be associated with all servers connecting
+to the database when the keyword "all" is used as the server tag.
+
+.. note::
+
+ As with other "set" commands, this command replaces all the
+ information about the particular subnet in the database, if the
+ subnet information is already present. Therefore, when sending this
+ command, make sure to always include all parameters that must be
+ specified for the updated subnet instance. Any unspecified parameter
+ will be marked as unspecified in the database, even if its value was
+ present prior to sending the command.
+
+.. isccmd:: remote-class4-del
+.. _command-remote-class4-del:
+
+.. isccmd:: remote-class6-del
+.. _command-remote-class6-del:
+
+The ``remote-class4-del``, ``remote-class6-del`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands delete a DHCPv4 or DHCPv6 client class by name. If any client
+classes in the database depend on the deleted class, an error is returned in
+response to this command. In this case, to successfully delete the class,
+the dependent client classes must be deleted first. Use the
+:isccmd:`remote-class4-get-all` command to fetch all client classes and find
+the dependent ones.
+
+.. code-block:: json
+
+ {
+ "command": "remote-class4-del",
+ "arguments": {
+ "client-classes": [
+ {
+ "name": "foo"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The ``server-tags`` parameter cannot be used for this command because client
+classes are uniquely identified by name.
+
+.. isccmd:: remote-class4-get
+.. _command-remote-class4-get:
+
+.. isccmd:: remote-class6-get
+.. _command-remote-class6-get:
+
+The ``remote-class4-get``, ``remote-class6-get`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands retrieve DHCPv4 or DHCPv6 client class information by a
+client-class name.
+
+.. code-block:: json
+
+ {
+ "command": "remote-class4-get",
+ "arguments": {
+ "client-classes": [
+ {
+ "name": "foo"
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ }
+ }
+ }
+
+The ``server-tags`` parameter cannot be used for this command because client
+classes are uniquely identified by name.
+
+A response to the command looks similar to this:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "DHCPv4 client class 'foo' found.",
+ "arguments": {
+ "client-classes": [
+ {
+ "name": "foo",
+ "metadata": {
+ "server-tags": [ "all" ]
+ }
+ }
+ ],
+ "count": 1
+ }
+ }
+
+.. isccmd:: remote-class4-get-all
+.. _command-remote-class4-get-all:
+
+.. isccmd:: remote-class6-get-all
+.. _command-remote-class6-get-all:
+
+The ``remote-class4-get-all``, ``remote-class6-get-all`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands retrieve all DHCPv4 or DHCPv6 client classes for a particular server,
+multiple explicitly listed servers, and/or all servers. A given server has its own
+server-specific tag and also has the "all" server tag; these commands retrieve
+the classes for both an individual server and for "all" servers. For example, the
+following command retrieves all client classes defined for "server1" as well as
+the client classes defined for "all" servers:
+
+.. code-block:: json
+
+ {
+ "command": "remote-class4-get-all",
+ "arguments": {
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "server1" ]
+ }
+ }
+
+The ``server-tags`` parameter is mandatory and contains one or more server
+tags. If other server tags are specified, "all" does not need to be included
+in ``server-tags``, as every server automatically also has the "all" server tag.
+If ``server-tags`` contains only the keyword "all", only the client classes associated
+with "all" servers are returned. When the ``server-tags`` list contains the
+``null`` value, the returned response contains a list of unassigned client
+classes, i.e. the networks which are associated with no servers.
+
+A response to the command looks similar to this:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "2 DHCPv4 client class(es) found.",
+ "arguments": {
+ "client-classes": [
+ {
+ "name": "foo",
+ "metadata": {
+ "server-tags": [ "all" ]
+ }
+ },
+ {
+ "name": "bar",
+ "test": "member('foo')",
+ "metadata": {
+ "server-tags": [ "all" ]
+ }
+ }
+ ],
+ "count": 2
+ }
+ }
+
+.. isccmd:: remote-class4-set
+.. _command-remote-class4-set:
+
+.. isccmd:: remote-class6-set
+.. _command-remote-class6-set:
+
+The ``remote-class4-set``, ``remote-class6-set`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands insert a new or replace an existing DHCPv4 or DHCPv6 client class in
+the database. The client class information structure is the same as in the Kea
+configuration file (see :ref:`dhcp4-client-classifier` and
+:ref:`dhcp6-client-classifier` for details).
+
+.. code-block:: json
+
+ {
+ "command": "remote-class4-set",
+ "arguments": {
+ "client-classes": [
+ {
+ "name": "foo",
+ "test": "member('KNOWN') or member('bar')",
+ "option-def": [
+ {
+ "name": "configfile",
+ "code": 224,
+ "type": "string"
+ }
+ ],
+ "option-data": [
+ {
+ "name": "configfile",
+ "data": "1APC"
+ }
+ ]
+ }
+ ],
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "all" ]
+ }
+ }
+
+
+Client-class ordering rules described in :ref:`classification-using-expressions`
+apply to the classes inserted into the database. They imply that the class `bar`
+referenced in the test expression must exist in the database when issuing the
+above command.
+
+By default, a new client class is inserted at the end of the class hierarchy in
+the database and can reference any class associated with the same server tag or
+with the special server tag "all". If an existing class is updated, it remains
+at its current position within the class hierarchy.
+
+However, the class commands allow the position of the inserted
+or updated client class to be specified. The optional ``follow-class-name`` parameter can be
+included in the command to indicate the name of the existing class after which
+the managed class should be placed. Suppose there are two DHCPv6 classes in the
+database: `first-class` and `second-class`. To add a new class, `third-class`,
+between these two, use a command similar to the following:
+
+.. code-block:: json
+
+ {
+ "command": "remote-class6-set",
+ "arguments": {
+ "client-classes": [
+ {
+ "name": "third-class",
+ "test": "member('first-class')"
+ }
+ ],
+ "follow-class-name": "first-class",
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "all" ]
+ }
+ }
+
+Note that `third-class` can depend on `first-class` because it is placed
+after `first-class`; `third-class` cannot depend on `second-class`
+because it is placed before it. However, `second-class` could be updated to
+depend on `third-class`.
+
+The ``follow-class-name`` parameter can be explicitly set to ``null``, e.g.:
+
+.. code-block:: json
+
+ {
+ "command": "remote-class6-set",
+ "arguments": {
+ "client-classes": [
+ {
+ "name": "third-class",
+ "test": "member('first-class')"
+ }
+ ],
+ "follow-class-name": null,
+ "remote": {
+ "type": "mysql"
+ },
+ "server-tags": [ "all" ]
+ }
+ }
+
+It yields the same behavior as if the ``follow-class-name`` parameter were not included,
+i.e. the new class is appended at the end of the class hierarchy, and the updated
+class remains at the current position.
diff --git a/doc/sphinx/arm/hooks-cb-mysql.rst b/doc/sphinx/arm/hooks-cb-mysql.rst
new file mode 100644
index 0000000..8e5ab42
--- /dev/null
+++ b/doc/sphinx/arm/hooks-cb-mysql.rst
@@ -0,0 +1,18 @@
+.. ischooklib:: libdhcp_mysql_cb.so
+.. _hooks-cb-mysql:
+
+``libdhcp_mysql_cb.so``: Configuration Backend for MySQL
+========================================================
+
+This hook library works in conjunction with :ischooklib:`libdhcp_cb_cmds.so` to
+implement the API to create, read, update, and delete (CRUD) the
+configuration in a MySQL database. Please see :ref:`hooks-cb-cmds`
+for more details.
+
+.. note::
+
+ :ischooklib:`libdhcp_mysql_cb.so` is part of the open source code and is
+ available to every Kea user, but it requires :ischooklib:`libdhcp_cb_cmds.so`
+ which is available only to ISC customers with
+ a paid support contract. For more information on subscription options,
+ please complete the form at https://www.isc.org/contact.
diff --git a/doc/sphinx/arm/hooks-cb-pgsql.rst b/doc/sphinx/arm/hooks-cb-pgsql.rst
new file mode 100644
index 0000000..c652693
--- /dev/null
+++ b/doc/sphinx/arm/hooks-cb-pgsql.rst
@@ -0,0 +1,18 @@
+.. ischooklib:: libdhcp_pgsql_cb.so
+.. _hooks-cb-pgsql:
+
+``libdhcp_pgsql_cb.so``: Configuration Backend for PostgreSQL
+=============================================================
+
+This hook library works in conjunction with :ischooklib:`libdhcp_cb_cmds.so` to
+implement the API to create, read, update, and delete (CRUD) the
+configuration in a PostgreSQL database. Please see :ref:`hooks-cb-cmds`
+for more details.
+
+.. note::
+
+ :ischooklib:`libdhcp_pgsql_cb.so` is part of the open source code and is
+ available to every Kea user, but it requires :ischooklib:`libdhcp_cb_cmds.so`
+ which is available only to ISC customers with
+ a paid support contract. For more information on subscription options,
+ please complete the form at https://www.isc.org/contact.
diff --git a/doc/sphinx/arm/hooks-class-cmds.rst b/doc/sphinx/arm/hooks-class-cmds.rst
new file mode 100644
index 0000000..5a55cc5
--- /dev/null
+++ b/doc/sphinx/arm/hooks-class-cmds.rst
@@ -0,0 +1,255 @@
+.. ischooklib:: libdhcp_class_cmds.so
+.. _hooks-class-cmds:
+
+``libdhcp_class_cmds.so``: Class Commands
+=========================================
+
+This hook library exposes
+several control commands for manipulating client classes (part of the
+Kea DHCP servers' configurations) without the need to restart those
+servers. Using these commands it is possible to add, update, delete, and
+list the client classes configured for a given server.
+
+.. note::
+
+ :ischooklib:`libdhcp_class_cmds.so` is available only to ISC customers with
+ a paid support contract. For more information on subscription options,
+ please complete the form at https://www.isc.org/contact.
+
+.. note::
+
+ This library can only be loaded by the :iscman:`kea-dhcp4` or
+ :iscman:`kea-dhcp6` process.
+
+.. isccmd:: class-add
+.. _command-class-add:
+
+The ``class-add`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :isccmd:`class-add` command adds a new client class to the DHCP server
+configuration. This class is appended at the end of the list of classes
+used by the server and may depend on any of the already-configured
+client classes.
+
+The following example demonstrates how to add a new client class to the
+DHCPv4 server configuration:
+
+::
+
+ {
+ "command": "class-add",
+ "arguments": {
+ "client-classes": [
+ {
+ "name": "ipxe_efi_x64",
+ "test": "option[93].hex == 0x0009",
+ "next-server": "192.0.2.254",
+ "server-hostname": "hal9000",
+ "boot-file-name": "/dev/null"
+ }
+ ]
+ }
+ }
+
+Note that the ``client-classes`` parameter is a JSON list, but it allows
+only a single client class to be present.
+
+Here is the response to the :isccmd:`class-add` command in our example:
+
+::
+
+ {
+ "result": 0,
+ "text": "Class 'ipxe_efi_x64' added."
+ }
+
+.. isccmd:: class-update
+.. _command-class-update:
+
+The ``class-update`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :isccmd:`class-update` command updates an existing client class in the
+DHCP server configuration. If the client class with the given name
+does not exist, the server returns the result code of 3, which means that
+the server configuration is not modified and the client class does not
+exist. The :isccmd:`class-add` command must be used instead to create the new
+client class.
+
+The :isccmd:`class-update` command has the same argument structure as the
+:isccmd:`class-add` command:
+
+::
+
+ {
+ "command": "class-update",
+ "arguments": {
+ "client-classes": [
+ {
+ "name": "ipxe_efi_x64",
+ "test": "option[93].hex == 0x0017",
+ "next-server": "0.0.0.0",
+ "server-hostname": "xfce",
+ "boot-file-name": "/dev/null"
+ }
+ ]
+ }
+ }
+
+Here is the response for our example:
+
+::
+
+ {
+ "result": 0,
+ "text": "Class 'ipxe_efi_x64' updated."
+ }
+
+Any parameter of the client class can be modified with this command,
+except ``name``. There is currently no way to rename the class, because
+the class name is used as a key for searching the class to be updated.
+To achieve a similar effect to renaming the class, an existing class can
+be removed with the :isccmd:`class-del` command and then added again with a
+different name using :isccmd:`class-add`. Note, however, that the class with
+the new name will be added at the end of the list of configured classes.
+
+As with other update commands, this command overwrites all the contents of the
+entry. If the client class previously had a resource assigned to it, and the
+:isccmd:`class-update` command is missing the resource, it is deleted from the server
+configuration. If an incremental update of the class is desired, then this can
+be achieved by doing a :isccmd:`class-get` to get the current state
+of the client class, picking the client class out of the response, modifying it
+to the required outcome, and then issuing the ``client-update`` command with the
+resulting client class attached.
+
+.. isccmd:: class-del
+.. _command-class-del:
+
+The ``class-del`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+The :isccmd:`class-del` command is used to remove a particular class from the server
+configuration. The class to be removed is identified by name. The class
+is not removed if there are other classes depending on it; to remove
+such a class, the dependent classes must be removed first.
+
+The following is a sample command removing the ``ipxe_efi_x64`` class:
+
+::
+
+ {
+ "command": "class-del",
+ "arguments": {
+ "name": "ipxe_efi_x64"
+ }
+ }
+
+Here is the response to the :isccmd:`class-del` command in our example, when
+the specified client class has been found:
+
+::
+
+ {
+ "result": 0,
+ "text": "Class 'ipxe_efi_x64' deleted."
+ }
+
+If the class does not exist, the result of 3 is returned.
+
+.. isccmd:: class-list
+.. _command-class-list:
+
+The ``class-list`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+:isccmd:`class-list` is used to retrieve a list of all client classes. This
+command includes no arguments:
+
+::
+
+ {
+ "command": "class-list"
+ }
+
+Here is the response of the server in our example, including the list of
+client classes:
+
+::
+
+ {
+ "result": 0,
+ "text": "2 classes found",
+ "arguments": {
+ "client-classes": [
+ {
+ "name": "ipxe_efi_x64"
+ },
+ {
+ "name": "pxeclient"
+ }
+ ]
+ }
+ }
+
+Note that the returned list does not contain full class definitions, but
+merely class names. To retrieve full class information, the
+:isccmd:`class-get` command should be used.
+
+.. isccmd:: class-get
+.. _command-class-get:
+
+The ``class-get`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`class-get` is used to retrieve detailed information about a specified
+class. The command structure is very simple:
+
+::
+
+ {
+ "command": "class-get",
+ "arguments": {
+ "name": "pxeclient"
+ }
+ }
+
+If the class with the specified name does not exist, the status code of
+3 is returned. If the specified client class exists, the class details
+are returned in the following format:
+
+::
+
+ {
+ "result": 0,
+ "text": "Class 'pxeclient' definition returned",
+ "arguments": {
+ "client-classes": [
+ {
+ "name": "pxeclient",
+ "only-if-required": true,
+ "test": "option[vendor-class-identifier].text == 'PXEClient'",
+ "option-def": [
+ {
+ "name": "configfile",
+ "code": 209,
+ "type": "string"
+ }
+ ],
+ "option-data": [ ],
+ "next-server": "0.0.0.0",
+ "server-hostname": "xfce",
+ "boot-file-name": "/dev/null"
+ }
+ ]
+ }
+ }
+
+Note that the example above is DHCPv4-specific; the last three
+parameters are only returned by the DHCPv4 server and are never returned
+by the DHCPv6 server. Also, some of the parameters provided in this
+example may not be returned if they are not specified for the class.
+Specifically, ``only-if-required``, ``test``, and ``option-def`` are not
+returned if they are not specified for the class.
diff --git a/doc/sphinx/arm/hooks-ddns-tuning.rst b/doc/sphinx/arm/hooks-ddns-tuning.rst
new file mode 100644
index 0000000..72b2a49
--- /dev/null
+++ b/doc/sphinx/arm/hooks-ddns-tuning.rst
@@ -0,0 +1,217 @@
+.. ischooklib:: libdhcp_ddns_tuning.so
+.. _hooks-ddns-tuning:
+
+``libdhcp_ddns_tuning.so``: DDNS Tuning
+=======================================
+
+This hook library adds support for fine-tuning various DNS update aspects.
+It currently supports procedural host-name generation and the ability to skip
+performing DDNS updates for select clients.
+
+.. note::
+
+ :ischooklib:`libdhcp_ddns_tuning.so` is available as a premium
+ hook library from ISC. Please visit https://www.isc.org/shop/ to purchase
+ the premium hook libraries, or contact us at https://www.isc.org/contact for
+ more information.
+
+The library, which was added in Kea 2.1.5, can be loaded by the :iscman:`kea-dhcp4`
+and :iscman:`kea-dhcp6` daemons by adding it to the ``hooks-libraries`` element of the
+server's configuration:
+
+.. code-block:: javascript
+
+ {
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/libdhcp_ddns_tuning.so",
+ "parameters": {
+ ...
+ }
+ },
+ ...
+ ],
+ ...
+ }
+
+Procedural Host-Name Generation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This hook library provides the ability to generate host names procedurally, based on
+an expression. The expression can be defined globally in the hook parameters, using
+`hostname-expr`. If defined globally, it applies to all hosts in all subnets. The
+expressions can use all tokens defined in :ref:`classify`. An example of a global
+expression is shown below:
+
+.. code-block:: javascript
+
+ {
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/libdhcp_ddns_tuning.so",
+ "parameters": {
+ "hostname-expr": "'host-'+hexstring(pkt4.mac,'-')",
+ ...
+ }
+ },
+ ...
+ ],
+ ...
+ }
+
+It is also possible to define this parameter in a subnet, using the user-context mechanism.
+If defined at the subnet level, the expression applies to a specific subnet only. If the
+subnet expression is defined as empty, ``""``, it suppresses (or disables) the use of a
+global expression for that subnet. An example subnet expression is shown below:
+
+.. code-block:: javascript
+
+ {
+ "subnet4": [{
+ "subnet": "192.0.2.0/24",
+ "pools": [{
+ "pool": "192.0.2.10 - 192.0.2.20"
+ } ],
+
+ // This is a subnet-specific user context.
+ "user-context": {
+ "ddns-tuning": {
+ "hostname-expr": "'guest-'+int8totext(substring(pkt4.yiaddr, 0,1))+'-' \
+ +int8totext(substring(pkt4.yiaddr, 1,2))+'-' \
+ +int8totext(substring(pkt4.yiaddr, 2,3))+'-' \
+ +int8totext(substring(pkt4.yiaddr, 3,4))"
+ },
+ "last-modified": "2017-09-04 13:32",
+ "description": "you can put anything you like here",
+ "phones": [ "x1234", "x2345" ],
+ "devices-registered": 42,
+ "billing": false
+ }
+ }],
+ ...
+ }
+
+.. note::
+
+ The expression value above uses a backslash, ``\``, to show line continuation. This is for
+ clarity only and is not valid JSON supported by Kea parsing. The actual value must
+ be expressed on a single line.
+
+.. note::
+
+ Privacy should be taken into consideration when generating a host name. The host name
+ is usually inserted into the DNS, which is a public system. Exposing identifiers that
+ can be used to track devices, such as a MAC address, are usually a very bad idea.
+ The global expression example here used a MAC address for simplicity.
+
+DHCPv4 Host-Name Generation
+---------------------------
+
+With this library installed, the behavior for :iscman:`kea-dhcp4` when forming host names in
+response to a client query (e.g. DISCOVER, REQUEST) is as follows:
+
+ 1. If a host name is supplied via a host reservation, use it with the DDNS
+ behavioral parameters to form the final host name. Go to step 4.
+
+ 2. If the client supplied an FQDN option (option 81), use the domain name value
+ specified within it, with the DDNS behavioral parameters, to form the final
+ host name. Go to step 4.
+
+ 3. If the client supplied a host-name option (option 12), use the host name specified
+ within it, with the DDNS behavioral parameters, to form the final host name.
+
+ 4. If there is a ``ddns-tuning`` in-scope host-name expression (either global or subnet),
+ calculate the host name using the expression. If the calculated value is not a fully
+ qualified name and there is an in-scope ``ddns-qualifying-suffix``, append the suffix.
+
+ 5. If the value calculated by the hook is not an empty string and is different than
+ the host name formed in steps 1 or 2, the calculated value becomes the
+ final host name.
+
+DHCPv6 Host-Name Generation
+---------------------------
+
+With this library installed, the behavior for :iscman:`kea-dhcp6` when forming host names in
+response to a client query (e.g. SOLICIT, REQUEST, RENEW, REBIND) is as follows:
+
+ 1. If the client supplied an FQDN option (option 39), use the domain name value
+ specified within it, with the DDNS behavioral parameters, to form the final
+ host name. Go to step 4.
+
+ 2. If the client did not supply an FQDN but ``ddns-replace-client-name`` is either
+ ``always`` or ``when-not-present``, then calculate the final form of the host
+ name and use it to create an outbound FQDN. Go to step 4.
+
+ 3. If there is no outbound FQDN at this point, client-name processing for this
+ packet stops. Without an outbound FQDN there is no way to communicate a host
+ name to the client.
+
+ 4. If a host name is supplied via a host reservation, use it along with the DDNS
+ behavioral parameters to form the final host name; it supersedes the FQDN value
+ calculated in steps 1 or 2.
+
+ 5. If there is a ``ddns-tuning`` in-scope host name expression (either global or subnet),
+ calculate the host name using the expression. If the calculated value is not a fully
+ qualified name and there is an in-scope ``ddns-qualifying-suffix``, append the suffix.
+
+ 6. If the value calculated by the hook is not an empty string and is different than
+ the host name formed in steps 1 or 2, the calculated value becomes the
+ final host name.
+
+
+Skipping DDNS Updates
+~~~~~~~~~~~~~~~~~~~~~
+
+:ischooklib:`libdhcp_ddns_tuning.so` also provides the ability to skip DDNS updates on a
+per-client basis. The library recognizes a special client class, "SKIP_DDNS"; when a
+client is matched to this class, the Kea servers (:iscman:`kea-dhcp4` and :iscman:`kea-dhcp6`) do not
+send DDNS update requests (NCRs) to :iscman:`kea-dhcp-ddns`. A common use case would be
+to skip DDNS updates for fixed-address host reservations. This is done easily by
+simply assigning the class to the host reservation as shown below:
+
+.. code-block:: javascript
+
+ {
+ "reservations": [
+ {
+ "hw-address": "01:02:03:04:05:06",
+ "ip-address": "192.0.2.1",
+ "client-classes": [ "SKIP_DDNS", "foo", "bar" ]
+ }]
+ }
+
+The :ischooklib:`libdhcp_ddns_tuning.so` hook library notes the
+presence of the ``"SKIP_DDNS"`` class in the
+client's class list each time the client requests, renews, or releases its lease,
+and instructs :iscman:`kea-dhcp4` to bypass sending DDNS updates. A similar workflow is
+supported for :iscman:`kea-dhcp6`:
+
+.. code-block:: javascript
+
+ {
+ "reservations": [
+ {
+ "duid": "01:02:03:04:05:06",
+ "ip-address": "2001:db8::1",
+ "client-classes": [ "SKIP_DDNS", "foo", "bar" ]
+ }]
+ }
+
+Although "SKIP_DDNS" is a special class, it can be defined with a test
+expression. Defining it as shown below would omit DDNS updates for all KNOWN
+clients:
+
+.. code-block:: javascript
+
+ {
+ "client-classes":[
+ {
+ "name": "SKIP_DDNS",
+ "test": "member('KNOWN')"
+ }]
+ }
+
+.. note::
+
+ The :ischooklib:`libdhcp_ddns_tuning.so` hook library must be
+ loaded for the ``"SKIP_DDNS"`` class to have an effect.
diff --git a/doc/sphinx/arm/hooks-flex-id.rst b/doc/sphinx/arm/hooks-flex-id.rst
new file mode 100644
index 0000000..d485f0b
--- /dev/null
+++ b/doc/sphinx/arm/hooks-flex-id.rst
@@ -0,0 +1,286 @@
+.. ischooklib:: libdhcp_flex_id.so
+.. _hooks-flex-id:
+
+``libdhcp_flex_id.so``: Flexible Identifier for Host Reservations
+=================================================================
+
+The Kea software provides a way to handle
+host reservations that include addresses, prefixes, options, client
+classes, and other features. The reservation can be based on hardware
+address, DUID, circuit-id, or client-id in DHCPv4 and on hardware
+address or DUID in DHCPv6. However, there are sometimes scenarios where
+the reservation is more complex; it may use options other than those mentioned
+above, use parts of specific options, or perhaps even use a combination of
+several options and fields to uniquely identify a client. Those
+scenarios are addressed by the Flexible Identifiers hook application.
+
+.. note::
+
+ :ischooklib:`libdhcp_flex_id.so` is available as a premium
+ hook library from ISC. Please visit https://www.isc.org/shop/ to purchase
+ the premium hook libraries, or contact us at https://www.isc.org/contact for
+ more information.
+
+.. note::
+
+ This library can only be loaded by the :iscman:`kea-dhcp4` or :iscman:`kea-dhcp6`
+ process.
+
+:ischooklib:`libdhcp_flex_id.so` allows the definition of an expression, using notation initially
+used only for client classification. (See
+:ref:`classification-using-expressions` for a detailed description of
+the syntax available.) One notable difference is that for client
+classification, the expression currently has to evaluate to either ``true``
+or ``false``, while the flexible identifier expression is expected to
+evaluate to a string that will be used as an identifier. It is a valid case
+for the expression to evaluate to an empty string (e.g. in cases where a
+client does not send specific options). This expression is then
+evaluated for each incoming packet, and this evaluation generates an
+identifier that is used to identify the client. In particular, there may
+be host reservations that are tied to specific values of the flexible
+identifier.
+
+The library can be loaded similarly to other hook libraries. It
+takes a mandatory parameter ``identifier-expression`` and some optional boolean
+parameters like ``replace-client-id`` and ``ignore-iaid``:
+
+::
+
+ "Dhcp6": {
+ "hooks-libraries": [
+ {
+ "library": "/path/libdhcp_flex_id.so",
+ "parameters": {
+ "identifier-expression": "expression",
+ "replace-client-id": false,
+ "ignore-iaid": false
+ }
+ },
+ ...
+ ]
+ }
+
+The flexible identifier library supports both DHCPv4 and DHCPv6.
+
+Let's consider a case of an IPv6 network that has an
+independent interface for each of its connected customers. Customers are
+able to plug in whatever device they want, so any type of identifier
+(e.g. a client-id) is unreliable. Therefore, the operator may decide to
+use an option inserted by a relay agent to differentiate between
+clients. In this particular deployment, the operator has verified that the
+interface-id is unique for each customer-facing interface, so it
+is suitable for usage as a reservation. However, only the first six bytes of
+the interface-id are interesting, because the remaining bytes are either
+randomly changed or not unique between devices. Therefore, the customer
+decides to use the first six bytes of the interface-id option inserted by the
+relay agent. After adding ``flex-id``, the ``host-reservation-identifiers`` goal
+can be achieved by using the following configuration:
+
+::
+
+ "Dhcp6": {
+ "subnet6": [{
+ # subnet definition starts here
+ "reservations": [{
+ "flex-id": "'port1234'",
+ # value of the first 8 bytes of the interface-id
+ "ip-addresses": [ "2001:db8::1" ]
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ # end of subnet definitions
+ "host-reservation-identifiers": ["duid", "flex-id"],
+ # add "flex-id" to reservation identifiers
+ "hooks-libraries": [
+ {
+ "library": "/path/libdhcp_flex_id.so",
+ "parameters": {
+ "identifier-expression": "substring(relay6[0].option[18].hex,0,8)"
+ }
+ },
+ ...
+ ],
+ ...
+ }
+
+.. note::
+
+ Care should be taken when adjusting the expression. If the expression
+ changes, then all the ``flex-id`` values may change, possibly rendering
+ all reservations based on ``flex-id`` unusable until they are manually updated.
+ It is strongly recommended that administrators start with the expression and a
+ handful of reservations, and then adjust the expression as needed. Once
+ the desired result is obtained with the expression, host reservations
+ can be deployed on a broader scale.
+
+``flex-id`` values in host reservations can be specified in two ways. First,
+they can be expressed as a hex string, e.g. the string "bar" can be represented
+as 626174. Alternatively, it can be expressed as a quoted value (using
+double and single quotes), e.g. "'bar'". The former is more convenient
+for printable characters, while hex string values are more convenient
+for non-printable characters and do not require the use of the
+``hexstring`` operator.
+
+::
+
+ "Dhcp6": {
+ "subnet6": [{
+ # subnet definition starts here
+ "reservations": [{
+ "flex-id": "01:02:03:04:05:06",
+ # value of the first 8 bytes of the interface-id
+ "ip-addresses": [ "2001:db8::1" ]
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ # end of subnet definitions
+ "host-reservation-identifiers": ["duid", "flex-id"],
+ # add "flex-id" to reservation identifiers
+ "hooks-libraries": [
+ {
+ "library": "/path/libdhcp_flex_id.so",
+ "parameters": {
+ "identifier-expression": "vendor[4491].option[1026].hex"
+ }
+ },
+ ...
+ ],
+ ...
+ }
+
+.. note::
+
+ One less common scenario where the examples above may prove useful is for
+ DHCPv6 clients that change their DUIDs between exchanges. Certain PXE
+ clients are known to behave this way.
+
+The ``replace-client-id`` Flag
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When ``replace-client-id`` is set to ``false`` (which is the default setting),
+:ischooklib:`libdhcp_flex_id.so` uses the evaluated flexible identifier solely for
+identifying host reservations, i.e. searching for reservations within a
+database. This is the functional equivalent of other identifiers, similar
+to hardware address or circuit-id. However, this mode of operation
+implies that if a client device is replaced, it may cause a
+conflict between an existing lease (allocated to the old device) and the
+new lease being allocated to the new device. The conflict arises
+because the same flexible identifier is computed for the replaced device,
+so the server will try to allocate the same lease. The mismatch between
+client identifiers sent by the new device and the old device causes the server
+to refuse this new allocation until the old lease expires. A
+manifestation of this problem is dependent on the specific expression used
+as the flexible identifier, and is likely to appear if only options
+and other parameters are used that identify where the device is connected
+(e.g. circuit-id), rather than the device identification itself (e.g.
+MAC address).
+
+:ischooklib:`libdhcp_flex_id.so` offers a way to overcome the problem with lease
+conflicts by dynamically replacing the client identifier (or DUID in DHCPv6)
+with a value derived from the flexible identifier. The server
+processes the client's query as if the flexible identifier were sent in the
+client identifier (or DUID) option. This guarantees that a returning
+client (for which the same flexible identifier is evaluated) will be
+assigned the same lease, despite the client identifier and/or MAC address
+change.
+
+The following is a stub configuration that enables this behavior:
+
+::
+
+ "Dhcp4": {
+ "hooks-libraries": [
+ {
+ "library": "/path/libdhcp_flex_id.so",
+ "parameters": {
+ "identifier-expression": "expression",
+ "replace-client-id": true
+ }
+ },
+ ...
+ ]
+ }
+
+In the DHCPv4 case, the value derived from the flexible identifier is
+formed by prepending one byte with a value of zero to the flexible identifier.
+In the DHCPv6 case, it is formed by prepending two zero bytes before the
+flexible identifier.
+
+Note that for this mechanism to take effect, the DHCPv4 server must be
+configured to respect the client identifier option value during lease
+allocation, i.e. ``match-client-id`` must be set to ``true``. See
+:ref:`dhcp4-match-client-id` for details. No additional settings are
+required for DHCPv6.
+
+If the ``replace-client-id`` option is set to ``true``, the value of the
+``echo-client-id`` parameter (which governs whether to send back a
+client-id option) is ignored.
+
+The :ref:`hooks-lease-cmds` section describes commands used to retrieve,
+update, and delete leases using various identifiers, such as ``hw-address`` and
+``client-id``. :ischooklib:`libdhcp_lease_cmds.so` does not natively support querying
+for leases by flexible identifier. However, when ``replace-client-id`` is
+set to ``true``, it makes it possible to query for leases using a value
+derived from the flexible identifier. In DHCPv4, the query
+looks similar to this:
+
+::
+
+ {
+ "command": "lease4-get",
+ "arguments": {
+ "identifier-type": "client-id",
+ "identifier": "00:54:64:45:66",
+ "subnet-id": 44
+ }
+ }
+
+where the hexadecimal value of "54:64:45:66" is a flexible identifier
+computed for the client.
+
+In DHCPv6, the corresponding query looks something like this:
+
+::
+
+ {
+ "command": "lease6-get",
+ "arguments": {
+ "identifier-type": "duid",
+ "identifier": "00:00:54:64:45:66",
+ "subnet-id": 10
+ }
+ }
+
+The ``ignore-iaid`` Flag
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+When ``ignore-iaid`` is set to ``true`` (the default value is ``false``),
+:ischooklib:`libdhcp_flex_id.so` causes the Kea DHCPv6 server to ignore the IAID value
+from incoming IPv6 packets. This parameter is ignored by the Kea DHCPv4 server.
+
+If the packet contains only one IA_NA, the IAID value will be changed to ``0``
+and stored as such in the lease storage. Similarly, if the packet contains only
+one IA_PD, the IAID value will be changed to ``0`` and stored as such in the
+lease storage. The IAID is restored to its initial value in the response back
+to the client. The change is visible in the identifier expression if the IAID is
+part of the expression.
+
+.. note::
+
+ To avoid lease conflicts, if the incoming packet contains more than one
+ IA_NA, the IAID value is not changed on any of the IA_NAs. Similarly,
+ if the incoming packet contains more than one IA_PD, the IAID value is not
+ changed on any of the IA_PDs.
+
+.. warning::
+
+ This functionality breaks RFC compliance and should be enabled only if
+ required. When enabled, a warning message is issued at configure time.
diff --git a/doc/sphinx/arm/hooks-flex-option.rst b/doc/sphinx/arm/hooks-flex-option.rst
new file mode 100644
index 0000000..ffac090
--- /dev/null
+++ b/doc/sphinx/arm/hooks-flex-option.rst
@@ -0,0 +1,196 @@
+.. ischooklib:: libdhcp_flex_option.so
+.. _hooks-flex-option:
+
+``libdhcp_flex_option.so``: Flexible Option Actions for Option Value Settings
+=============================================================================
+
+This library allows administrators to define an action to take, for a given
+option, based upon on the result of an expression. These actions are carried
+out during the final stages of constructing a query response packet, just
+before it is sent to the client. The three actions currently supported are
+``add``, ``supersede``, and ``remove``.
+
+.. note::
+
+ :ischooklib:`libdhcp_flex_option.so` is part of the open source code and is
+ available to every Kea user.
+
+The syntax used for the action expressions is the same syntax used
+for client classification and the Flexible Identifier hook library;
+see either :ref:`classification-using-expressions` or :ref:`hooks-flex-id`
+for a detailed description of the syntax.
+
+The ``add`` and ``supersede`` actions use an expression returning a
+string, and do nothing if the string is empty. The
+``remove`` application uses an expression returning ``true`` or ``false``,
+and does nothing on ``false``. When it is necessary to set an option to the
+empty value this mechanism does not work, but a client class can be
+used instead.
+
+The ``add`` action adds an option only when the option does not already
+exist and the expression does not evaluate to the empty string.
+The ``supersede`` action is similar, but it overwrites the option value
+if it already exists. The ``remove`` action removes the option from
+the response packet if it already exists and the expression evaluates to
+true.
+
+The option to which an action applies may be specified by either its
+numeric code or its name; either the code or the name must be
+specified. The option space is DHCPv4 or DHCPv6, depending
+on the server where the hook library is loaded.
+
+Similar to other hook libraries, :ischooklib:`libdhcp_flex_option.so` can be loaded
+by either the :iscman:`kea-dhcp4` or :iscman:`kea-dhcp6`
+process. It takes a mandatory ``options`` parameter with a list of
+per-option parameter maps, with ``code``, ``name``, ``add``, ``supersede``, and
+``remove`` actions. Action entries take a string value representing an
+expression.
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/libdhcp_flex_option.so",
+ "parameters": {
+ "options": [
+ {
+ "code": 67,
+ "add": "ifelse(option[host-name].exists,concat(option[host-name].text,'.boot'),'')"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+
+If (and only if) the **query** includes a ``host-name`` option (code 12), a
+``boot-file-name`` option (code 67) is added to the response with the host name
+followed by ``.boot`` for content.
+
+A commonly discussed use case is modifying the DHCPv4 subnet mask option
+(code 1). The following example demonstrates that capability. All ingress
+packets identified by the gateway address 192.168.0.1 are met with a /32 subnet
+mask in the response.
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/libdhcp_flex_option.so",
+ "parameters": {
+ "options": [
+ {
+ "code": 1,
+ "supersede": "ifelse(pkt4.giaddr==192.168.0.1, '255.255.255.255', '')"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+
+The flexible option library supports both DHCPv4 and DHCPv6.
+
+Since Kea 1.9.0, the ``add`` and ``supersede`` actions take an optional
+```csv-format``` boolean parameter. If not specified or set to ``false``, the
+option data is set using the raw value of the evaluated expression. When it is
+configured to ``true``, this value is parsed using the option definition from
+the option data specified in the configuration file. This eases option setting
+for options using complex record formats or fully qualified domain names.
+
+For instance, if the expression evaluation returns "example.com" and
+the option is defined with the ``fqdn`` type, the domain name will be
+encoded into DNS binary format.
+
+Since Kea 2.1.4, the ``client-class`` parameter specifies a class guard.
+It takes a client class name. If not empty, the client's packet needs to
+belong to specified class for this entry to be used.
+
+Since Kea 2.1.4, it is allowed to have multiple entries for the same option,
+but each entry must have exactly one action. If the option is not defined
+in the ``dhcp4`` for DHCPv4 or ``dhcp6`` for DHCPv6 you can specify the
+space where to find the option definition using its name with the new
+``space`` parameter.
+
+Since Kea 2.1.4, sub-options are supported with a new entry ``sub-options``
+which replaces the action in the configuration of the container option,
+i.e. the option where sub-options are located.
+
+The ``sub-options`` entry takes a list of sub-option configuration similar
+to the option one with:
+
+- ``code`` - specifies the sub-option code, either the ``code`` or ``name``
+ must be specified. When both are given they must match or the configuration
+ is rejected at load time.
+
+- ``name`` - specifies the sub-option name, either the ``code`` or ``name``
+ must be specified. When both are given they must match or the configuration
+ is rejected at load time.
+
+- ``space`` - specifies the space where the sub-option can be defined. This
+ parameter is optional because it can be found in the container option
+ definition. The configuration is rejected if no valid space name is
+ available at load time. Note that vendor spaces are supported for the
+ DHCPv4 ``vivso-suboptions`` and for the DHCPv6 ``vendor-opts``, both
+ pre-defined (e.g. DoCSIS vendor id 4491) or custom.
+
+- ``add`` - (action) adds a sub-option only if it does not already exist
+ and the expression does not evaluate to the empty string.
+
+- ``supersede`` - (action) adds or overwrites a sub-option if the expression
+ does not evaluate to the empty string.
+
+- ``remove`` - (action) removes a sub-option if it already exists and the
+ expression evaluates to true.
+
+- ``container-add`` - boolean value which specifies if the container option
+ should be created if it does not exit in the ``add`` and ``supersede``
+ action. When not specified, it defaults to true.
+
+- ``container-remove`` - boolean value which specifies if the container option
+ should be deleted if it remains empty after the removal of a sub-option by
+ the ``remove`` action. When not specified, it defaults to true.
+
+- ``csv-format`` - boolean value which specifies if the raw value of the
+ evaluated expression is used (false, default) or parsed using the sub-option
+ definition (true).
+
+- ``client-class`` - specifies if the sub-option entry must be skipped when
+ the **query** does not belong to the specified client class. Note the similar
+ parameter in the container option entry applies to the whole ``sub-options``
+ list.
+
+For instance this configuration adds a string sub-option in the DHCPv4
+``vendor-encapsulated-options`` (code 43) option. Note this option
+in last resort encapsulates the ``vendor-encapsulated-options`` space.
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/libdhcp_flex_option.so",
+ "parameters": {
+ "options": [
+ {
+ "code": 43,
+ "sub-options": [
+ {
+ "code": 1,
+ "add": "'foobar'"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
diff --git a/doc/sphinx/arm/hooks-gss-tsig.rst b/doc/sphinx/arm/hooks-gss-tsig.rst
new file mode 100644
index 0000000..7c4e0b9
--- /dev/null
+++ b/doc/sphinx/arm/hooks-gss-tsig.rst
@@ -0,0 +1,15 @@
+.. ischooklib:: libddns_gss_tsig.so
+.. _hooks-gss-tsig:
+
+``libddns_gss_tsig.so``: Sign DNS Updates With GSS-TSIG
+=======================================================
+
+This hook library allows the :iscman:`kea-dhcp-ddns` server to use
+GSS-TSIG to sign DNS updates. For a full discussion of GSS-TSIG in Kea,
+please see :ref:`gss-tsig`.
+
+.. note::
+
+ :ischooklib:`libddns_gss_tsig.so` is available only to ISC customers with
+ a paid support contract. For more information on subscription options,
+ please complete the form at https://www.isc.org/contact.
diff --git a/doc/sphinx/arm/hooks-ha.rst b/doc/sphinx/arm/hooks-ha.rst
new file mode 100644
index 0000000..9acfffe
--- /dev/null
+++ b/doc/sphinx/arm/hooks-ha.rst
@@ -0,0 +1,2662 @@
+.. ischooklib:: libdhcp_ha.so
+.. _hooks-high-availability:
+
+``libdhcp_ha.so``: High Availability Outage Resilience for Kea Servers
+======================================================================
+
+This hook library can be loaded on a pair of DHCPv4 or DHCPv6 servers, to
+increase the reliability of the DHCP service in the event of an outage on one
+server.
+
+.. note::
+
+ :ischooklib:`libdhcp_ha.so` is part of the open source code and is
+ available to every Kea user. It was previously available only to ISC
+ customers with a paid support contract.
+
+.. note::
+
+ This library can only be loaded by the :iscman:`kea-dhcp4` or :iscman:`kea-dhcp6` process.
+
+High Availability (HA) of the DHCP service is provided by running multiple
+cooperating server instances. If any of these instances becomes unavailable for
+any reason (DHCP software crash, Control Agent software crash, power outage,
+hardware failure), a surviving server instance can continue providing reliable
+service to clients. Many DHCP server implementations include the "DHCP Failover"
+protocol, whose most significant features are communication between the servers,
+partner failure detection, and lease synchronization between the servers.
+However, the DHCPv4 failover standardization process was never completed by the
+IETF. The DHCPv6 failover standard (RFC 8156) was published, but it is complex
+and difficult to use, has significant operational constraints, and is different
+from its v4 counterpart. Although it may be useful to use a "standard" failover
+protocol, most Kea users are simply interested in a working solution which
+guarantees high availability of the DHCP service. Therefore, the Kea HA hook
+library derives major concepts from the DHCP failover protocol but uses its own
+solutions for communication and configuration. It offers its own state machine,
+which greatly simplifies its implementation and generally fits better into Kea,
+and it provides the same features in both DHCPv4 and DHCPv6. This document
+intentionally uses the term "high availability" rather than "failover" to
+emphasize that it is not the failover protocol implementation.
+
+The following sections describe the configuration and operation of the Kea HA
+hook library.
+
+.. _ha-supported-configurations:
+
+Supported Configurations
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+The Kea HA hook library supports three configurations, also known as HA modes:
+``load-balancing``, ``hot-standby``, and ``passive-backup``. In the
+``load-balancing`` mode, two servers respond to DHCP requests. The
+``load-balancing`` function is implemented as described in `RFC
+3074 <https://tools.ietf.org/html/rfc3074>`__, with each server responding to
+half the received DHCP queries. When one of the servers allocates a lease for a
+client, it notifies the partner server over the control channel (via the RESTful
+API), so the partner can save the lease information in its own database. If the
+communication with the partner is unsuccessful, the DHCP query is dropped and
+the response is not returned to the DHCP client. If the lease update is
+successful, the response is returned to the DHCP client by the server which has
+allocated the lease. By exchanging lease updates, both servers get a copy of all
+leases allocated by the entire HA setup, and either server can be switched to
+handle the entire DHCP traffic if its partner becomes unavailable.
+
+In the ``load-balancing`` configuration, one of the servers must be designated
+as ``primary`` and the other as ``secondary``. Functionally, there is no
+difference between the two during normal operation. However, this distinction is
+required when the two servers are started at (nearly) the same time and have to
+synchronize their lease databases. The primary server synchronizes the database
+first. The secondary server waits for the primary server to complete the lease
+database synchronization before it starts the synchronization.
+
+In the ``hot-standby`` configuration, one of the servers is designated as
+``primary`` and the other as ``standby``. During normal operation, the primary
+server is the only one that responds to DHCP requests. The standby server
+receives lease updates from the primary over the control channel; however, it
+does not respond to any DHCP queries as long as the primary is running or, more
+accurately, until the standby considers the primary to be offline. If the
+standby server detects the failure of the primary, it starts responding to all
+DHCP queries.
+
+.. note::
+
+ Operators often wonder whether to use ``load-balancing`` or ``hot-standby``
+ mode. The ``load-balancing`` mode has the benefit of splitting the DHCP load
+ between two instances, reducing the traffic processed by each of them.
+ However, it is not always clear to the operators that using the
+ ``load-balancing`` mode requires manually splitting the address pools between
+ two Kea instances using client classification, to preclude both servers from
+ allocating the same address to different clients.
+ Such a split is not needed in the ``hot-standby`` mode. Thus, the benefit
+ of using ``hot-standby`` over ``load-balancing`` is that the former has a
+ simpler configuration. Conversely, ``load-balancing`` has higher performance
+ potential at the cost of more complex configuration.
+ See :ref:`ha-load-balancing-config` for details on how to split the pools
+ using client classification.
+
+In the configurations described above, both the primary and secondary/standby
+are referred to as ``active`` servers, because they receive lease updates and
+can automatically react to the partner's failures by responding to the DHCP
+queries which would normally be handled by the partner. The HA hook library
+supports another server type/role: ``backup``. The use of a backup server is
+optional, and can be implemented in both ``load-balancing`` and ``hot-standby``
+setup, in addition to the active servers. There is no limit on the number of
+backup servers in the HA setup; however, the presence of backup servers may
+increase the latency of DHCP responses, because not only do active servers send
+lease updates to each other, but also to the backup servers. The active servers
+do not expect acknowledgments from the backup servers before responding to the
+DHCP clients, so the overhead of sending lease updates to the backup servers is
+minimized.
+
+In the last supported configuration, ``passive-backup``, there is only one
+active server and typically one or more backup servers. A ``passive-backup``
+configuration with no backup servers is also accepted, but it is no different
+than running a single server with no HA function at all.
+
+The ``passive-backup`` configuration is used in situations when an administrator
+wants to take advantage of the backup server(s) as an additional storage for
+leases without running the full-blown failover setup. In this case, if the
+primary server fails, the DHCP service is lost; it requires the administrator to
+manually restart the primary to resume DHCP service. The administrator may also
+configure one of the backup servers to provide DHCP service to the clients, as
+these servers should have accurate or nearly accurate information about the
+allocated leases. The major advantage of the ``passive-backup`` mode is that it
+provides some redundancy of the lease information but with better performance of
+the primary server responding to the DHCP queries.
+The primary server does not have to wait for acknowledgments to the lease
+updates from the backup servers before it sends a response to the DHCP client.
+This reduces the response time compared to the ``load-balancing`` and
+``hot-standby`` cases, in which the server responding to the DHCP query has to
+wait for the acknowledgment from the other active server before it can respond
+to the client.
+
+.. note::
+
+ An interesting use case for a single active server running in the
+ ``passive-backup`` mode is a notification service, in which software
+ pretending to be a backup server receives live notifications about allocated
+ and deleted leases from the primary server and can display them on a
+ monitoring screen, trigger alerts, etc.
+
+Clocks on Active Servers
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Synchronized clocks are essential for the HA setup to operate reliably.
+The servers share lease information - via lease updates and during
+synchronization of the databases - including the time when the lease was
+allocated and when it expires. Some clock skew between the servers participating
+in the HA setup usually exists; this is acceptable as long as the clock skew is
+relatively low, compared to the lease lifetimes. However, if the clock skew
+becomes too high, the different lease expiration times on different servers may
+cause the HA system to malfunction. For example, one server may consider a lease
+to be expired when it is actually still valid. The lease reclamation process may
+remove a name associated with this lease from the DNS, causing problems when the
+client later attempts to renew the lease.
+
+Each active server monitors the clock skew by comparing its current time with
+the time returned by its partner in response to the :isccmd:`ha-heartbeat` command. This
+gives a good approximation of the clock skew, although it does not take into
+account the time between the partner sending the response and the receipt of
+this response by the server which sent the :isccmd:`ha-heartbeat` command. If the clock skew
+exceeds 30 seconds, a warning log message is issued. The administrator may
+correct this problem by synchronizing the clocks (e.g. using NTP); the servers
+should notice the clock skew correction and stop issuing the warning.
+
+If the clock skew is not corrected and exceeds 60 seconds, the HA service on
+each of the servers is terminated, i.e. the state machine enters the
+``terminated`` state. The servers will continue to respond to DHCP clients (as
+in the ``load-balancing`` or ``hot-standby`` mode), but will exchange neither
+lease updates nor heartbeats and their lease databases will diverge. In this
+case, the administrator should synchronize the clocks and restart the servers.
+
+.. note::
+
+ It is possible to restart the servers one at a time, in no particular order.
+ The clocks must be in sync before restarting the servers.
+
+.. note::
+
+ The clock skew is only assessed between two active servers, and only the
+ active servers enter the ``terminated`` state if the skew is too high. The
+ clock skew between active and backup servers is not assessed, because active
+ servers do not exchange heartbeat messages with backup servers.
+
+.. _ha-https-support:
+
+HTTPS Support
+~~~~~~~~~~~~~
+
+Since Kea 1.9.7, the High Availability hook library supports HTTPS via TLS, as
+described in :ref:`tls`.
+
+The HTTPS configuration parameters are:
+
+- ``trust-anchor`` - specifies the name of a file or directory where the
+ certification authority certificate of a Control Agent can be found.
+
+- ``cert-file`` - specifies the name of the file containing the end-entity
+ certificate to use.
+
+- ``key-file`` - specifies the private key of the end-entity certificate to use.
+
+These parameters can be configured at the global and peer levels. When
+configured at both levels the peer value is used, allowing common values to be
+shared.
+
+The three parameters must be either all not specified (HTTPS disabled) or all
+specified (HTTPS enabled). Specification of the empty string is considered not
+specified; this can be used, for instance, to disable HTTPS for a particular
+peer when it is enabled at the global level.
+
+As the High Availability hook library is an HTTPS client, there is no
+``cert-required`` parameter in this hook configuration.
+This parameter can be set in the Control Agent to require and verify a client
+certificate in client-server communication. It does not affect communication
+between HA peers at the client side; see below for information on the server
+side.
+
+Before Kea 2.1.7 using HTTPS in the HA setup required use of the Control Agent
+on all peers. (See :ref:`tls` for Control Agent TLS configuration).
+
+Since Kea 2.1.7 the HTTPS server side is supported:
+
+- the peer entry for the server name is used for the TLS setting.
+
+- the new ``require-client-certs`` parameter specifies whether client
+ certificates are required and verified, i.e. like ``cert-required``. It
+ defaults to ``true`` and is an HA config (vs. peer config) parameter.
+
+Kea 2.1.7 added a new security feature with the ``restrict-commands`` HA config
+parameter: when set to ``true``, commands which are not used by the hook are
+rejected. The default is ``false``.
+
+The following is an example of an HA server pair and Control Agent configuration
+for ``hot-standby`` with TLS.
+
+Server 1:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "hooks-libraries": [{
+ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ }, {
+ "library": "/usr/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ "high-availability": [{
+ "this-server-name": "server1",
+ "trust-anchor": "/usr/lib/kea/CA.pem",
+ "cert-file": "/usr/lib/kea/server1_cert.pem",
+ "key-file": "/usr/lib/kea/server1_key.pem",
+ "mode": "hot-standby",
+ "heartbeat-delay": 10000,
+ "max-response-delay": 60000,
+ "max-ack-delay": 5000,
+ "max-unacked-clients": 5,
+ "peers": [{
+ "name": "server1",
+ "url": "http://192.168.56.33:8000/",
+ "role": "primary",
+ "auto-failover": true
+ }, {
+ "name": "server2",
+ "url": "http://192.168.56.66:8000/",
+ "role": "standby",
+ "auto-failover": true
+ }]
+ }]
+ }
+ }],
+
+ "subnet4": [{
+ "id": 1,
+ "subnet": "192.0.3.0/24",
+ "pools": [{
+ "pool": "192.0.3.100 - 192.0.3.250"
+ }]
+ }]
+ }
+ }
+
+Server 2:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "hooks-libraries": [{
+ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ }, {
+ "library": "/usr/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ "high-availability": [{
+ "this-server-name": "server2",
+ "trust-anchor": "/usr/lib/kea/CA.pem",
+ "cert-file": "/usr/lib/kea/server2_cert.pem",
+ "key-file": "/usr/lib/kea/server2_key.pem",
+ "mode": "hot-standby",
+ "heartbeat-delay": 10000,
+ "max-response-delay": 60000,
+ "max-ack-delay": 5000,
+ "max-unacked-clients": 5,
+ "peers": [{
+ "name": "server1",
+ "url": "http://192.168.56.33:8000/",
+ "role": "primary",
+ "auto-failover": true
+ }, {
+ "name": "server2",
+ "url": "http://192.168.56.66:8000/",
+ "role": "standby",
+ "auto-failover": true
+ }]
+ }]
+ }
+ }],
+
+ "subnet4": [{
+ "id": 1,
+ "subnet": "192.0.3.0/24",
+ "pools": [{
+ "pool": "192.0.3.100 - 192.0.3.250"
+ }]
+ }]
+ }
+ }
+
+Control Agent on Server 1:
+::
+
+ {
+ "Control-agent": {
+ "http-host": "192.168.56.33",
+ "http-port": 8000,
+ "control-sockets": {
+ "dhcp4": {
+ "socket-type": "unix",
+ "socket-name": "/var/run/kea/control_socket"
+ }
+ },
+ "trust-anchor": "/var/lib/kea/CA.pem",
+ "cert-file": "/var/lib/kea/server1_cert.pem",
+ "key-file": "/var/lib/kea/server1_key.pem",
+ "cert-required": true
+ }
+ }
+
+Control Agent on Server 2:
+::
+
+ {
+ "Control-agent": {
+ "http-host": "192.168.56.66",
+ "http-port": 8000,
+ "control-sockets": {
+ "dhcp4": {
+ "socket-type": "unix",
+ "socket-name": "/var/run/kea/control_socket"
+ }
+ },
+ "trust-anchor": "/var/lib/kea/CA.pem",
+ "cert-file": "/var/lib/kea/server2_cert.pem",
+ "key-file": "/var/lib/kea/server2_key.pem",
+ "cert-required": true
+ }
+ }
+
+.. _ha-server-states:
+
+Server States
+~~~~~~~~~~~~~
+
+A DHCP server operating within an HA setup runs a state machine, and the state
+of the server can be retrieved by its peers using the :isccmd:`ha-heartbeat` command
+sent over the RESTful API. If the partner server does not respond to the
+:isccmd:`ha-heartbeat` command within the specified amount of time, the communication
+is considered interrupted and the server may, depending on the configuration,
+use additional measures (described later in this document) to verify that the
+partner is still operating. If it finds that the partner is not operating, the
+server transitions to the ``partner-down`` state to handle all the DHCP traffic
+directed to the system.
+
+In this case, the surviving server continues to send the :isccmd:`ha-heartbeat`
+command to detect when the partner wakes up. At that time, the partner
+synchronizes the lease database. When it is again ready to operate, the
+surviving server returns to normal operation, i.e. the ``load-balancing`` or
+``hot-standby`` state.
+
+The following is the list of all possible server states:
+
+- ``backup`` - normal operation of the backup server. In this state it receives
+ lease updates from the active server(s).
+
+- ``communication-recovery`` - an active server running in ``load-balancing``
+ mode may transition to this state when it experiences communication issues
+ with a partner server over the control channel. This is an intermediate state
+ between the ``load-balancing`` and ``partner-down`` states. In this state the
+ server continues to respond to DHCP queries but does not send lease updates
+ to the partner; lease updates are queued and are sent when normal
+ communication is resumed. If communication does not resume within the time
+ specified, the primary server then transitions to the ``partner-down`` state.
+ The ``communication-recovery`` state was introduced to ensure reliable DHCP
+ service when both active servers remain operational but the communication
+ between them is interrupted for a prolonged period of time. Either server can
+ be configured to never enter this state by setting the
+ ``delayed-updates-limit`` to 0 (please refer to
+ :ref:`ha-load-balancing-config`, later in this chapter, for details on this
+ parameter). Disabling entry into the ``communication-recovery`` state causes
+ the server to begin testing for the ``partner-down`` state as soon as the
+ server is unable to communicate with its partner.
+
+.. note::
+
+ In Kea 1.9.4, with the introduction of ``delayed-updates-limit``, the default
+ server's behavior in ``load-balancing`` mode changed. When a server
+ experiences communication issues with its partner, it now enters the
+ ``communication-recovery`` state and queues lease updates until communication
+ is resumed. Prior to Kea 1.9.4, a server that could not communicate with its
+ partner in ``load-balancing`` mode would immediately begin the transition to
+ the ``partner-down`` state.
+
+- ``hot-standby`` - normal operation of the active server running in the
+ ``hot-standby`` mode; both the primary and the standby server are in this
+ state during their normal operation. The primary server responds to DHCP
+ queries and sends lease updates to the standby server and to any backup
+ servers that are present.
+
+- ``load-balancing`` - normal operation of the active server running in the
+ ``load-balancing`` mode; both the primary and the secondary server are in
+ this state during their normal operation. Both servers respond to DHCP
+ queries and send lease updates to each other and to any backup servers that
+ are present.
+
+- ``in-maintenance`` - an active server transitions to this state as a result
+ of being notified by its partner that the administrator requested maintenance
+ of the HA setup. The administrator requests the maintenance by sending the
+ :isccmd:`ha-maintenance-start` command to the server which is supposed to take over
+ the responsibility for responding to the DHCP clients while the other server
+ is taken offline for maintenance. If the server is in the ``in-maintenance``
+ state it can be safely shut down. The partner transitions to the
+ ``partner-down`` state immediately after discovering that the server in
+ maintenance has been shut down.
+
+- ``partner-down`` - an active server transitions to this state after detecting
+ that its partner (another active server) is offline. The server does not
+ transition to this state if only a backup server is unavailable. In the
+ ``partner-down`` state the active server responds to all DHCP queries,
+ including those queries which are normally handled by the server that is now
+ unavailable.
+
+- ``partner-in-maintenance`` - an active server transitions to this state
+ after receiving a :isccmd:`ha-maintenance-start` command from the administrator.
+ The server in this state becomes responsible for responding to all DHCP
+ requests. The server sends a :isccmd:`ha-maintenance-notify` command to the partner,
+ which should enter the ``in-maintenance`` state. The server remaining in the
+ ``partner-in-maintenance`` state keeps sending lease updates to the partner
+ until it finds that the partner has stopped responding to those lease updates,
+ heartbeats, or any other commands. In this case, the server in the
+ ``partner-in-maintenance`` state transitions to the ``partner-down`` state
+ and keeps responding to the queries, but no longer sends lease updates.
+
+- ``passive-backup`` - a primary server running in the ``passive-backup`` HA
+ mode transitions to this state immediately after it boots up. The primary
+ server in this state responds to all DHCP traffic and sends lease updates to
+ the backup servers it is connected to. By default, the primary server does
+ not wait for acknowledgments from the backup servers and responds to a DHCP
+ query right after sending lease updates to all backup servers. If any of the
+ lease updates fail, a backup server misses the lease update but the DHCP
+ client is still provisioned. This default configuration can be changed by
+ setting the ``wait-backup-ack`` configuration parameter to ``true``, in which
+ case the primary server always waits for the acknowledgements and drops the
+ DHCP query if sending any of the corresponding lease updates fails. This
+ improves lease database consistency between the primary and the secondary.
+ However, if a communication failure between the active server and any of the
+ backups occurs, it effectively causes the failure of the DHCP service from
+ the DHCP clients' perspective.
+
+- ``ready`` - an active server transitions to this state after synchronizing
+ its lease database with an active partner. This state indicates to the
+ partner (which may be in the ``partner-down`` state) that it should return to
+ normal operation. If and when it does, the server in the ``ready`` state also
+ starts normal operation.
+
+- ``syncing`` - an active server transitions to this state to fetch leases from
+ the active partner and update the local lease database. When in this state,
+ the server issues the :isccmd:`dhcp-disable` command to disable the DHCP service of
+ the partner from which the leases are fetched. The DHCP service is disabled
+ for a maximum time of 60 seconds, after which it is automatically re-enabled,
+ in case the syncing partner was unable to re-enable the service. If the
+ synchronization completes successfully, the synchronizing server issues the
+ :isccmd:`ha-sync-complete-notify` command to notify the partner. In most states,
+ the partner re-enables its DHCP service to continue responding to the DHCP
+ queries. In the ``partner-down`` state, the partner first ensures that
+ communication between the servers is re-established before enabling the DHCP
+ service. The syncing operation is synchronous; the server waits for an answer
+ from the partner and does nothing else while the lease synchronization takes
+ place. A server that is configured not to synchronize the lease database with
+ its partner, i.e. when the ``sync-leases`` configuration parameter is set to
+ ``false``, will never transition to this state. Instead, it transitions
+ directly from the ``waiting`` state to the ``ready`` state.
+
+- ``terminated`` - an active server transitions to this state when the High
+ Availability hook library is unable to further provide reliable service and a
+ manual intervention of the administrator is required to correct the problem.
+ Various issues with the HA setup may cause the server to transition to this
+ state. While in this state, the server continues responding to DHCP clients
+ based on the HA mode selected (``load-balancing`` or ``hot-standby``), but
+ lease updates are not exchanged and heartbeats are not sent. Once a server
+ has entered the ``terminated`` state, it remains in this state until it is
+ restarted. The administrator must correct the issue which caused this
+ situation prior to restarting the server (e.g. synchronize the clocks);
+ otherwise, the server will return to the ``terminated`` state once it finds
+ that the issue persists.
+
+- ``waiting`` - each started server instance enters this state. A backup server
+ transitions directly from this state to the ``backup`` state. An active
+ server sends a heartbeat to its partner to check its state; if the partner
+ appears to be unavailable, the server transitions to the ``partner-down``
+ state. If the partner is available, the server transitions to the ``syncing``
+ or ``ready`` state, depending on the setting of the ``sync-leases``
+ configuration parameter. If both servers appear to be in the ``waiting``
+ state (concurrent startup), the primary server transitions to the next state
+ first. The secondary or standby server remains in the ``waiting`` state until
+ the primary transitions to the ``ready`` state.
+
+.. note::
+
+ Currently, restarting the HA service from the ``terminated`` state requires
+ restarting the DHCP server or reloading its configuration.
+
+Whether the server responds to DHCP queries and which queries it responds to is
+a matter of the server's state, if no administrative action is performed to
+configure the server otherwise. The following table provides the default
+behavior for various states.
+
+The ``DHCP Service Scopes`` denote which group of received DHCP queries the
+server responds to in the given state. The HA configuration must specify a
+unique name for each server within the HA setup. This document uses the
+following convention within the provided examples: "server1" for a primary
+server, "server2" for the secondary or standby server, and "server3" for the
+backup server. In real life any names can be used as long as they remain unique.
+
+An in-depth explanation of the scopes can be found below.
+
+.. table:: Default behavior of the server in various HA states
+
+ +------------------------+-----------------+-----------------+----------------+
+ | State | Server Type | DHCP Service | DHCP Service |
+ | | | | Scopes |
+ +========================+=================+=================+================+
+ | backup | backup server | disabled | none |
+ +------------------------+-----------------+-----------------+----------------+
+ | communication-recovery | primary or | enabled | "HA_server1" |
+ | | secondary | | or |
+ | | (load-balancing | | "HA_server2" |
+ | | mode only) | | |
+ +------------------------+-----------------+-----------------+----------------+
+ | hot-standby | primary or | enabled | "HA_server1" |
+ | | standby | | if primary, |
+ | | (hot-standby | | none otherwise |
+ | | mode) | | |
+ +------------------------+-----------------+-----------------+----------------+
+ | load-balancing | primary or | enabled | "HA_server1" |
+ | | secondary | | or |
+ | | (load-balancing | | "HA_server2" |
+ | | mode) | | |
+ +------------------------+-----------------+-----------------+----------------+
+ | in-maintenance | active server | disabled | none |
+ +------------------------+-----------------+-----------------+----------------+
+ | partner-down | active server | enabled | all scopes |
+ +------------------------+-----------------+-----------------+----------------+
+ | partner-in-maintenance | active server | enabled | all scopes |
+ +------------------------+-----------------+-----------------+----------------+
+ | passive-backup | active server | enabled | all scopes |
+ +------------------------+-----------------+-----------------+----------------+
+ | ready | active server | disabled | none |
+ +------------------------+-----------------+-----------------+----------------+
+ | syncing | active server | disabled | none |
+ +------------------------+-----------------+-----------------+----------------+
+ | terminated | active server | enabled | same as in the |
+ | | | | load-balancing |
+ | | | | or hot-standby |
+ | | | | state |
+ +------------------------+-----------------+-----------------+----------------+
+ | waiting | any server | disabled | none |
+ +------------------------+-----------------+-----------------+----------------+
+
+In the ``load-balancing`` mode there are two scopes specified for the active
+servers: "HA_server1" and "HA_server2". The DHCP queries load-balanced to
+``server1`` belong to the "HA_server1" scope and the queries load-balanced to
+``server2`` belong to the "HA_server2" scope. If either server is in the
+``partner-down`` state, the active partner is responsible for serving both
+scopes.
+
+In the ``hot-standby`` mode, there is only one scope - "HA_server1" - because
+only ``server1`` is responding to DHCP queries. If that server becomes
+unavailable, ``server2`` becomes responsible for this scope.
+
+The backup servers do not have their own scopes. In some cases they can be used
+to respond to queries belonging to the scopes of the active servers. Also, a
+backup server which is neither in the ``partner-down`` state nor in normal
+operation serves no scopes.
+
+The scope names can be used to associate pools, subnets, and networks with
+certain servers, so that only these servers can allocate addresses or prefixes
+from those pools, subnets, or networks. This is done via the client
+classification mechanism (see :ref:`ha-load-balancing-advanced-config` for more
+details).
+
+.. _ha-scope-transition:
+
+Scope Transition in a Partner-Down Case
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When one of the servers finds that its partner is unavailable, it starts serving
+clients from both its own scope and the scope of the unavailable partner. This
+is straightforward for new clients, i.e. those sending DHCPDISCOVER (DHCPv4) or
+Solicit (DHCPv6), because those requests are not sent to any particular server.
+The available server responds to all such queries when it is in the
+``partner-down`` state.
+
+When a client renews a lease, it sends its DHCPREQUEST (DHCPv4) or Renew (DHCPv6)
+message directly to the server which has allocated the lease being renewed. If
+this server is no longer available, the client will get no response. In that
+case, the client continues to use its lease and attempts to renew until the
+rebind timer (T2) elapses. The client then enters the rebinding phase, in which
+it sends a DHCPREQUEST (DHCPv4) or Rebind (DHCPv6) message to any available
+server. The surviving server receives the rebinding request and typically
+extends the lifetime of the lease. The client then continues to contact that new
+server to renew its lease as appropriate.
+
+If and when the other server once again becomes available, both active servers
+will eventually transition to the ``load-balancing`` or ``hot-standby`` state,
+in which they will again be responsible for their own scopes. Some clients
+belonging to the scope of the restarted server will try to renew their leases
+via the surviving server, but this server will no longer respond to them; the
+client will eventually transition back to the correct server via the rebinding
+mechanism.
+
+.. _ha-load-balancing-config:
+
+Load-Balancing Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The following is the configuration snippet to enable high availability on the
+primary server within the ``load-balancing`` configuration. The same
+configuration should be applied on the secondary and backup servers, with the
+only difference that ``this-server-name`` should be set to "server2" and
+"server3" on those servers, respectively.
+
+.. note::
+
+ Remember that ``load-balancing`` mode requires the address pools and
+ delegated prefix pools to be split between the active servers. During normal
+ operation, the servers use non-overlapping pools to avoid allocating the same
+ lease to different clients by both instances. A server only uses the pool
+ fragments owned by the partner when the partner is not running. See the notes
+ in :ref:`ha-supported-configurations` highlighting differences between the
+ ``load-balancing`` and ``hot-standby`` modes. The semantics of pool
+ partitioning is explained further in this section.
+ The :ref:`ha-load-balancing-advanced-config` section provides advanced
+ pool-partitioning examples.
+
+::
+
+ "Dhcp4": {
+ "hooks-libraries": [{
+ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ }, {
+ "library": "/usr/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ "high-availability": [{
+ "this-server-name": "server1",
+ "mode": "load-balancing",
+ "heartbeat-delay": 10000,
+ "max-response-delay": 60000,
+ "max-ack-delay": 5000,
+ "max-unacked-clients": 5,
+ "max-rejected-lease-updates": 10,
+ "delayed-updates-limit": 100,
+ "peers": [{
+ "name": "server1",
+ "url": "http://192.168.56.33:8000/",
+ "role": "primary",
+ "auto-failover": true
+ }, {
+ "name": "server2",
+ "url": "http://192.168.56.66:8000/",
+ "role": "secondary",
+ "auto-failover": true
+ }, {
+ "name": "server3",
+ "url": "http://192.168.56.99:8000/",
+ "role": "backup",
+ "basic-auth-user": "foo",
+ "basic-auth-password": "bar",
+ "auto-failover": false
+ }]
+ }]
+ }
+ }],
+
+ "subnet4": [{
+ "id": 1,
+ "subnet": "192.0.3.0/24",
+ "pools": [{
+ "pool": "192.0.3.100 - 192.0.3.150",
+ "client-class": "HA_server1"
+ }, {
+ "pool": "192.0.3.200 - 192.0.3.250",
+ "client-class": "HA_server2"
+ }],
+
+ "option-data": [{
+ "name": "routers",
+ "data": "192.0.3.1"
+ }],
+
+ "relay": { "ip-address": "10.1.2.3" }
+ }]
+ }
+
+Two hook libraries must be loaded to enable HA: :ischooklib:`libdhcp_lease_cmds.so` and
+:ischooklib:`libdhcp_ha.so`. The latter implements the HA feature, while the former
+enables control commands required by HA to fetch and manipulate leases on the
+remote servers. In the example provided above, it is assumed that Kea libraries
+are installed in the ``/usr/lib`` directory. If Kea is not installed in the
+``/usr`` directory, the hook libraries' locations must be updated accordingly.
+
+The HA configuration is specified within the scope of :ischooklib:`libdhcp_ha.so`.
+Note that while the top-level parameter ``high-availability`` is a list, only a
+single entry is currently supported.
+
+The following are the global parameters which control the server's behavior with
+respect to HA:
+
+- ``this-server-name`` - is a unique identifier of the server within this HA
+ setup. It must match one of the servers specified within the ``peers`` list.
+
+- ``mode`` - specifies an HA mode of operation. The currently supported modes
+ are ``load-balancing`` and ``hot-standby``.
+
+- ``heartbeat-delay`` - specifies a duration in milliseconds between sending
+ the last heartbeat (or other command sent to the partner) and the next
+ heartbeat. Heartbeats are sent periodically to gather the status of the
+ partner and to verify whether the partner is still operating. The default
+ value of this parameter is 10000 ms.
+
+- ``max-response-delay`` - specifies a duration in milliseconds since the last
+ successful communication with the partner, after which the server assumes
+ that communication with the partner is interrupted. This duration should be
+ greater than the ``heartbeat-delay``; typically it should be a multiple of
+ ``heartbeat-delay``. When the server detects that communication is
+ interrupted, it may transition to the ``partner-down`` state (when
+ ``max-unacked-clients`` is 0) or trigger the failure-detection procedure
+ using the values of the two parameters below. The default value of this
+ parameter is 60000 ms.
+
+- ``max-ack-delay`` - is one of the parameters controlling partner
+ failure-detection. When communication with the partner is interrupted, the
+ server examines the values of the "secs" field (DHCPv4) or "elapsed time"
+ option (DHCPv6), which denote how long the DHCP client has been trying to
+ communicate with the DHCP server. This parameter specifies the maximum time
+ in milliseconds for the client to try to communicate with the DHCP server,
+ after which this server assumes that the client failed to communicate with
+ the DHCP server (is unacknowledged or "unacked"). The default value of this
+ parameter is 10000.
+
+- ``max-unacked-clients`` - specifies how many "unacked" clients are allowed
+ (see ``max-ack-delay``) before this server assumes that the partner is
+ offline and transitions to the ``partner-down`` state. The special value of 0
+ is allowed for this parameter, which disables the failure-detection mechanism.
+ In this case, a server that cannot communicate with its partner over the
+ control channel assumes that the partner server is down and transitions to
+ the ``partner-down`` state immediately. The default value of this parameter
+ is 10.
+
+- ``max-rejected-lease-updates`` - specifies how many lease updates for distinct
+ clients can fail, due to a conflict between the lease and the partner configuration
+ or state, before the server transitions to the ``terminated`` state. Conflict
+ can be a sign of a misconfiguration; usually, a small number of conflicted
+ leases are acceptable because they affect only a few devices. However, if
+ the conflicts occur for many devices (e.g., an entire subnet), the HA service
+ becomes unreliable and should be terminated, and the problem must be manually
+ corrected by an administrator. It is up to the administrator to select
+ the highest acceptable value of ``max-rejected-lease-updates``. The default
+ value is 10. The special value of 0 configures the server to never terminate
+ the HA service due to lease conflicts. If the value is 1, the server
+ transitions to the ``terminated`` state when the first conflict occurs.
+ This parameter does not pertain to conflicting lease updates sent to
+ the backup servers.
+
+- ``delayed-updates-limit`` - specifies the maximum number of lease updates
+ which can be queued while the server is in the ``communication-recovery``
+ state. This parameter was introduced in Kea 1.9.4. The special value of 0
+ configures the server to never transition to the ``communication-recovery``
+ state and the server behaves as in earlier Kea versions, i.e. if the server
+ cannot reach its partner, it goes straight into the ``partner-down`` state.
+ The default value of this parameter is 100.
+
+.. note::
+
+ The ``max-rejected-lease-updates`` parameter was introduced in Kea 2.3.1.
+ Previously, the server did not differentiate between a lease update
+ failure due to a non-functioning partner and a failure due to a conflict
+ (e.g., configuration issues).
+ As a result, the server could sometimes transition to the ``partner-down``
+ state even though the partner was operating normally, but only certain leases
+ had issues. Conflicts should no longer cause such a transition. However,
+ depending on the ``max-rejected-lease-updates`` setting, too many conflicts
+ can lead to termination of the High Availability service. In that case, both
+ servers continue to respond to DHCP queries but no longer send lease updates.
+
+The values of ``max-ack-delay`` and ``max-unacked-clients`` must be selected
+carefully, taking into account the specifics of the network in which the DHCP
+servers are operating. The server in question may not respond to some DHCP
+clients following administrative policy, or the server may drop malformed
+queries from clients. Therefore, selecting too low a value for the
+``max-unacked-clients`` parameter may result in a transition to the
+``partner-down`` state even though the partner is still operating. On the other
+hand, selecting too high a value may result in never transitioning to the
+``partner-down`` state if the DHCP traffic in the network is very low (e.g. at
+night), because the number of distinct clients trying to communicate with the
+server could be lower than the ``max-unacked-clients`` setting.
+
+In some cases it may be useful to disable the failure-detection mechanism
+altogether, if the servers are located very close to each other and network
+partitioning is unlikely, i.e. failure to respond to heartbeats is only possible
+when the partner is offline. In such cases, set ``max-unacked-clients`` to 0.
+
+The ``delayed-updates-limit`` parameter is used to enable or disable the
+``communication-recovery`` procedure, and controls the server's behavior in the
+``communication-recovery`` state. This parameter can only be used in the
+``load-balancing`` mode.
+
+If a server in the ``load-balancing`` state experiences communication issues
+with its partner (a heartbeat or lease-update failure), the server transitions
+to the ``communication-recovery`` state. In this state, the server keeps
+responding to DHCP queries but does not send lease updates to the partner. The
+lease updates are queued until communication is re-established, to ensure that
+DHCP service remains available even in the event of the communication loss
+between the partners. There may appear to be communication loss when either one
+of the servers has terminated, or when both servers remain available but cannot
+communicate with each other. In the former case, the surviving server will
+follow the normal procedure and should eventually transition to the
+``partner-down`` state. In the latter case, both servers should transition to
+the ``communication-recovery`` state and should never transition to the
+``partner-down`` state (if ``max-unacked-clients`` is set to a non-zero value),
+because all DHCP queries are answered and neither server would see any unacked
+DHCP queries.
+
+Introduction of the ``communication-recovery`` procedure was motivated by issues
+which may appear when two servers remain online but the communication between
+them remains interrupted for a period of time. In earlier Kea versions, the
+servers having communication issues used to drop DHCP packets before
+transitioning to the ``partner-down`` state. In some cases they both
+transitioned to the ``partner-down`` state, which could potentially result in
+allocations of the same IP addresses or delegated prefixes to different clients
+by both servers. By entering the intermediate ``communication-recovery`` state,
+these problems are avoided.
+
+If a server in the ``communication-recovery`` state re-establishes communication
+with its partner, it tries to send the partner all of the outstanding lease
+updates it has queued. This is done synchronously and may take a considerable
+amount of time before the server transitions to the ``load-balancing`` state and
+resumes normal operation.
+The maximum number of lease updates which can be queued in the
+``communication-recovery`` state is controlled by ``delayed-updates-limit``.
+If the limit is exceeded, the server stops queuing lease updates and performs a
+full database synchronization after re-establishing the connection with the
+partner, instead of sending outstanding lease updates before transitioning to
+the ``load-balancing`` state. Even if the limit is exceeded, the server in the
+``communication-recovery`` state remains responsive to DHCP clients.
+
+It may be preferable to set higher values of ``delayed-updates-limit`` when
+there is a risk of prolonged communication interruption between the servers and
+when the lease database is large, to avoid costly lease-database synchronization.
+On the other hand, if the lease database is small, the time required to send
+outstanding lease updates may be longer than the lease-database synchronization.
+In such cases it may be better to use a lower value, e.g. 10. The default value
+of 100 is a reasonable compromise and should work well in most deployments with
+moderate traffic.
+
+.. note::
+
+ This parameter is new and values for it that work well in some environments
+ may not work well in others. Feedback from users will help us build a better
+ working set of recommendations.
+
+The ``peers`` parameter contains a list of servers within this HA setup.
+This configuration must contain at least one primary and one secondary server.
+It may also contain an unlimited number of backup servers. In this example,
+there is one backup server which receives lease updates from the active servers.
+
+Since Kea version 1.9.0, basic HTTP authentication is available
+to protect the Kea control agent against local attackers.
+
+These are the parameters specified for each of the peers within this
+list:
+
+- ``name`` - specifies a unique name for the server.
+
+- ``url`` - specifies the URL to be used to contact this server over the
+ control channel. Other servers use this URL to send control commands to that
+ server.
+
+- ``basic-auth-user`` - specifies the user ID for basic HTTP authentication. If
+ not specified or specified as an empty string, no authentication header is
+ added to HTTP transactions. It must not contain the colon (:) character.
+
+- ``basic-auth-password`` - specifies the password for basic HTTP
+ authentication. This parameter is ignored when the user ID is not specified
+ or is empty. The password is optional; if not specified, an empty password is
+ used.
+
+- ``basic-auth-password-file`` - is an alternative to ``basic-auth-password``:
+ instead of presenting the password in the configuration file it is specified
+ in the file indicated by this parameter.
+
+- ``role`` - denotes the role of the server in the HA setup. The following
+ roles are supported in the ``load-balancing`` configuration: ``primary``,
+ ``secondary``, and ``backup``. There must be exactly one primary and one
+ secondary server in the ``load-balancing`` setup.
+
+- ``auto-failover`` - a boolean value which denotes whether a server detecting
+ a partner's failure should automatically start serving the partner's clients.
+ The default value of this parameter is ``true``.
+
+In our example configuration above, both active servers can allocate leases from
+the subnet "192.0.3.0/24". This subnet contains two address pools:
+"192.0.3.100 - 192.0.3.150" and "192.0.3.200 - 192.0.3.250", which are
+associated with HA server scopes using client classification. When ``server1``
+processes a DHCP query, it uses the first pool for lease allocation. Conversely,
+when ``server2`` processes a DHCP query it uses the second pool. If either of
+the servers is in the ``partner-down`` state, the other can serve leases from
+both pools; it selects the pool which is appropriate for the received query. In
+other words, if the query would normally be processed by ``server2`` but this
+server is not available, ``server1`` allocates the lease from the pool of
+"192.0.3.200 - 192.0.3.250". The Kea control agent in front of ``server3``
+requires basic HTTP authentication, and authorizes the user ID "foo" with the
+password "bar".
+
+.. note::
+
+ The ``url`` schema can be ``http`` or ``https``, but since Kea version 1.9.6
+ the ``https`` schema requires a TLS setup. The hostname part must be an IPv4
+ address or an IPv6 address between square brackets, e.g.
+ ``http://[2001:db8::1]:8080/``. Names are not accepted.
+
+.. _ha-load-balancing-advanced-config:
+
+Load Balancing With Advanced Classification
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the previous section, we provided an example of a ``load-balancing``
+configuration with client classification limited to the "HA_server1" and
+"HA_server2" classes, which are dynamically assigned to the received DHCP
+queries. In many cases, HA is needed in deployments which already use some other
+client classification.
+
+Suppose there is a system which classifies devices into two groups: "phones" and
+"laptops", based on some classification criteria specified in the Kea
+configuration file. Both types of devices are allocated leases from different
+address pools. Introducing HA in ``load-balancing`` mode results in a further
+split of each of those pools, as each server allocates leases for some phones
+and some laptops. This requires each of the existing pools to be split between
+"HA_server1" and "HA_server2", so we end up with the following classes:
+
+- "phones_server1"
+- "laptops_server1"
+- "phones_server2"
+- "laptops_server2"
+
+The corresponding server configuration, using advanced classification (and the
+``member`` expression), is provided below. For brevity's sake, the HA hook
+library configuration has been removed from this example.
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "client-classes": [{
+ "name": "phones",
+ "test": "substring(option[60].hex,0,6) == 'Aastra'"
+ }, {
+ "name": "laptops",
+ "test": "not member('phones')"
+ }, {
+ "name": "phones_server1",
+ "test": "member('phones') and member('HA_server1')"
+ }, {
+ "name": "phones_server2",
+ "test": "member('phones') and member('HA_server2')"
+ }, {
+ "name": "laptops_server1",
+ "test": "member('laptops') and member('HA_server1')"
+ }, {
+ "name": "laptops_server2",
+ "test": "member('laptops') and member('HA_server2')"
+ }],
+
+ "hooks-libraries": [{
+ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ }, {
+ "library": "/usr/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ "high-availability": [{
+ }]
+ }
+ }],
+
+ "subnet4": [{
+ "id": 1,
+ "subnet": "192.0.3.0/24",
+ "pools": [{
+ "pool": "192.0.3.100 - 192.0.3.125",
+ "client-class": "phones_server1"
+ }, {
+ "pool": "192.0.3.126 - 192.0.3.150",
+ "client-class": "laptops_server1"
+ }, {
+ "pool": "192.0.3.200 - 192.0.3.225",
+ "client-class": "phones_server2"
+ }, {
+ "pool": "192.0.3.226 - 192.0.3.250",
+ "client-class": "laptops_server2"
+ }],
+
+ "option-data": [{
+ "name": "routers",
+ "data": "192.0.3.1"
+ }],
+
+ "relay": { "ip-address": "10.1.2.3" }
+ }]
+ }
+ }
+
+The configuration provided above splits the address range into four pools: two
+pools dedicated to "HA_server1" and two to "HA_server2". Each server can assign
+leases to both phones and laptops. Both groups of devices are assigned addresses
+from different pools. The "HA_server1" and "HA_server2" classes are built-in
+(see :ref:`built-in-client-classes`) and do not need to be declared.
+They are assigned dynamically by the HA hook library as a result of the
+``load-balancing`` algorithm. "phones_*" and "laptop_*" evaluate to ``true``
+when the query belongs to a given combination of other classes, e.g. "HA_server1"
+and "phones". The pool is selected accordingly as a result of such an evaluation.
+
+Consult :ref:`classify` for details on how to use the ``member`` expression and
+class dependencies.
+
+.. _ha-hot-standby-config:
+
+Hot-Standby Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The following is an example configuration of the primary server in a
+``hot-standby`` configuration:
+
+::
+
+ "Dhcp4": {
+ "hooks-libraries": [{
+ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ }, {
+ "library": "/usr/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ "high-availability": [{
+ "this-server-name": "server1",
+ "mode": "hot-standby",
+ "heartbeat-delay": 10000,
+ "max-response-delay": 60000,
+ "max-ack-delay": 5000,
+ "max-unacked-clients": 5,
+ "max-rejected-lease-updates": 10,
+ "peers": [{
+ "name": "server1",
+ "url": "http://192.168.56.33:8000/",
+ "role": "primary",
+ "auto-failover": true
+ }, {
+ "name": "server2",
+ "url": "http://192.168.56.66:8000/",
+ "role": "standby",
+ "auto-failover": true
+ }, {
+ "name": "server3",
+ "url": "http://192.168.56.99:8000/",
+ "basic-auth-user": "foo",
+ "basic-auth-password": "bar",
+ "role": "backup",
+ "auto-failover": false
+ }]
+ }]
+ }
+ }],
+
+ "subnet4": [{
+ "id": 1,
+ "subnet": "192.0.3.0/24",
+ "pools": [{
+ "pool": "192.0.3.100 - 192.0.3.250",
+ "client-class": "HA_server1"
+ }],
+
+ "option-data": [{
+ "name": "routers",
+ "data": "192.0.3.1"
+ }],
+
+ "relay": { "ip-address": "10.1.2.3" }
+ }]
+ }
+
+This configuration is very similar to the ``load-balancing`` configuration
+described in :ref:`ha-load-balancing-config`, with a few notable differences.
+
+The ``mode`` is now set to ``hot-standby``, in which only one server responds to
+DHCP clients. If the primary server is online, it responds to all DHCP queries.
+The ``standby`` server takes over all DHCP traffic only if it discovers that the
+primary is unavailable.
+
+In this mode, the non-primary active server is called ``standby`` and that is
+its role.
+
+Finally, because there is always only one server responding to DHCP queries,
+there is only one scope - "HA_server1" - in use within pool definitions. In fact,
+the ``client-class`` parameter could be removed from this configuration without
+harm, because there can be no conflicts in lease allocations by different
+servers as they do not allocate leases concurrently. The ``client-class``
+remains in this example mostly for demonstration purposes, to highlight the
+differences between the ``hot-standby`` and ``load-balancing`` modes of
+operation.
+
+.. _ha-passive-backup-config:
+
+Passive-Backup Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The following is an example configuration file for the primary server in a
+``passive-backup`` configuration:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "hooks-libraries": [{
+ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ }, {
+ "library": "/usr/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ "high-availability": [{
+ "this-server-name": "server1",
+ "mode": "passive-backup",
+ "wait-backup-ack": false,
+ "peers": [{
+ "name": "server1",
+ "url": "http://192.168.56.33:8000/",
+ "role": "primary"
+ }, {
+ "name": "server2",
+ "url": "http://192.168.56.66:8000/",
+ "role": "backup"
+ }, {
+ "name": "server3",
+ "url": "http://192.168.56.99:8000/",
+ "basic-auth-user": "foo",
+ "basic-auth-password": "bar",
+ "role": "backup"
+ }]
+ }]
+ }
+ }],
+
+ "subnet4": [{
+ "id": 1,
+ "subnet": "192.0.3.0/24",
+ "pools": [{
+ "pool": "192.0.3.100 - 192.0.3.250"
+ }],
+
+ "option-data": [{
+ "name": "routers",
+ "data": "192.0.3.1"
+ }],
+
+ "relay": { "ip-address": "10.1.2.3" }
+ }]
+ }
+ }
+
+The configurations of three peers are included: one for the primary and two for
+the backup servers.
+
+Many of the parameters present in the ``load-balancing`` and ``hot-standby``
+configuration examples are not relevant in the ``passive-backup`` mode, thus
+they are not specified here. For example: ``heartbeat-delay``,
+``max-unacked-clients``, ``max-rejected-lease-updates``, and others related to
+the failover mechanism should not be specified in the ``passive-backup`` mode.
+
+The ``wait-backup-ack`` is a boolean parameter not present in previous examples.
+It defaults to ``false`` and must not be modified in the ``load-balancing`` and
+``hot-standby`` modes. In the ``passive-backup`` mode this parameter can be set
+to ``true``, which causes the primary server to expect acknowledgments to the
+lease updates from the backup servers prior to responding to the DHCP client. It
+ensures that the lease has propagated to all servers before the client is given
+the lease, but it poses a risk of losing a DHCP service if there is a
+communication problem with one of the backup servers. This setting also
+increases the latency of the DHCP response, because of the time that the primary
+spends waiting for the acknowledgements. We recommend that the
+``wait-backup-ack`` setting be left at its default value (``false``) if the DHCP
+service reliability is more important than consistency of the lease information
+between the primary and the backups, and in all cases when the DHCP service
+latency should be minimal.
+
+.. note::
+
+ Currently, active servers place lease updates to be sent to peers onto
+ internal queues (one queue per peer/URL). In ``passive-backup`` mode, active
+ servers do not wait for lease updates to be acknowledged; thus during times
+ of heavy client traffic it is possible for the number of lease updates queued
+ for transmission to accumulate faster than they can be delivered. As client
+ traffic lessens the queues begin to empty. Since Kea 2.0.0, active servers
+ monitor the size of these queues and emit periodic warnings (see
+ HTTP_CLIENT_QUEUE_SIZE_GROWING in :ref:`kea-messages`) if they perceive a
+ queue as growing too quickly. The warnings cease once the queue size begins
+ to shrink. These messages are intended as a bellwether and seeing them
+ sporadically during times of heavy traffic load does not necessarily indicate
+ a problem. If, however, they occur continually during times of routine
+ traffic load, they likely indicate potential mismatches in server
+ capabilities and/or configuration; this should be investigated, as the size
+ of the queues may eventually impair an active server's ability to respond to
+ clients in a timely manner.
+
+.. _ha-sharing-lease-info:
+
+Lease Information Sharing
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+An HA-enabled server informs its active partner about allocated or renewed
+leases by sending appropriate control commands, and the partner updates the
+lease information in its own database. When the server starts up for the first
+time or recovers after a failure, it synchronizes its lease database with its
+partner. These two mechanisms guarantee consistency of the lease information
+between the servers and allow the designation of one of the servers to handle
+the entire DHCP traffic load if the other server becomes unavailable.
+
+In some cases, though, it is desirable to disable lease updates and/or database
+synchronization between the active servers, if the exchange of information about
+the allocated leases is performed using some other mechanism. Kea supports
+various database types that can be used to store leases, including MySQL and
+PostgreSQL. Those databases include built-in solutions for data replication
+which are often used by Kea administrators to provide redundancy.
+
+The HA hook library supports such scenarios by disabling lease updates over the
+control channel and/or lease-database synchronization, leaving the server to
+rely on the database replication mechanism. This is controlled by the two
+boolean parameters ``send-lease-updates`` and ``sync-leases``, whose values
+default to ``true``:
+
+::
+
+ "Dhcp4": {
+ "hooks-libraries": [
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ },
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ "high-availability": [ {
+ "this-server-name": "server1",
+ "mode": "load-balancing",
+ "send-lease-updates": false,
+ "sync-leases": false,
+ "peers": [
+ {
+ "name": "server1",
+ "url": "http://192.168.56.33:8000/",
+ "role": "primary"
+ },
+ {
+ "name": "server2",
+ "url": "http://192.168.56.66:8000/",
+ "role": "secondary"
+ }
+ ]
+ } ]
+ }
+ }
+ ],
+ ...
+ }
+
+In the most typical use case, both parameters are set to the same value, i.e.
+both are ``false`` if database replication is in use, or both are ``true``
+otherwise. Introducing two separate parameters to control lease updates and
+lease-database synchronization is aimed at possible special use cases; for
+example, when synchronization is performed by copying a lease file (therefore
+``sync-leases`` is set to ``false``), but lease updates should be conducted as
+usual (``send-lease-updates`` is set to ``true``). It should be noted that Kea
+does not natively support such use cases, but users may develop their own
+scripts and tools around Kea to provide such mechanisms. The HA hook library
+configuration is designed to maximize flexibility of administration.
+
+.. _ha-syncing-page-limit:
+
+Controlling Lease-Page Size Limit
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+An HA-enabled server initiates synchronization of the lease database after
+downtime or upon receiving the :isccmd:`ha-sync` command. The server uses commands
+:isccmd:`lease4-get-page` and :isccmd:`lease6-get-page`
+to fetch leases from its partner server (lease queries). The size of the results
+page (the maximum number of leases to be returned in a single response to one of
+these commands) can be controlled via configuration of the HA hook library.
+Increasing the page size decreases the number of lease queries sent to the
+partner server, but it causes the partner server to generate larger responses,
+which lengthens transmission time as well as increases memory and CPU
+utilization on both servers. Decreasing the page size helps to decrease resource
+utilization, but requires more lease queries to be issued to fetch the entire
+lease database.
+
+The default value of the ``sync-page-limit`` command controlling the page size
+is 10000. This means that the entire lease database can be fetched with a single
+command if the size of the database is equal to or less than 10000 lines.
+
+.. _ha-syncing-timeouts:
+
+Timeouts
+~~~~~~~~
+
+In deployments with a large number of clients connected to the network,
+lease-database synchronization after a server failure may be a time-consuming
+operation. The synchronizing server must gather all leases from its partner,
+which yields a large response over the RESTful interface. The server receives
+leases using the paging mechanism described in :ref:`ha-syncing-page-limit`.
+Before the page of leases is fetched, the synchronizing server sends a
+:isccmd:`dhcp-disable` command to disable the DHCP service on the partner server. If
+the service is already disabled, this command resets the timeout for the DHCP
+service being disabled, which by default is set to 60 seconds. If fetching a
+single page of leases takes longer than the specified time, the partner server
+assumes that the synchronizing server has died and resumes its DHCP service. The
+connection of the synchronizing server with its partner is also protected by the
+timeout. If the synchronization of a single page of leases takes longer than the
+specified time, the synchronizing server terminates the connection and the
+synchronization fails. Both timeout values are controlled by a single
+configuration parameter, ``sync-timeout``. The following configuration snippet
+demonstrates how to modify the timeout for automatic re-enabling of the DHCP
+service on the partner server and how to increase the timeout for fetching a
+single page of leases from 60 seconds to 90 seconds:
+
+::
+
+ "Dhcp4": {
+ "hooks-libraries": [
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ },
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ "high-availability": [ {
+ "this-server-name": "server1",
+ "mode": "load-balancing",
+ "sync-timeout": 90000,
+ "peers": [
+ {
+ "name": "server1",
+ "url": "http://192.168.56.33:8000/",
+ "role": "primary"
+ },
+ {
+ "name": "server2",
+ "url": "http://192.168.56.66:8000/",
+ "role": "secondary"
+ }
+ ]
+ } ]
+ }
+ }
+ ],
+ ...
+ }
+
+It is important to note that extending this ``sync-timeout`` value may sometimes
+be insufficient to prevent issues with timeouts during lease-database
+synchronization. The control commands travel via the Control Agent, which also
+monitors incoming (with a synchronizing server) and outgoing (with a DHCP server)
+connections for timeouts. The DHCP server also monitors the connection from the
+Control Agent for timeouts. Those timeouts cannot currently be modified via
+configuration; extending these timeouts is only possible by modifying them in
+the Kea code and recompiling the server. The relevant constants are located in
+the Kea source at: ``src/lib/config/timeouts.h``.
+
+.. _ha-pause-state-machine:
+
+Pausing the HA State Machine
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``high-availability`` state machine includes many different states described
+in detail in :ref:`ha-server-states`. The server enters each state when certain
+conditions are met, most often taking into account the partner server's state.
+In some states the server performs specific actions, e.g. synchronization of the
+lease database in the ``syncing`` state, or responding to DHCP queries according
+to the configured mode of operation in the ``load-balancing`` and ``hot-standby``
+states.
+
+By default, transitions between the states are performed automatically and the
+server administrator has no direct control over when the transitions take place;
+in most cases, the administrator does not need such control. In some situations,
+however, the administrator may want to "pause" the HA state machine in a
+selected state to perform some additional administrative actions before the
+server transitions to the next state.
+
+Consider a server failure which results in the loss of the entire lease database.
+Typically, the server rebuilds its lease database when it enters the ``syncing``
+state by querying the partner server for leases, but it is possible that the
+partner was also experiencing a failure and lacks lease information. In this
+case, it may be required to reconstruct lease databases on both servers from
+some external source, e.g. a backup server. If the lease database is to be
+reconstructed via the RESTful API, the servers should be started in the initial,
+i.e. ``waiting``, state and remain in this state while leases are being added.
+In particular, the servers should not attempt to synchronize their lease
+databases nor start serving DHCP clients.
+
+The HA hook library provides configuration parameters and a command to control
+pausing and resuming the HA state machine. The following configuration causes
+the HA state machine to pause in the ``waiting`` state after server startup.
+
+::
+
+ "Dhcp4": {
+ "hooks-libraries": [
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ },
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ "high-availability": [ {
+ "this-server-name": "server1",
+ "mode": "load-balancing",
+ "peers": [
+ {
+ "name": "server1",
+ "url": "http://192.168.56.33:8000/",
+ "role": "primary"
+ },
+ {
+ "name": "server2",
+ "url": "http://192.168.56.66:8000/",
+ "role": "secondary"
+ }
+ ],
+ "state-machine": {
+ "states": [
+ {
+ "state": "waiting",
+ "pause": "once"
+ }
+ ]
+ }
+ } ]
+ }
+ }
+ ],
+ ...
+ }
+
+The ``pause`` parameter value ``once`` denotes that the state machine should be
+paused upon the first transition to the ``waiting`` state; later transitions to
+this state will not cause the state machine to pause. Two other supported values
+of the ``pause`` parameter are ``always`` and ``never``. The latter is the
+default value for each state, which instructs the server never to pause the
+state machine.
+
+In order to "unpause" the state machine, the :isccmd:`ha-continue` command must be
+sent to the paused server. This command does not take any arguments. See
+:ref:`ha-control-commands` for details about commands specific to :ischooklib:`libdhcp_ha.so`.
+
+It is possible to configure the state machine to pause in more than one state.
+Consider the following configuration:
+
+::
+
+ "Dhcp4": {
+ "hooks-libraries": [
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ },
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ "high-availability": [ {
+ "this-server-name": "server1",
+ "mode": "load-balancing",
+ "peers": [
+ {
+ "name": "server1",
+ "url": "http://192.168.56.33:8000/",
+ "role": "primary"
+ },
+ {
+ "name": "server2",
+ "url": "http://192.168.56.66:8000/",
+ "role": "secondary"
+ }
+ ],
+ "state-machine": {
+ "states": [
+ {
+ "state": "ready",
+ "pause": "always"
+ },
+ {
+ "state": "partner-down",
+ "pause": "once"
+ }
+ ]
+ }
+ } ]
+ }
+ }
+ ],
+ ...
+ }
+
+This configuration instructs the server to pause the state machine every time it
+transitions to the ``ready`` state and upon the first transition to the
+``partner-down`` state.
+
+Refer to :ref:`ha-server-states` for a complete list of server states. The state
+machine can be paused in any of the supported states; however, it is not
+practical to pause in the ``backup`` or ``terminated`` states because the server
+never transitions out of these states anyway.
+
+.. note::
+
+ In the ``syncing`` state the server is paused before it makes an attempt to
+ synchronize the lease database with a partner. To pause the state machine
+ after lease-database synchronization, use the ``ready`` state instead.
+
+.. note::
+
+ The state of the HA state machine depends on the state of the cooperating
+ server. Therefore, pausing the state machine of one server may affect the
+ operation of the partner server. For example: if the primary server is paused
+ in the ``waiting`` state, the partner server will also remain in the
+ ``waiting`` state until the state machine of the primary server is resumed
+ and that server transitions to the ``ready`` state.
+
+.. _ha-ctrl-agent-config:
+
+Control Agent Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :ref:`kea-ctrl-agent` describes in detail the Kea daemon, which provides a
+RESTful interface to control the Kea servers. The same functionality is used by
+the High Availability hook library to establish communication between the HA
+peers. Therefore, the HA library requires that the Control Agent (CA) be started
+for each DHCP instance within the HA setup. If the Control Agent is not started,
+the peers cannot communicate with a particular DHCP server (even if the DHCP
+server itself is online) and may eventually consider this server to be offline.
+
+The following is an example configuration for the CA running on the same
+machine as the primary server. This configuration is valid for both the
+``load-balancing`` and the ``hot-standby`` cases presented in previous sections.
+
+::
+
+ {
+ "Control-agent": {
+ "http-host": "192.168.56.33",
+
+ // If enabling HA and multi-threading, the 8000 port is used by the HA
+ // hook library http listener. When using HA hook library with
+ // multi-threading to function, make sure the port used by dedicated
+ // listener is different (e.g. 8001) than the one used by CA. Note
+ // the commands should still be sent via CA. The dedicated listener
+ // is specifically for HA updates only.
+ "http-port": 8000,
+
+ "control-sockets": {
+ "dhcp4": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea-dhcp4-ctrl.sock"
+ },
+ "dhcp6": {
+ "socket-type": "unix",
+ "socket-name": "/tmp/kea-dhcp6-ctrl.sock"
+ }
+ }
+ }
+ }
+
+Since Kea 1.9.0, basic HTTP authentication is supported.
+
+.. _ha-mt-config:
+
+Multi-Threaded Configuration (HA+MT)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+HA peer communication consists of specialized API commands sent between HA peers.
+Prior to Kea 1.9.7, each peer had to be paired with a local instance of
+:iscman:`kea-ctrl-agent` in order to exchange commands. The agent received HA commands
+via HTTP, communicated via Linux socket with the local peer to carry out the
+command, and then sent the response back to the requesting peer via HTTP. To
+send HA commands, each peer opened its own HTTP client connection to the URL of
+each of its peers.
+
+In Kea 1.9.7 and newer, it is possible to configure HA to use direct
+multi-threaded communication between peers. We refer to this mode as HA+MT.
+With HA+MT enabled, each peer runs its own dedicated, internal HTTP listener
+(i.e. server) which receives and responds to commands directly, thus eliminating
+the need for an agent to carry out HA protocol between peers. In addition, both
+the listener and client components use multi-threading to support multiple,
+concurrent connections between peers. By eliminating the agent and executing
+multiple command exchanges in parallel, HA throughput between peers should
+improve considerably in most situations.
+
+The following parameters have been added to the HA configuration, to support
+HA+MT operation:
+
+- ``enable-multi-threading`` - enables or disables multi-threading HA peer
+ communication (HA+MT). Kea core multi-threading must be enabled for HA+MT to
+ operate. When ``false``, the server relies on :iscman:`kea-ctrl-agent` for
+ communication with its peer, and uses single-threaded HTTP client processing.
+ The default is ``true``.
+
+- ``http-dedicated-listener`` - enables or disables the creation of a dedicated,
+ internal HTTP listener through which the server receives HA messages from its
+ peers. The internal listener replaces the role of :iscman:`kea-ctrl-agent` traffic,
+ allowing peers to send their HA commands directly to each other. The listener
+ listens on the peer's ``url``. When ``false``, the server
+ relies on :iscman:`kea-ctrl-agent`. This parameter has been provided largely for
+ flexibility and testing; running HA+MT without dedicated listeners enabled
+ will substantially limit HA throughput. The default is ``true``.
+
+- ``http-listener-threads`` - indicates the maximum number of threads the
+ dedicated listener should use. A value of ``0`` instructs the server to use the
+ same number of threads that the Kea core is using for DHCP multi-threading.
+ The default is ``0``.
+
+- ``http-client-threads`` - indicates the maximum number of threads that should
+ be used to send HA messages to its peers. A value of ``0`` instructs the server
+ to use the same number of threads that the Kea core is using for DHCP
+ multi-threading. The default is ``0``.
+
+These parameters are grouped together under a map element, ``multi-threading``,
+as illustrated below:
+
+::
+
+ "Dhcp4": {
+ "hooks-libraries": [
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ },
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ "high-availability": [ {
+ "this-server-name": "server1",
+ "multi-threading": {
+ "enable-multi-threading": true,
+ "http-dedicated-listener": true,
+ "http-listener-threads": 4,
+ "http-client-threads": 4
+ },
+ "peers": [
+ // This is the configuration of this server instance.
+ {
+ "name": "server1",
+ // This specifies the URL of our server instance.
+ // Since the HA+MT uses a direct connection, the
+ // DHCPv4 server open its own socket. Note that it
+ // must be different than the one used by the CA
+ // (typically 8000). In this example, 8001 is used.
+ "url": "http://192.0.2.1:8001/",
+ // This server is primary. The other one must be
+ // secondary.
+ "role": "primary"
+ },
+ // This is the configuration of our HA peer.
+ {
+ "name": "server2",
+ // This specifies the URL of our server instance.
+ // Since the HA+MT uses a direct connection, the
+ // DHCPv4 server open its own socket. Note that it
+ // must be different than the one used by the CA
+ // (typically 8000). In this example, 8001 is used.
+ "url": "http://192.0.2.2:8001/",
+ // The partner is a secondary. This server is a
+ // primary as specified in the previous "peers"
+ // entry and in "this-server-name" before that.
+ "role": "secondary"
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ]
+ }
+ },
+ ...
+ ],
+ ...
+ }
+
+
+In the example above, HA+MT is enabled with four threads for the listener and
+four threads for the client.
+
+.. note::
+
+ It is essential to configure the ports correctly. One common mistake is to
+ configure CA to listen on port 8000 and also configure dedicated listeners on
+ port 8000. In such a configuration, the communication will still work over CA,
+ but it will be slow and the DHCP server will fail to bind sockets.
+ Administrators should ensure that dedicated listeners use a different port
+ (8001 is a suggested alternative); if ports are misconfigured or the ports
+ dedicated to CA are used, the performance bottlenecks caused by the
+ single-threaded nature of CA and the sequential nature of the UNIX socket
+ that connects CA to DHCP servers will nullify any performance gains offered
+ by HA+MT.
+
+.. _ha-parked-packet-limit:
+
+Parked-Packet Limit
+~~~~~~~~~~~~~~~~~~~
+
+Kea servers contain a mechanism by which the response to a client packet may
+be held, pending completion of hook library work. We refer to this as "parking"
+the packet. The HA hook library makes use of this mechanism. When an HA server
+needs to send a lease update to its peer(s) to notify it of the change to the
+lease, it will "park" the client response until the peer acknowledges the lease
+update. At that point, the server will "unpark" the response and send it to the
+client. This applies to client queries which cause lease changes, such as
+DHCPREQUEST for DHCPv4 and Request, Renew, and Rebind for DHCPv6. It does not
+apply to DHPCDISCOVERs (v4) or Solicits (v6).
+
+There is a global parameter, ``parked-packet-limit``, that may be used to limit
+the number of responses that may be parked at any given time. This acts as a
+form of congestion handling and protects the server from being swamped when the
+volume of client queries is outpacing the server's ability to respond. Once the
+limit is reached, the server emits a log and drops any new responses until
+parking spaces are available.
+
+In general, smaller values for the parking lot limit are likely to cause more
+drops but with shorter response times. Larger values are likely to result in
+fewer drops but with longer response times. Currently, the default value for
+``parked-packet-limit`` is 256.
+
+.. warning::
+
+ Using too small a value may result in an unnecessarily high drop rate, while
+ using too large a value may lead to response times that are simply too long
+ to be useful. A value of 0, while allowed, disables the limit altogether, but
+ this is highly discouraged as it may lead to Kea servers becoming
+ unresponsive to clients. Choosing the best value is very site-specific; we
+ recommend users initially leave it at the default value of 256 and observe
+ how the system behaves over time with varying load conditions.
+
+::
+
+ "Dhcp6": {
+ // Limit the number of concurrently parked packets to 128.
+ "parked-packet-limit": 128,
+ "hooks-libraries": [
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": { }
+ },
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ "high-availability": [ {
+ "this-server-name": "server1",
+ ...
+ } ]
+ }
+ },
+ ...
+ ],
+ ...
+ }
+
+.. note::
+
+ While ``parked-packet-limit`` is not specifically tied to HA, currently HA
+ is the only ISC hook that employs packet parking.
+
+.. _ha-maintenance:
+
+Controlled Shutdown and Maintenance of DHCP Servers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Having a pair of servers providing High Availability allows for controlled
+shutdown and maintenance of those servers without disrupting the DHCP service.
+For example, an administrator can perform an upgrade of one of the servers while
+the other one continues to respond to DHCP queries. When the first server is
+upgraded and back online, the upgrade can be performed for the second server.
+
+A typical problem reported with early versions of the High Availability hook
+library was that the administrator did not have direct control over the state of
+the DHCP server. Shutting down one of the servers for maintenance did not
+necessarily cause the other server to start responding to all DHCP queries,
+because the failure-detection algorithm described in :ref:`ha-scope-transition`
+requires that the partner not respond for a configured period of time and,
+depending on the configuration, may also require that a number of DHCP requests
+not be responded to for a specified period of time. The maintenance procedure,
+however, requires that the administrator be able to instruct one of the servers
+to instantly start serving all DHCP clients, and the other server to instantly
+stop serving any DHCP clients, so it can be safely shut down.
+
+The maintenance feature of the High Availability hook library addresses this
+situation. The :isccmd:`ha-maintenance-start` command was introduced to allow the
+administrator to put the pairs of the active servers in a state in which one of
+them is responding to all DHCP queries and the other one is awaiting shutdown.
+
+Suppose that the HA setup includes two active servers, ``server1`` and
+``server2``, and the latter needs to be shut down for maintenance.
+The administrator can send the :isccmd:`ha-maintenance-start` command to ``server1``,
+as this is the server which is going to handle the DHCP traffic while the other
+one is offline. ``server1`` responds with an error if its state or the partner's
+state does not allow for a maintenance shutdown: for example, if maintenance is
+not supported for the backup server or if the server is in the ``terminated``
+state. Also, an error is returned if the :isccmd:`ha-maintenance-start` request was
+already sent to the other server.
+
+Upon receiving the :isccmd:`ha-maintenance-start` command, ``server1`` sends the
+:isccmd:`ha-maintenance-notify` command to ``server2`` to put it in the
+``in-maintenance`` state. If ``server2`` confirms, ``server1`` transitions to
+the ``partner-in-maintenance`` state. This is similar to the ``partner-down``
+state, except that in the ``partner-in-maintenance`` state ``server1`` continues
+to send lease updates to ``server2`` until the administrator shuts down
+``server2``. ``server1`` now responds to all DHCP queries.
+
+The administrator can now safely shut down ``server2`` in the ``in-maintenance``
+state and perform any necessary maintenance actions. While ``server2`` is
+offline, ``server1`` will obviously not be able to communicate with its partner,
+so it will immediately transition to the ``partner-down`` state; it will
+continue to respond to all DHCP queries but will no longer send lease updates to
+``server2``. Restarting ``server2`` after the maintenance will trigger normal
+state negotiation, lease-database synchronization, and, ultimately, a transition
+to the normal ``load-balancing`` or ``hot-standby`` state. Maintenance can then
+be performed on ``server1``, after sending the :isccmd:`ha-maintenance-start` command
+to ``server2``.
+
+If the :isccmd:`ha-maintenance-start` command was sent to the server and the server
+has transitioned to the ``partner-in-maintenance`` state, it is possible to
+transition both it and its partner back to their previous states to resume the
+normal operation of the HA pair. This is achieved by sending the
+:isccmd:`ha-maintenance-cancel` command to the server that is in the
+``partner-in-maintenance`` state. However, if the server has already
+transitioned to the ``partner-down`` state as a result of detecting that the
+partner is offline, canceling the maintenance is no longer possible. In that
+case, it is necessary to restart the other server and allow it to complete its
+normal state negotiation process.
+
+If the server has many relationships with different partners, the ``ha-maintenance-start``
+command attempts to transition all of the relationships into the
+``partner-in-maintenance`` state by sending the ``ha-mainteance-notify`` to all
+partner servers. If this step fails for any server an error is returned.
+In that case, send the ``ha-maintenance-cancel`` command to resume normal
+operation and fix the issue.
+
+
+Upgrading From Older HA Versions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To upgrade from an older HA hook library to the current version, the
+administrator must shut down one of the servers and rely on the failover
+mechanism to force the online server to transition to the ``partner-down`` state,
+where it starts serving all DHCP clients. Once the hook library on the first
+server is upgraded to a current version, the :isccmd:`ha-maintenance-start` command
+can be used to upgrade the second server.
+
+In such a case, shut down the server running the old version. Next, send the
+:isccmd:`ha-maintenance-start` command to the server that has been upgraded. This
+server should immediately transition to the ``partner-down`` state as it cannot
+communicate with its offline partner. In the ``partner-down`` state the first
+(upgraded) server will respond to all DHCP requests, allowing the administrator
+to perform the upgrade on the second server.
+
+.. note::
+
+ Do not send the :isccmd:`ha-maintenance-start` command while the server running the
+ old hook library is still online. The server receiving this command will
+ return an error.
+
+
+.. _ha-control-commands:
+
+Control Commands for High Availability
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Even though the HA hook library is designed to automatically resolve issues with
+DHCP service interruptions by redirecting the DHCP traffic to a surviving server
+and synchronizing the lease database as needed, it may be useful for the
+administrator to have more control over both servers' behavior. In particular,
+it may be useful to be able to trigger lease-database synchronization on demand,
+or to manually set the HA scopes that are being served.
+
+The backup server can sometimes be used to handle DHCP traffic if both active
+servers are down. The backup server does not perform the failover function
+automatically; thus, in order to use the backup server to respond to DHCP
+queries, the server administrator must enable this function manually.
+
+The following sections describe commands supported by the HA hook library which
+are available for the administrator.
+
+.. isccmd:: ha-sync
+.. _command-ha-sync:
+
+The ``ha-sync`` Command
+-----------------------
+
+The :isccmd:`ha-sync` command instructs the server to synchronize its local lease
+database with the selected peer. The server fetches all leases from the peer and
+updates any locally stored leases which are older than those fetched. It also
+creates new leases when any of those fetched do not exist in the local database.
+All leases that are not returned by the peer but are in the local database are
+preserved. The database synchronization is unidirectional; only the database on
+the server to which the command has been sent is updated. To synchronize the
+peer's database, a separate :isccmd:`ha-sync` command must be issued to that peer.
+
+Database synchronization may be triggered for both active and backup server
+types. The :isccmd:`ha-sync` command has the following structure (in a DHCPv4 example):
+
+::
+
+ {
+ "command": "ha-sync",
+ "service": [ "dhcp4 "],
+ "arguments": {
+ "server-name": "server2",
+ "max-period": 60
+ }
+ }
+
+When the server receives this command it first disables the DHCP service of the
+server from which it will be fetching leases, by sending the :isccmd:`dhcp-disable`
+command to that server. The ``max-period`` parameter specifies the maximum
+duration (in seconds) for which the DHCP service should be disabled. If the DHCP
+service is successfully disabled, the synchronizing server fetches leases from
+the remote server by issuing one or more :isccmd:`lease4-get-page` commands. When the
+lease-database synchronization is complete, the synchronizing server sends the
+:isccmd:`dhcp-enable` command to the peer to re-enable its DHCP service.
+
+The ``max-period`` value should be sufficiently long to guarantee that it does
+not elapse before the synchronization is completed. Otherwise, the DHCP server
+will automatically enable its DHCP function while the synchronization is still
+in progress. If the DHCP server subsequently allocates any leases during the
+synchronization, those new (or updated) leases will not be fetched by the
+synchronizing server, leading to database inconsistencies.
+
+.. isccmd:: ha-scopes
+.. _command-ha-scopes:
+
+The ``ha-scopes`` Command
+-------------------------
+
+This command allows an administrator to modify the HA scopes being served.
+Consult :ref:`ha-load-balancing-config` and :ref:`ha-hot-standby-config` to
+learn which scopes are available for the different HA modes of operation. The
+:isccmd:`ha-scopes` command has the following structure (in a DHCPv4 example):
+
+::
+
+ {
+ "command": "ha-scopes",
+ "service": [ "dhcp4" ],
+ "arguments": {
+ "scopes": [ "HA_server1", "HA_server2" ],
+ "server-name": "server2"
+ }
+ }
+
+This command configures the server to handle traffic from both the "HA_server1"
+and "HA_server2" scopes. To disable all scopes specify an empty list:
+
+::
+
+ {
+ "command": "ha-scopes",
+ "service": [ "dhcp4 "],
+ "arguments": {
+ "scopes": [ ],
+ "server-name": "server2"
+ }
+ }
+
+The optional ``server-name`` parameter specifies a name of one of the partners belonging
+to the HA relationship this command pertains to. This parameter can be omitted if the
+server receiving this command has only one HA relationship in the configuration.
+
+.. isccmd:: ha-continue
+.. _command-ha-continue:
+
+The ``ha-continue`` Command
+---------------------------
+
+This command is used to resume the operation of the paused HA state machine, as
+described in :ref:`ha-pause-state-machine`. It takes no arguments, so the
+command structure is simply:
+
+::
+
+ {
+ "command": "ha-continue",
+ "service": [ "dhcp4" ],
+ "arguments": {
+ "scopes": [ ],
+ "server-name": "server2"
+ }
+ }
+
+The optional ``server-name`` parameter specifies a name of one of the partners belonging
+to the HA relationship this command pertains to. This parameter can be omitted if the
+server receiving this command has only one HA relationship in the configuration.
+
+.. isccmd:: ha-heartbeat
+.. _command-ha-heartbeat:
+
+The ``ha-heartbeat`` Command
+----------------------------
+
+The :ref:`ha-server-states` section describes how the :isccmd:`ha-heartbeat` command
+is used by a pair of active HA servers to detect one partner's failure. This
+command, however, can also be sent by the system administrator to one or both
+servers to check their HA state. This allows a monitoring system to be deployed
+on the HA enabled servers to periodically check whether they are operational or
+whether any manual intervention is required. The :isccmd:`ha-heartbeat` command takes
+no arguments:
+
+::
+
+ {
+ "command": "ha-heartbeat",
+ "service": [ "dhcp4" ],
+ "arguments": {
+ "scopes": [ ],
+ "server-name": "server2"
+ }
+ }
+
+The optional ``server-name`` parameter specifies a name of one of the partners belonging
+to the HA relationship this command pertains to. This parameter can be omitted if the
+server receiving this command has only one HA relationship in the configuration.
+
+Upon successful communication with the server, a response similar to this should
+be returned:
+
+::
+
+ {
+ "result": 0,
+ "text": "HA peer status returned.",
+ "arguments":
+ {
+ "state": "partner-down",
+ "date-time": "Thu, 07 Nov 2019 08:49:37 GMT",
+ "scopes": [ "server1" ],
+ "unsent-update-count": 123
+ }
+ }
+
+The returned ``state`` value should be one of the values listed in
+:ref:`ha-server-states`. In the example above, the ``partner-down`` state is
+returned, which indicates that the server which responded to the command
+believes that its partner is offline; thus, it is serving all DHCP requests sent
+to the servers. To ensure that the partner is indeed offline, the administrator
+should send the :isccmd:`ha-heartbeat` command to the second server. If sending the
+command fails, e.g. due to an inability to establish a TCP connection to the
+Control Agent, or if the Control Agent reports issues with communication with
+the DHCP server, it is very likely that the server is not running.
+
+The ``date-time`` parameter conveys the server's notion of time.
+
+The ``unsent-update-count`` value is a cumulative count of all unsent lease
+updates since the server was booted; its value is set to 0 when the server is
+started. It is never reset to 0 during the server's operation, even after the
+partner synchronizes the database. It is incremented by the partner sending the
+heartbeat response when it cannot send the lease update. For example, suppose
+the failure is a result of a temporary communication interruption. In that case,
+the partner receiving the ``partner-down`` heartbeat response tracks the value
+changes and can determine, once communication is reestablished, whether there
+are any new lease updates that it did not receive. If the values on both servers
+do not match, it is an indication that the partner should synchronize its lease
+database. A non-zero value itself is not an indication of any present issues
+with lease updates, but a constantly incrementing value is.
+
+The typical response returned by one server when both are
+operational is:
+
+::
+
+ {
+ "result": 0,
+ "text": "HA peer status returned.",
+ "arguments":
+ {
+ "state": "load-balancing",
+ "date-time": "Thu, 07 Nov 2019 08:49:37 GMT",
+ "scopes": [ "server1" ],
+ "unsent-update-count": 0
+ }
+ }
+
+In most cases, the :isccmd:`ha-heartbeat` command should be sent to both HA-enabled
+servers to verify the state of the entire HA setup. In particular, if one of the
+servers indicates that it is in the ``load-balancing`` state, it means that this
+server is operating as if its partner is functional. When a partner goes down,
+it takes some time for the surviving server to realize it. The
+:ref:`ha-scope-transition` section describes the algorithm which the surviving
+server follows before it transitions to the ``partner-down`` state. If the
+:isccmd:`ha-heartbeat` command is sent during the time window between the failure of
+one of the servers and the transition of the surviving server to the
+``partner-down`` state, the response from the surviving server does not reflect
+the failure. Resending the command detects the failure once the surviving server
+has entered the ``partner-down`` state.
+
+.. note:
+
+ Always send the :isccmd:`ha-heartbeat` command to both active HA servers to check
+ the state of the entire HA setup. Sending it to only one of the servers may
+ not reflect issues that just began with one of the servers.
+
+.. isccmd:: ha-status-get
+.. _command-ha-status-get:
+
+The ``status-get`` Command
+--------------------------
+
+:isccmd:`status-get` is a general-purpose command supported by several Kea daemons,
+not only the DHCP servers. However, when sent to a DHCP server with HA enabled,
+it can be used to get insight into the details of the HA-specific server status.
+Not only does the response contain the status information of the server
+receiving this command, but also the information about its partner if it is
+available.
+
+The following is an example response to the :isccmd:`status-get` command, including
+the HA status of two ``load-balancing`` servers:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "",
+ "arguments": {
+ "pid": 1234,
+ "uptime": 3024,
+ "reload": 1111,
+ "high-availability": [
+ {
+ "ha-mode": "load-balancing",
+ "ha-servers": {
+ "local": {
+ "role": "primary",
+ "scopes": [ "server1" ],
+ "state": "load-balancing",
+ "server-name": "server1"
+ },
+ "remote": {
+ "age": 10,
+ "in-touch": true,
+ "role": "secondary",
+ "last-scopes": [ "server2" ],
+ "last-state": "load-balancing",
+ "communication-interrupted": true,
+ "connecting-clients": 2,
+ "unacked-clients": 1,
+ "unacked-clients-left": 2,
+ "analyzed-packets": 8,
+ "server-name": "server2"
+ }
+ }
+ }
+ ],
+ "multi-threading-enabled": true,
+ "thread-pool-size": 4,
+ "packet-queue-size": 64,
+ "packet-queue-statistics": [ 0.2, 0.1, 0.1 ],
+ "sockets": {
+ "status": "ready"
+ }
+ }
+ }
+
+The ``high-availability`` argument is a list which currently comprises only one
+element.
+
+The ``ha-servers`` map contains two structures: ``local`` and ``remote``. The
+former contains the status information of the server which received the command,
+while the latter contains the status information known to the local server about
+the partner. The ``role`` of the partner server is gathered from the local
+configuration file, and thus should always be available. The remaining status
+information, such as ``last-scopes`` and ``last-state``, is not available until
+the local server communicates with the remote by successfully sending the
+:isccmd:`ha-heartbeat` command. If at least one such communication has taken place,
+the returned value of the ``in-touch`` parameter is set to ``true``. By
+examining this value, the command's sender can determine whether the information
+about the remote server is reliable.
+
+The ``last-scopes`` and ``last-state`` parameters contain information about the
+HA scopes served by the partner and its state. This information is gathered
+during the :isccmd:`ha-heartbeat` command exchange, so it may not be accurate if a
+communication problem occurs between the partners and this status information is
+not refreshed. In such a case, it may be useful to send the :isccmd:`status-get`
+command to the partner server directly to check its current state. The ``age``
+parameter specifies the age of the information from the partner, in seconds.
+
+The ``communication-interrupted`` boolean value indicates whether the server
+receiving the :isccmd:`status-get` command (the local server) has been unable to
+communicate with the partner longer than the duration specified as
+``max-response-delay``. In such a situation, the active servers are considered
+to be in the ``communication-interrupted`` state. At this point, the local
+server may start monitoring the DHCP traffic directed to the partner to see if
+the partner is responding to this traffic. More about the failover procedure can
+be found in :ref:`ha-load-balancing-config`.
+
+The ``connecting-clients``, ``unacked-clients``, ``unacked-clients-left``, and
+``analyzed-packets`` parameters were introduced along with the
+``communication-interrupted`` parameter and they convey useful information about
+the state of the DHCP traffic monitoring in the ``communication-interrupted``
+state. Once the server leaves the ``communication-interrupted`` state, these
+parameters are all reset to 0.
+
+These parameters have the following meaning in the ``communication-interrupted``
+state:
+
+- ``connecting-clients`` - this is the number of different clients which have
+ attempted to get a lease from the remote server. These clients are
+ differentiated by their MAC address and client identifier (in DHCPv4) or DUID
+ (in DHCPv6). This number includes "unacked" clients (for which the "secs"
+ field or "elapsed time" value exceeded the ``max-response-delay``).
+
+- ``unacked-clients`` - this is the number of different clients which have been
+ considered "unacked", i.e. the clients which have been trying to get the
+ lease longer than the value of the "secs" field, or for which the
+ "elapsed time" exceeded the ``max-response-delay`` setting.
+
+- ``unacked-clients-left`` - this indicates the number of additional clients
+ which have to be considered "unacked" before the server enters the
+ ``partner-down`` state. This value decreases when the ``unacked-clients``
+ value increases. The local server enters the ``partner-down`` state when this
+ value decreases to 0.
+
+- ``analyzed-packets`` - this is the total number of packets directed to the
+ partner server and analyzed by the local server since entering the
+ communication interrupted state. It includes retransmissions from the same
+ clients.
+
+Monitoring these values helps to predict when the local server will enter the
+``partner-down`` state or to understand why the server has not yet entered this
+state.
+
+The ``ha-mode`` parameter returns the HA mode of operation selected using the
+``mode`` parameter in the configuration file. It can hold one of the following
+values: ``load-balancing``, ``hot-standby``, or ``passive-backup``.
+
+The :isccmd:`status-get` response has the format described above only in the
+``load-balancing`` and ``hot-standby`` modes. In the ``passive-backup`` mode the
+``remote`` map is not included in the response because in this mode there is
+only one active server (local). The response includes no information about the
+status of the backup servers.
+
+.. isccmd:: ha-maintenance-start
+.. _command-ha-maintenance-start:
+
+The ``ha-maintenance-start`` Command
+------------------------------------
+
+This command is used to initiate the transition of the server's partners into the
+``in-maintenance`` state and the transition of the server receiving the command
+into the ``partner-in-maintenance`` state in each HA relationship. See the
+:ref:`ha-maintenance` section for details.
+
+::
+
+ {
+ "command": "ha-maintenance-start",
+ "service": [ "dhcp4" ]
+ }
+
+.. isccmd:: ha-maintenance-cancel
+.. _command-ha-maintenance-cancel:
+
+The ``ha-maintenance-cancel`` Command
+-------------------------------------
+
+This command is used to cancel the maintenance previously initiated using the
+:isccmd:`ha-maintenance-start` command. The server receiving this command will first
+send :isccmd:`ha-maintenance-notify`, with the ``cancel`` flag set to ``true``, to its
+partners. Next, the server reverts from the ``partner-in-maintenance`` state to
+its previous state. See the :ref:`ha-maintenance` section for details.
+
+::
+
+ {
+ "command": "ha-maintenance-cancel",
+ "service": [ "dhcp4" ]
+ }
+
+.. isccmd:: ha-maintenance-notify
+.. _command-ha-maintenance-notify:
+
+The ``ha-maintenance-notify`` Command
+-------------------------------------
+
+This command is sent by the server receiving the :isccmd:`ha-maintenance-start` or the
+:isccmd:`ha-maintenance-cancel` command to its partner, to cause the partner to
+transition to the ``in-maintenance`` state or to revert from this state to a
+previous state. See the :ref:`ha-maintenance` section for details.
+
+::
+
+ {
+ "command": "ha-maintenance-notify",
+ "service": [ "dhcp4" ],
+ "arguments": {
+ "cancel": false,
+ "server-name": "server2"
+ }
+ }
+
+The optional ``server-name`` parameter specifies a name of one of the partners belonging
+to the HA relationship this command pertains to. This parameter can be omitted if the
+server receiving this command has only one HA relationship in the configuration.
+
+.. warning::
+
+ The :isccmd:`ha-maintenance-notify` command is not meant to be used by system
+ administrators. It is used for internal communication between a pair of
+ HA-enabled DHCP servers. Direct use of this command is not supported and may
+ produce unintended consequences.
+
+.. isccmd:: ha-reset
+.. _command-ha-reset:
+
+The ``ha-reset`` Command
+------------------------
+
+This command causes the server to reset its High Availability state machine by
+transitioning it to the ``waiting`` state. A partner in the
+``communication-recovery`` state may send this command to cause the server
+to synchronize its lease database. Database synchronization is required when the
+partner has failed to send all lease database updates after re-establishing
+connection after a temporary connection failure. It is also required when the
+``delayed-updates-limit`` is exceeded, when the server is in the
+``communication-recovery`` state.
+
+A server administrator may send this command to reset a misbehaving state
+machine.
+
+::
+
+ {
+ "command": "ha-reset",
+ "service": [ "dhcp4" ],
+ "arguments": {
+ "server-name": "server2"
+ }
+ }
+
+The optional ``server-name`` parameter specifies a name of one of the partners belonging
+to the HA relationship this command pertains to. This parameter can be omitted if the
+server receiving this command has only one HA relationship in the configuration.
+
+It elicits the response:
+
+::
+
+ {
+ "result": 0,
+ "text": "HA state machine reset."
+ }
+
+If the server receiving this command is already in the ``waiting`` state, the
+command has no effect.
+
+.. isccmd:: ha-sync-complete-notify
+.. _command-ha-sync-complete-notify:
+
+The ``ha-sync-complete-notify`` Command
+---------------------------------------
+
+A server sends this command to its partner to signal that it has completed
+lease-database synchronization. The partner may enable its DHCP service if it
+can allocate new leases in its current state. The partner does not enable the
+DHCP service in the ``partner-down`` state until it sends a successful :isccmd:`ha-heartbeat`
+test to its partner server. If the connection is still unavailable, the server
+in the ``partner-down`` state enables its own DHCP service to continue
+responding to clients.
+
+::
+
+ {
+ "command": "ha-sync-complete-notify",
+ "service": [ "dhcp4" ],
+ "arguments": {
+ "origin": 2000,
+ "server-name": "server2"
+ }
+ }
+
+The optional ``server-name`` parameter specifies a name of one of the partners belonging
+to the HA relationship this command pertains to. This parameter can be omitted if the
+server receiving this command has only one HA relationship in the configuration.
+
+The ``origin`` parameter is used to select the HA service for which the receiving server should
+enable the DHCP service when it receives this notification. This is the same origin the
+sending server used previously to disable the DHCP service before synchronization.
+
+It elicits the response:
+
+::
+
+ {
+ "result": 0,
+ "text": "Server successfully notified about the synchronization completion."
+ }
+
+.. warning::
+
+ The :isccmd:`ha-sync-complete-notify` command is not meant to be used by system
+ administrators. It is used for internal communication between a pair of
+ HA-enabled DHCP servers. Direct use of this command is not supported and may
+ produce unintended consequences.
+
+
+.. _ha-hub-and-spoke:
+
+Hub and Spoke Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The hub-and-spoke is a common arrangement of the DHCP servers for resiliency. It contains
+one central server and multiple branch servers. The branch servers are the primary servers
+in the ``hot-standby`` mode and respond to the local DHCP traffic in their respective
+locations. The central server acts as a standby server for each branch server.
+It maintains independent state machines with the branch servers, called relationships.
+If one of the branch servers experiences a failure, the central server can take over its
+DHCP traffic. In this case, we say that one of the central server's relationships is in
+the ``partner-down`` state. The remaining relationships may still be in the ``hot-standby``
+state and not actively respond to DHCP traffic. When the branch server becomes active again,
+it synchronizes the lease database with the central server, and the central server becomes
+fully passive again. In rare cases, when multiple branch servers stop, the central server
+takes responsibility for all their traffic (possibly the entire DHCP traffic in the network
+when all branch servers are down). A simple hub-and-spoke arrangement consisting of two
+branch servers and one central server is shown below.
+
+::
+
+
+ +----- Central Server ------+
+ | |
+ +----------+ relationship 1 | +----------+----------+ | relationship 2 +----------+
+ | Server 1 |===================| Server 2 | Server 4 |===================| Server 3 |
+ +----------+ | +----------+----------+ | +----------+
+ | |
+ +---------------------------+
+
+Each branch server's configuration comprises a set of subnets appropriate for the branch
+server. Different branch servers serve different subnets. The central server's configuration
+comprises all subnets of the branch servers so that it can respond to the DHCP traffic
+directed to any of the failing branch servers. The subnets in the central server must be
+grouped into relationships like in the snippet below:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ "enp0s8", "enp0s9" ]
+ },
+ "hooks-libraries": [
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": {}
+ },
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ "high-availability": [
+ {
+ "this-server-name": "server2",
+ "mode": "hot-standby",
+ "multi-threading": {
+ "enable-multi-threading": true,
+ "http-dedicated-listener": true,
+ "http-listener-threads": 4,
+ "http-client-threads": 4
+ },
+ "peers": [
+ {
+ "name": "server1",
+ "url": "http://192.168.56.66:8000/",
+ "role": "primary",
+ "auto-failover": true
+ },
+ {
+ "name": "server2",
+ "url": "http://192.168.56.33:8000/",
+ "role": "standby",
+ "auto-failover": true
+ }
+ ]
+ },
+ {
+ "this-server-name": "server4",
+ "mode": "hot-standby",
+ "multi-threading": {
+ "enable-multi-threading": true,
+ "http-dedicated-listener": true,
+ "http-listener-threads": 4,
+ "http-client-threads": 4
+ },
+ "peers": [
+ {
+ "name": "server3",
+ "url": "http://192.168.57.99:8000/",
+ "role": "primary",
+ "auto-failover": true
+ },
+ {
+ "name": "server4",
+ "url": "http://192.168.57.33:8000/",
+ "role": "standby",
+ "auto-failover": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ],
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8:1::/64",
+ "pools": [ { "pool": "2001:db8:1::/80" } ],
+ "interface": "enp0s8",
+ "user-context": {
+ "ha-server-name": "server2"
+ }
+ },
+ {
+ "id": 2,
+ "subnet": "2001:db8:2::/64",
+ "pools": [ { "pool": "2001:db8:2::/80" } ],
+ "interface": "enp0s9",
+ "user-context": {
+ "ha-server-name": "server4"
+ }
+ }
+ ]
+ }
+ }
+
+
+
+
+The peer names in the relationships must be unique. The user context for each subnet contains
+the ``ha-server-name`` parameter associating a subnet with a relationship. The ``ha-server-name``
+can be any of the peer names in the relationship. Suppose a relationship contains peer names
+``server1`` and ``server2``. It doesn't matter whether the ``ha-server-name`` is ``server1`` or
+``server2``. In both cases, it associates a subnet with that relationship.
+
+It is not required to specify the ``ha-server-name`` in the branch servers, assuming that the
+branch servers only contain the subnets they serve. Consider the following configuration for
+branch ``server3``:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "interfaces-config": {
+ "interfaces": [ "enp0s8" ]
+ },
+ "hooks-libraries": [
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
+ "parameters": {}
+ },
+ {
+ "library": "/usr/lib/kea/hooks/libdhcp_ha.so",
+ "parameters": {
+ "high-availability": [
+ {
+ "this-server-name": "server3",
+ "mode": "hot-standby",
+ "multi-threading": {
+ "enable-multi-threading": true,
+ "http-dedicated-listener": true,
+ "http-listener-threads": 4,
+ "http-client-threads": 4
+ },
+ "peers": [
+ {
+ "name": "server3",
+ "url": "http://192.168.57.99:8000/",
+ "role": "primary",
+ "auto-failover": true
+ },
+ {
+ "name": "server4",
+ "url": "http://192.168.57.33:8000/",
+ "role": "standby",
+ "auto-failover": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ],
+ "subnet6": [
+ {
+ "id": 2,
+ "subnet": "2001:db8:2::/64",
+ "pools": [
+ {
+ "pool": "2001:db8:2::/80"
+ }
+ ],
+ "interface": "enp0s8",
+ "user-context": {
+ "ha-server-name": "server3"
+ }
+ }
+ ]
+ }
+ }
+
+.. note::
+
+ Even though it is not required to include the ``ha-server-name`` user context parameters in the
+ branch servers, we recommend including them. The servers fetch all leases from the
+ partners during the database synchronization. If the subnets are not explicitly associated with
+ the relationship, the branch server inserts all fetched leases from the central server (including
+ those from other relationships) into its database. Specifying ``ha-server-name`` parameter for
+ each configured subnet in the branch server guarantees that only the leases belonging to its
+ relationship are inserted into the branch server's database.
+
+.. note::
+
+ The peer names in the branch servers must match the peer names in the respective central
+ server's relationships because these names are used for signaling between the HA partners.
diff --git a/doc/sphinx/arm/hooks-host-cache.rst b/doc/sphinx/arm/hooks-host-cache.rst
new file mode 100644
index 0000000..ebf0cd9
--- /dev/null
+++ b/doc/sphinx/arm/hooks-host-cache.rst
@@ -0,0 +1,324 @@
+.. ischooklib:: libdhcp_host_cache.so
+.. _hooks-host-cache:
+
+``libdhcp_host_cache.so``: Host Cache Reservations for Improved Performance
+===========================================================================
+
+Some database backends, such as RADIUS, are slow and may take
+a long time to respond. Since Kea in general is synchronous, backend
+performance directly affects DHCP performance. To minimize the
+impact and improve performance, the Host Cache library provides a way to
+cache information from the database locally. This includes negative
+caching, i.e. the ability to remember that there is no client
+information in the database.
+
+.. note::
+
+ :ischooklib:`libdhcp_host_cache.so` is available only to ISC customers with
+ a paid support contract. For more information on subscription options,
+ please complete the form at https://www.isc.org/contact.
+
+.. note::
+
+ This library can only be loaded by the :iscman:`kea-dhcp4` or
+ :iscman:`kea-dhcp6` process.
+
+In principle, this hook library can be used with any backend that may
+introduce performance degradation (MySQL, PostgreSQL or RADIUS). Host Cache
+must be loaded for the RADIUS accounting mechanism to work.
+
+The Host Cache hook library is very simple. It takes only one
+optional parameter (``maximum``), which defines the maximum number of hosts
+to be cached. If not specified, the default value of 0 is used, which
+means there is no limit. This hook library can be loaded the same way as
+any other hook library; for example, this configuration could be used:
+
+::
+
+ "Dhcp4": {
+
+ # Your regular DHCPv4 configuration parameters here.
+
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/kea/hooks/libdhcp_host_cache.so",
+ "parameters": {
+
+ # Tells Kea to never cache more than 1000 hosts.
+ "maximum": 1000
+
+ }
+ } ],
+ ...
+ }
+
+Once loaded, the Host Cache hook library provides a number of new
+commands which can be used either over the control channel (see
+:ref:`ctrl-channel-client`) or the RESTful API (see
+:ref:`agent-overview`). An example RESTful API client is described in
+:ref:`shell-overview`. The following sections describe the commands
+available.
+
+.. isccmd:: cache-flush
+.. _command-cache-flush:
+
+The ``cache-flush`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command allows removal of a specified number of cached host
+entries. It takes one parameter, which defines the number of hosts to be
+removed. An example usage looks as follows:
+
+::
+
+ {
+ "command": "cache-flush",
+ "arguments": 1000
+ }
+
+This command removes 1000 hosts; to delete *all* cached
+hosts, use :isccmd:`cache-clear` instead. The hosts are stored in FIFO
+(first-in, first-out) order, so the oldest entries are always removed.
+
+.. isccmd:: cache-clear
+.. _command-cache-clear:
+
+The ``cache-clear`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command allows removal of all cached host entries. An example usage
+looks as follows:
+
+::
+
+ {
+ "command": "cache-clear"
+ }
+
+This command removes all hosts. To delete only a certain
+number of cached hosts, please use :isccmd:`cache-flush` instead.
+
+.. isccmd:: cache-size
+.. _command-cache-size:
+
+The ``cache-size`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command returns the number of host entries. An example usage looks
+as follows:
+
+::
+
+ {
+ "command": "cache-size"
+ }
+
+.. isccmd:: cache-write
+.. _command-cache-write:
+
+The ``cache-write`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In general, the cache content is considered a runtime state and the
+server can be shut down or restarted as usual; the cache is then
+repopulated after restart. However, there are some cases when it is
+useful to store the contents of the cache. One such case is RADIUS,
+where the cached hosts also retain additional cached RADIUS attributes;
+there is no easy way to obtain this information again, because renewing
+clients send their packet to the DHCP server directly. Another use case
+is when an administrator wants to restart the server and, for performance reasons,
+wants it to start with a hot (populated) cache.
+
+This command allows writing the contents of the in-memory cache to a
+file on disk. It takes one parameter, which defines the filename. An
+example usage looks as follows:
+
+::
+
+ {
+ "command": "cache-write",
+ "arguments": "/tmp/kea-host-cache.json"
+ }
+
+This causes the contents to be stored in the ``/tmp/kea-host-cache.json``
+file. That file can then be loaded with the :isccmd:`cache-load` command or
+processed by any other tool that is able to understand JSON format.
+
+.. isccmd:: cache-load
+.. _command-cache-load:
+
+The ``cache-load`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+See the previous section for a discussion of use cases where it may be
+useful to write and load contents of the host cache to disk.
+
+This command allows the contents of a file on disk to be loaded into an
+in-memory cache. It takes one parameter, which defines the filename. An
+example usage looks as follows:
+
+::
+
+ {
+ "command": "cache-load",
+ "arguments": "/tmp/kea-host-cache.json"
+ }
+
+This command stores the contents to the ``/tmp/kea-host-cache.json``
+file. That file can then be loaded with the :isccmd:`cache-load` command or
+processed by any other tool that is able to understand JSON format.
+
+.. isccmd:: cache-get
+.. _command-cache-get:
+
+The ``cache-get`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is similar to :isccmd:`cache-write`, but instead of writing the cache
+contents to disk, it returns the contents to whoever sent the command.
+
+This command allows the contents of a file on disk to be loaded into an
+in-memory cache. It takes one parameter, which defines the filename. An
+example usage looks as follows:
+
+::
+
+ {
+ "command": "cache-get"
+ }
+
+This command returns all the cached hosts; the response
+may be large.
+
+.. isccmd:: cache-get-by-id
+.. _command-cache-get-by-id:
+
+The ``cache-get-by-id`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is similar to :isccmd:`cache-get`, but instead of returning the whole
+content it returns only the entries matching the given identifier.
+
+It takes one parameter, which defines the identifier of wanted cached
+host reservations. An example usage looks as follows:
+
+::
+
+ {
+ "command": "cache-get-by-id",
+ "arguments": {
+ "hw-address": "01:02:03:04:05:06"
+ }
+ }
+
+This command returns all the cached hosts with the given hardware
+address.
+
+.. isccmd:: cache-insert
+.. _command-cache-insert:
+
+The ``cache-insert`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command may be used to manually insert a host into the cache; there
+are very few use cases when this command might be useful. This command
+expects its arguments to follow the usual syntax for specifying host
+reservations (see :ref:`host-reservation-v4` or
+:ref:`host-reservation-v6`), with one difference: the ``subnet-id``
+value must be explicitly specified.
+
+An example command to insert an IPv4 host into the host cache
+looks as follows:
+
+::
+
+ {
+ "command": "cache-insert",
+ "arguments": {
+ "hw-address": "01:02:03:04:05:06",
+ "subnet-id4": 4,
+ "subnet-id6": 0,
+ "ip-address": "192.0.2.100",
+ "hostname": "somehost.example.org",
+ "client-classes4": [ ],
+ "client-classes6": [ ],
+ "option-data4": [ ],
+ "option-data6": [ ],
+ "next-server": "192.0.0.2",
+ "server-hostname": "server-hostname.example.org",
+ "boot-file-name": "bootfile.efi",
+ "host-id": 0
+ }
+ }
+
+An example command to insert an IPv6 host into the host cache
+looks as follows:
+
+::
+
+ {
+ "command": "cache-insert",
+ "arguments": {
+ "hw-address": "01:02:03:04:05:06",
+ "subnet-id4": 0,
+ "subnet-id6": 6,
+ "ip-addresses": [ "2001:db8::cafe:babe" ],
+ "prefixes": [ "2001:db8:dead:beef::/64" ],
+ "hostname": "",
+ "client-classes4": [ ],
+ "client-classes6": [ ],
+ "option-data4": [ ],
+ "option-data6": [ ],
+ "next-server": "0.0.0.0",
+ "server-hostname": "",
+ "boot-file-name": "",
+ "host-id": 0
+ }
+ }
+
+.. isccmd:: cache-remove
+.. _command-cache-remove:
+
+The ``cache-remove`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes it is useful to remove a single entry from the host cache: for
+example, consider a situation where the device is active, Kea has already
+provided configuration, and the host entry is in cache. As a result of
+administrative action (e.g. the customer hasn't paid their bills or has
+been upgraded to better service), the information in the backend database
+(e.g. MySQL or RADIUS) is being updated. However, since the cache is in use,
+Kea does not notice the change as the cached values are used. The
+:isccmd:`cache-remove` command can solve this problem by removing a cached entry
+after administrative changes.
+
+The :isccmd:`cache-remove` command works similarly to the :isccmd:`reservation-get` command.
+It allows querying by two parameters: either ``subnet-id4`` or ``subnet-id6``;
+or ``ip-address`` (may be an IPv4 or IPv6 address), ``hw-address`` (specifies
+a hardware/MAC address), ``duid``, ``circuit-id``, ``client-id``, or ``flex-id``.
+
+An example command to remove an IPv4 host with reserved address
+192.0.2.1 from a subnet with a ``subnet-id`` 123 looks as follows:
+
+::
+
+ {
+ "command": "cache-remove",
+ "arguments": {
+ "ip-address": "192.0.2.1",
+ "subnet-id": 123
+ }
+ }
+
+Another example that removes an IPv6 host identifier by DUID and
+specific ``subnet-id`` is:
+
+::
+
+ {
+ "command": "cache-remove",
+ "arguments": {
+ "duid": "00:01:ab:cd:f0:a1:c2:d3:e4",
+ "subnet-id": 123
+ }
+ }
diff --git a/doc/sphinx/arm/hooks-host-cmds.rst b/doc/sphinx/arm/hooks-host-cmds.rst
new file mode 100644
index 0000000..bcfc54f
--- /dev/null
+++ b/doc/sphinx/arm/hooks-host-cmds.rst
@@ -0,0 +1,1212 @@
+.. ischooklib:: libdhcp_host_cmds.so
+.. _hooks-host-cmds:
+
+``libdhcp_host_cmds.so``: Host Commands
+=======================================
+
+Kea can store host reservations in a database; in many larger deployments,
+it is useful to be able to manage that information while the server is
+running. The Host Commands library provides management commands for adding, querying,
+and deleting host reservations in a safe way without restarting the
+server. In particular, it validates the parameters, so an attempt to
+insert incorrect data - such as adding a host with a conflicting identifier in the
+same subnet - is rejected. Those commands are exposed via the command
+channel (JSON over UNIX sockets) and the Control Agent (JSON over a RESTful
+interface).
+
+.. note::
+
+ :ischooklib:`libdhcp_host_cmds.so` is available as a premium
+ hook library from ISC. Please visit https://www.isc.org/shop/ to purchase
+ the premium hook libraries, or contact us at https://www.isc.org/contact for
+ more information.
+
+.. note::
+
+ This library can only be loaded by the :iscman:`kea-dhcp4` or :iscman:`kea-dhcp6`
+ process.
+
+Currently, the following commands are supported:
+
+- :isccmd:`reservation-add`, which adds a new host reservation.
+
+- :isccmd:`reservation-get`, which returns an existing reservation if specified
+ criteria are matched.
+
+- :isccmd:`reservation-get-all`, which returns all reservations in a specified subnet.
+
+- :isccmd:`reservation-get-page`, a variant of :isccmd:`reservation-get-all` that returns
+ reservations by pages, either all or in a specified subnet.
+
+- :isccmd:`reservation-get-by-address`, which returns all reservations with a
+ specified IP address or a delegated prefix (without a prefix length), and optionally a subnet id.
+
+- :isccmd:`reservation-get-by-hostname`, which returns all reservations with a
+ specified hostname and optionally in a subnet.
+
+- :isccmd:`reservation-get-by-id`, which returns all reservations with a specified
+ identifier (since Kea version 1.9.0).
+
+- :isccmd:`reservation-del`, which attempts to delete a reservation matching specified
+ criteria.
+
+- :isccmd:`reservation-update`, which updates (replaces) an existing reservation
+ matching the given identifiers in a subnet.
+
+To use the commands that change reservation information
+(i.e. :isccmd:`reservation-add`, :isccmd:`reservation-del`, and :isccmd:`reservation-update`) to
+modify data stored in the host database, the hosts database must be specified
+and it must not operate in read-only mode (for details, see the
+``hosts-databases`` descriptions in :ref:`hosts-databases-configuration4` and
+:ref:`hosts-databases-configuration6`). If the ``hosts-databases`` are not
+specified or are running in read-only mode, :ischooklib:`libdhcp_host_cmds.so` will
+load, but any attempts to use :isccmd:`reservation-add`, :isccmd:`reservation-del`, and
+:isccmd:`reservation-update` to modify data in that database will fail.
+
+These commands can also modify hosts from the JSON configuration independently
+from the hosts database. The changes provided in the JSON configuration are
+volatile and can be made permanent by sending the :isccmd:`config-write` command.
+
+For a description of proposed future commands, see the `Control API
+Requirements <https://gitlab.isc.org/isc-projects/kea/wikis/designs/commands>`__
+document.
+
+All host commands use JSON syntax. They can be issued either using the
+control channel (see :ref:`ctrl-channel`) or via the Control Agent (see
+:ref:`kea-ctrl-agent`).
+
+The library can be loaded similarly to other hook libraries. It
+does not take any parameters, and it supports both the DHCPv4 and DHCPv6
+servers.
+
+::
+
+ "Dhcp6": {
+ "hooks-libraries": [
+ {
+ "library": "/path/libdhcp_host_cmds.so"
+ },
+ ...
+ ]
+ }
+
+The ``subnet-id`` Parameter
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Before examining the individual commands, it is worth discussing the
+parameter ``subnet-id``. Currently this parameter is mandatory for all of the
+commands supplied by this library, with the exception of
+:isccmd:`reservation-get-by-hostname` and :isccmd:`reservation-get-by-address`,
+where it is optional. Since Kea 1.9.0,
+``subnet-id`` is also optional in :isccmd:`reservation-get-page`, and
+it is forbidden in :isccmd:`reservation-get-by-id`.
+
+Reservations can be specified globally, and are not necessarily specific to any
+subnet. When reservations are supplied via the configuration file, the
+ID of the containing subnet (or lack thereof) is implicit in the
+configuration structure. However, when managing reservations using
+host commands, it is necessary to explicitly identify the scope to which
+the reservation belongs. This is done via the ``subnet-id`` parameter.
+For global reservations, use a value of zero (0). For reservations
+scoped to a specific subnet, use that subnet's ID.
+
+On the other hand, when the ``subnet-id`` is not specified in the command
+parameters, it is added to each host in responses. If the ``subnet-id``
+has the unused special value, this means the host entry belongs only
+to the other IP version (i.e. IPv6 in DHCPv4 server or IPv4 in DHCPv6
+server) and this entry is ignored.
+
+The ``operation-target`` Parameter
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Most host commands accept the ``operation-target`` parameter that selects
+the host data source. The commands may process data from the server
+configuration (i.e., memory operation target), a database (database target),
+or both of them (all sources). The operation target parameter is optional.
+By default, the commands that only read the data use all data sources
+(memory and database); the commands that modify the state (i.e., :isccmd:`reservation-add`,
+:isccmd:`reservation-del`, and :isccmd:`reservation-update`) only use the
+database target.
+
+The ``operation-target`` parameter accepts the following values:
+
+- ``memory`` - query or update the runtime server configuration.
+- ``database`` - query or update host database(s).
+- ``all`` - query or update both runtime configuration and host database(s).
+- ``default`` - query or update a default host data source - it is command specific.
+
+.. isccmd:: reservation-add
+.. _command-reservation-add:
+
+The ``reservation-add`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`reservation-add` allows for the insertion of a new host. It takes a
+set of arguments that vary depending on the nature of the host
+reservation. Any parameters allowed in the configuration file that
+pertain to host reservation are permitted here. For details regarding
+IPv4 reservations, see :ref:`host-reservation-v4`; for IPv6 reservations, see
+:ref:`host-reservation-v6`. The ``subnet-id`` is mandatory. Use a
+value of zero (0) to add a global reservation, or the ID of the subnet
+to which the reservation should be added. The command can be as simple as having
+only the two mandatory entries:
+
+.. code-block:: json
+
+ {
+ "command": "reservation-add",
+ "arguments": {
+ "reservation": {
+ "subnet-id": 1,
+ "hw-address": "1a:1b:1c:1d:1e:1f"
+ }
+ }
+ }
+
+In that case, however, it does not assign any resources to the host. An IPv4
+address can be assigned like so:
+
+.. code-block:: json
+
+ {
+ "command": "reservation-add",
+ "arguments": {
+ "reservation": {
+ "subnet-id": 1,
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "ip-address": "192.0.2.202"
+ }
+ }
+ }
+
+It can also take many more parameters, for example:
+
+.. code-block:: json
+
+ {
+ "command": "reservation-add",
+ "arguments": {
+ "reservation": {
+ "subnet-id": 1,
+ "client-id": "01:0a:0b:0c:0d:0e:0f",
+ "ip-address": "192.0.2.205",
+ "next-server": "192.0.2.1",
+ "server-hostname": "hal9000",
+ "boot-file-name": "/dev/null",
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "data": "10.1.1.202,10.1.1.203"
+ }
+ ],
+ "client-classes": [ "special_snowflake", "office" ]
+ }
+ }
+ }
+
+Here is an example of a complex IPv6 reservation:
+
+.. code-block:: json
+
+ {
+ "command": "reservation-add",
+ "arguments": {
+ "reservation": {
+ "subnet-id": 1,
+ "duid": "01:02:03:04:05:06:07:08:09:0A",
+ "ip-addresses": [ "2001:db8:1:cafe::1" ],
+ "prefixes": [ "2001:db8:2:abcd::/64" ],
+ "hostname": "foo.example.com",
+ "option-data": [
+ {
+ "name": "vendor-opts",
+ "data": "4491"
+ },
+ {
+ "name": "tftp-servers",
+ "space": "vendor-4491",
+ "data": "3000:1::234"
+ }
+ ]
+ }
+ }
+ }
+
+The command accepts the ``operation-target`` argument. By default, it adds the
+reservation to the hosts database only.
+
+.. code-block:: json
+
+ {
+ "command": "reservation-add",
+ "arguments": {
+ "reservation": {},
+ "operation-target": "all"
+ }
+ }
+
+The command returns a status that indicates either success (result 0)
+or failure (result 1). A failed command always includes a text parameter
+that explains the cause of the failure. Here's an example of a successful
+addition:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "Host added."
+ }
+
+And here's an example of a failure:
+
+.. code-block:: json
+
+ {
+ "result": 1,
+ "text": "Mandatory 'subnet-id' parameter missing."
+ }
+
+
+As :isccmd:`reservation-add` is expected to store the host, the ``hosts-databases``
+parameter must be specified in the configuration, and databases must not
+run in read-only mode.
+
+.. isccmd:: reservation-get
+.. _command-reservation-get:
+
+The ``reservation-get`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`reservation-get` can be used to query the host database and retrieve
+existing reservations. This command supports two types of parameters:
+(``subnet-id``, ``address``) or (``subnet-id``, ``identifier-type``,
+``identifier``). The first type of query is used when the address (either
+IPv4 or IPv6) is known, but the details of the reservation are not. One
+common use for this type of query is to find out whether a given
+address is reserved. The second query uses identifiers. For
+maximum flexibility, Kea stores the host identifying information as a
+pair of values: the type and the actual identifier. Currently supported
+identifiers are ``"hw-address"``, ``"duid"``, ``"circuit-id"``, ``"client-id"``, and
+``"flex-id"``. The ``subnet-id`` is mandatory. Use a value
+of zero (0) to fetch a global reservation, or the ID of the subnet to
+which the reservation belongs.
+
+An example command for getting a host reservation by a (``subnet-id``,
+``address``) pair looks as follows:
+
+::
+
+ {
+ "command": "reservation-get",
+ "arguments": {
+ "subnet-id": 1,
+ "ip-address": "192.0.2.202"
+ }
+ }
+
+An example query by (``subnet-id``, ``identifier-type``, ``identifier``) looks as
+follows:
+
+::
+
+ {
+ "command": "reservation-get",
+ "arguments": {
+ "subnet-id": 4,
+ "identifier-type": "hw-address",
+ "identifier": "01:02:03:04:05:06"
+ }
+ }
+
+The command accepts the ``operation-target`` argument. By default, it gets the
+reservation from both JSON configuration and the hosts database.
+
+.. code-block:: json
+
+ {
+ "command": "reservation-get",
+ "arguments": {
+ "subnet-id": 1,
+ "ip-address": "192.0.2.202",
+ "operation-target": "alternate"
+ }
+ }
+
+Command :isccmd:`reservation-get` typically returns the result 0 when a query was
+conducted properly. In particular, 0 is returned when the host was not
+found. If the query was successful, the host parameters are
+returned. An example of a query that did not find the host looks as
+follows:
+
+::
+
+ { "result": 0, "text": "Host not found." }
+
+Here's an example of a result returned when the host was found successfully:
+
+::
+
+ {
+ "arguments": {
+ "boot-file-name": "bootfile.efi",
+ "client-classes": [
+
+ ],
+ "hostname": "somehost.example.org",
+ "hw-address": "01:02:03:04:05:06",
+ "ip-address": "192.0.2.100",
+ "next-server": "192.0.0.2",
+ "option-data": [
+
+ ],
+ "server-hostname": "server-hostname.example.org",
+ "subnet-id": 4
+ },
+ "result": 0,
+ "text": "Host found."
+ }
+
+An example result returned when the query was malformed might look like this:
+
+::
+
+ { "result": 1, "text": "No 'ip-address' provided and 'identifier-type' is either missing or not a string." }
+
+.. isccmd:: reservation-get-all
+.. _command-reservation-get-all:
+
+The ``reservation-get-all`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`reservation-get-all` can be used to query the host database and
+retrieve all reservations in a specified subnet. This command uses
+parameters providing the mandatory ``subnet-id``. Global host reservations
+can be retrieved by using a ``subnet-id`` value of zero (0).
+
+For instance, retrieving host reservations for the subnet 1:
+
+::
+
+ {
+ "command": "reservation-get-all",
+ "arguments": {
+ "subnet-id": 1
+ }
+ }
+
+returns some IPv4 hosts:
+
+::
+
+ {
+ "arguments": {
+ "hosts": [
+ {
+ "boot-file-name": "bootfile.efi",
+ "client-classes": [ ],
+ "hostname": "somehost.example.org",
+ "hw-address": "01:02:03:04:05:06",
+ "ip-address": "192.0.2.100",
+ "next-server": "192.0.0.2",
+ "option-data": [ ],
+ "server-hostname": "server-hostname.example.org",
+ "subnet-id": 1
+ },
+ {
+ "boot-file-name": "bootfile.efi",
+ "client-classes": [ ],
+ "hostname": "otherhost.example.org",
+ "hw-address": "01:02:03:04:05:ff",
+ "ip-address": "192.0.2.200",
+ "next-server": "192.0.0.2",
+ "option-data": [ ],
+ "server-hostname": "server-hostname.example.org",
+ "subnet-id": 1
+ },
+ ...
+ ]
+ },
+ "result": 0,
+ "text": "72 IPv4 host(s) found."
+ }
+
+The response returned by :isccmd:`reservation-get-all` can be very long. The
+DHCP server does not handle DHCP traffic while preparing a response to
+:isccmd:`reservation-get-all`, so if there are many reservations in a subnet, this
+may be disruptive; use with caution. For larger deployments, please
+consider using :isccmd:`reservation-get-page` instead.
+
+The command accepts the ``operation-target`` argument. By default, it gets the
+reservation from both JSON configuration and the hosts database.
+
+.. code-block:: json
+
+ {
+ "command": "reservation-get-all",
+ "arguments": {
+ "subnet-id": 1,
+ "operation-target": "alternate"
+ }
+ }
+
+For more information, see :ref:`command-reservation-get-all`.
+
+.. isccmd:: reservation-get-page
+.. _command-reservation-get-page:
+
+The ``reservation-get-page`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`reservation-get-page` can be used to query the host database and
+retrieve all reservations in a specified subnet, by pages. This command
+uses parameters providing the mandatory ``subnet-id``. Use a value of zero
+(0) to fetch global reservations. The second mandatory parameter is the
+page size limit. The optional ``source-index`` and ``from-host-id`` parameters, both
+of which default to 0, are used to chain page queries.
+Since Kea version 1.9.0, the ``subnet-id`` parameter is optional.
+
+The usage of the ``from`` and ``source-index`` parameters requires additional
+explanation. For the first call, those parameters should not be specified
+(or should be specified as zeros). For any follow-up calls, they should be set to
+the values returned in previous calls, in a next map holding ``from`` and
+``source-index`` values. Subsequent calls should be issued until all
+reservations are returned. The end is reached once the returned list is
+empty, the count is 0, no next map is present, and result status 3 (empty) is
+returned.
+
+.. note::
+
+ The ``from`` and ``source-index`` parameters reflect the internal state of
+ the search. There is no need to understand what they represent; it is
+ simply a value that should be copied from one response to the
+ next query. However, for those who are curious, the ``from`` field represents a
+ 64-bit representation of the host identifier used by a host backend. The
+ ``source-index`` is an internal representation of multiple host
+ backends: 0 is used to represent hosts defined in a configuration
+ file, and 1 represents the first database backend. In some uncommon cases
+ there may be more than one database backend configured, so
+ potentially there may be a 2. In any case, Kea iterates over all
+ backends configured.
+
+For instance, retrieving host reservations for the subnet 1 and
+requesting the first page can be done by:
+
+::
+
+ {
+ "command": "reservation-get-page",
+ "arguments": {
+ "subnet-id": 1,
+ "limit": 10
+ }
+ }
+
+Since this is the first call, ``source-index`` and ``from`` should not be
+specified. They are set to their zero default values.
+
+Some hosts are returned with information to get the next page:
+
+::
+
+ {
+ "arguments": {
+ "count": 72,
+ "hosts": [
+ {
+ "boot-file-name": "bootfile.efi",
+ "client-classes": [ ],
+ "hostname": "somehost.example.org",
+ "hw-address": "01:02:03:04:05:06",
+ "ip-address": "192.0.2.100",
+ "next-server": "192.0.0.2",
+ "option-data": [ ],
+ "server-hostname": "server-hostname.example.org"
+ },
+ {
+ "boot-file-name": "bootfile.efi",
+ "client-classes": [ ],
+ "hostname": "otherhost.example.org",
+ "hw-address": "01:02:03:04:05:ff",
+ "ip-address": "192.0.2.200",
+ "next-server": "192.0.0.2",
+ "option-data": [ ],
+ "server-hostname": "server-hostname.example.org"
+ },
+ ...
+ ],
+ "next": {
+ "from": 1234567,
+ "source-index": 1
+ }
+ },
+ "result": 0,
+ "text": "72 IPv4 host(s) found."
+ }
+
+Note that the ``from`` and ``source-index`` fields were specified in the response in
+the next map. Those two must be copied to the next command, so Kea
+continues from the place where the last command finished. To get the
+next page the following command can be sent:
+
+::
+
+ {
+ "command": "reservation-get-page",
+ "arguments": {
+ "subnet-id": 1,
+ "source-index": 1,
+ "from": 1234567,
+ "limit": 10
+ }
+ }
+
+The response will contain a list of hosts with updated ``source-index``
+and ``from`` fields. Continue calling the command until the last
+page is received. Its response will look like this:
+
+.. code-block:: json
+
+ {
+ "arguments": {
+ "count": 0,
+ "hosts": [ ]
+ },
+ "result": 3,
+ "text": "0 IPv4 host(s) found."
+ }
+
+The command doesn't accept the ``operation-target`` argument.
+
+This command is more complex than :isccmd:`reservation-get-all`, but lets
+users retrieve larger host reservations lists in smaller chunks. For
+small deployments with few reservations, it is easier to use
+:isccmd:`reservation-get-all`.
+
+.. isccmd:: reservation-get-by-address
+.. _command-reservation-get-by-address:
+
+The ``reservation-get-by-address`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`reservation-get-by-address` can be used to query the host database and
+retrieve all reservations for given IP address or a delegated prefix (without
+a prefix length) in a specified subnet or in all subnets. This command uses
+parameters providing the mandatory ``ip-address`` and the optional ``subnet-id``
+and ``operation-target``.
+
+For instance, retrieving host reservations for the IPv4 address "192.0.200.181"
+in the subnet 1:
+
+::
+
+ {
+ "command": "reservation-get-by-address",
+ "arguments": {
+ "ip-address": "192.0.200.181",
+ "subnet-id": 1
+ },
+ "service": [
+ "dhcp4"
+ ]
+ }
+
+can return two IPv4 hosts:
+
+::
+
+ {
+ "arguments": {
+ "hosts": [
+ {
+ "boot-file-name": "",
+ "client-classes": [],
+ "hostname": "",
+ "hw-address": "99:99:99:99:99:01",
+ "ip-address": "192.0.200.181",
+ "next-server": "0.0.0.0",
+ "option-data": [],
+ "server-hostname": "",
+ "subnet-id": 1
+ },
+ {
+ "boot-file-name": "",
+ "circuit-id": "1234",
+ "client-classes": [],
+ "hostname": "",
+ "ip-address": "192.0.200.181",
+ "next-server": "0.0.0.0",
+ "option-data": [],
+ "server-hostname": "",
+ "subnet-id": 1
+ }
+ ]
+ },
+ "result": 0,
+ "text": "2 IPv4 host(s) found."
+ }
+
+To search for all reservations in all subnets simply skip the ``subnet-id`` parameter:
+
+::
+
+ {
+ "command": "reservation-get-by-address",
+ "arguments": {
+ "ip-address": "192.0.200.181"
+ },
+ "service": [
+ "dhcp4"
+ ]
+ }
+
+An example response can be:
+
+::
+
+ {
+ "arguments": {
+ "hosts": [
+ {
+ "boot-file-name": "",
+ "client-classes": [],
+ "hostname": "",
+ "hw-address": "99:99:99:99:99:01",
+ "ip-address": "192.0.200.181",
+ "next-server": "0.0.0.0",
+ "option-data": [],
+ "server-hostname": "",
+ "subnet-id": 1
+ },
+ {
+ "boot-file-name": "",
+ "circuit-id": "1234",
+ "client-classes": [],
+ "hostname": "",
+ "ip-address": "192.0.200.181",
+ "next-server": "0.0.0.0",
+ "option-data": [],
+ "server-hostname": "",
+ "subnet-id": 1
+ },
+ {
+ "boot-file-name": "",
+ "client-classes": [],
+ "hostname": "",
+ "hw-address": "99:99:99:99:99:02",
+ "ip-address": "192.0.200.181",
+ "next-server": "0.0.0.0",
+ "option-data": [],
+ "server-hostname": "",
+ "subnet-id": 0
+ },
+ {
+ "boot-file-name": "",
+ "client-classes": [],
+ "hostname": "",
+ "hw-address": "99:99:99:99:99:03",
+ "ip-address": "192.0.200.181",
+ "next-server": "0.0.0.0",
+ "option-data": [],
+ "server-hostname": "",
+ "subnet-id": 2
+ }
+ ]
+ },
+ "result": 0,
+ "text": "4 IPv4 host(s) found."
+ }
+
+When using the command for retrieving DHCP6 host reservations, one can provide
+either an IPv6 address or a delegated prefix as the ``ip-address`` parameter.
+Please notice that this command does not take prefix length as a parameter in the
+current implementation. That's why searching by an IP address ``2001:db8:2:cafe::``
+can return host reservations having delegated prefixes matching this search with
+different lengths. For example: ``2001:db8:2:cafe::/63``, ``2001:db8:2:cafe::/64`` etc.
+Please consider the example below:
+
+::
+
+ {
+ "command": "reservation-get-by-address",
+ "arguments": {
+ "ip-address": "2001:db8:2:cafa::"
+ },
+ "service": [
+ "dhcp6"
+ ]
+ }
+
+Response:
+
+::
+
+ {
+ "arguments": {
+ "hosts": [
+ {
+ "client-classes": [],
+ "duid": "01:02:03:04:05:06:07:88:98:fa",
+ "hostname": "foo.example.com",
+ "ip-addresses": [
+ "2001:db8:1:cafe::2"
+ ],
+ "option-data": [],
+ "prefixes": [
+ "2001:db8:2:abcd::/64",
+ "2001:db8:2:cafa::/63"
+ ],
+ "subnet-id": 8
+ },
+ {
+ "client-classes": [],
+ "duid": "01:02:03:04:05:06:07:88:98:fb",
+ "hostname": "foo.example.com",
+ "ip-addresses": [
+ "2001:db8:1:cafe::2"
+ ],
+ "option-data": [],
+ "prefixes": [
+ "2001:db8:2:abcd::/64",
+ "2001:db8:2:cafa::/64"
+ ],
+ "subnet-id": 8
+ }
+ ]
+ },
+ "result": 0,
+ "text": "2 IPv6 host(s) found."
+ }
+
+The command accepts the ``operation-target`` argument. By default, it gets the
+reservation from both JSON configuration and the hosts database.
+
+.. code-block:: json
+
+ {
+ "command": "reservation-get-by-address",
+ "arguments": {
+ "ip-address": "192.0.200.181",
+ "subnet-id": 1,
+ "operation-target": "alternate"
+ },
+ "service": [
+ "dhcp4"
+ ]
+ }
+
+.. note::
+
+ This command is useful in specific cases. By default, having more than
+ one host reservation for the same IP address in a given subnet is not allowed,
+ as explained in the
+ :ref:`Multiple Reservations for the Same IPv4 <multiple-reservations-same-ip4>`
+ or in the
+ :ref:`Multiple Reservations for the Same IPv6 <multiple-reservations-same-ip6>`.
+ That's why this command comes in handy
+ when the ``ip-reservations-unique`` boolean parameter is set to ``false``.
+ Other use case of this command is having overlapping subnets and having
+ the same IP address reservation (but with different identifier) in different
+ subnets.
+
+.. isccmd:: reservation-get-by-hostname
+.. _command-reservation-get-by-hostname:
+
+The ``reservation-get-by-hostname`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`reservation-get-by-hostname` can be used to query the host database and
+retrieve all reservations with a specified hostname or in
+a specified subnet. This command uses parameters providing the mandatory
+``hostname`` and the optional ``subnet-id``. Global host reservations
+can be retrieved by using a ``subnet-id`` value of zero (0).
+Hostname matching is case-insensitive.
+
+For instance, retrieving host reservations for "foobar" in the subnet 1:
+
+::
+
+ {
+ "command": "reservation-get-by-hostname",
+ "arguments": {
+ "hostname": "foobar.example.org",
+ "subnet-id": 1
+ }
+ }
+
+returns some IPv4 hosts:
+
+::
+
+ {
+ "arguments": {
+ "hosts": [
+ {
+ "boot-file-name": "bootfile.efi",
+ "client-classes": [ ],
+ "hostname": "foobar.example.org",
+ "hw-address": "01:02:03:04:05:06",
+ "ip-address": "192.0.2.100",
+ "next-server": "192.0.0.2",
+ "option-data": [ ],
+ "server-hostname": "server-hostname.example.org"
+ },
+ {
+ "boot-file-name": "bootfile.efi",
+ "client-classes": [ ],
+ "hostname": "foobar.example.org",
+ "hw-address": "01:02:03:04:05:ff",
+ "ip-address": "192.0.2.200",
+ "next-server": "192.0.0.2",
+ "option-data": [ ],
+ "server-hostname": "server-hostname.example.org"
+ },
+ ...
+ ]
+ },
+ "result": 0,
+ "text": "5 IPv4 host(s) found."
+ }
+
+The response returned by :isccmd:`reservation-get-by-hostname` can be long,
+particularly when responses are not limited to a subnet.
+
+The command accepts the ``operation-target`` argument. By default, it gets the
+reservation from both JSON configuration and the hosts database.
+
+.. code-block:: json
+
+ {
+ "command": "reservation-get-by-hostname",
+ "arguments": {
+ "hostname": "foobar.example.org",
+ "subnet-id": 1,
+ "operation-target": "alternate"
+ }
+ }
+
+For more information, see :ref:`command-reservation-get-by-hostname`.
+
+.. note::
+
+ When using MySQL as the host backend, this command relies on the fact
+ that the hostname column in the hosts table uses a case-insensitive
+ collation, as explained in the :ref:`mysql-database` section of
+ :ref:`admin`.
+
+.. isccmd:: reservation-get-by-id
+.. _command-reservation-get-by-id:
+
+The ``reservation-get-by-id`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`reservation-get-by-id` can be used to query the host database and
+retrieve all reservations with a specified identifier (``identifier-type``
+and ``identifier`` parameters), independently of subnets. The syntax for
+parameters is the same as for :isccmd:`reservation-get`.
+The ``subnet-id`` parameter cannot be used, to avoid confusion.
+This command is available since Kea version 1.9.0.
+
+For instance, retrieving host reservations for the 01:02:03:04:05:06 MAC
+address:
+
+::
+
+ {
+ "command": "reservation-get-by-id",
+ "arguments": {
+ "identifier-type": "hw-address",
+ "identifier": "01:02:03:04:05:06"
+ }
+ }
+
+returns some IPv4 hosts:
+
+::
+
+ {
+ "arguments": {
+ "hosts": [
+ {
+ "boot-file-name": "bootfile.efi",
+ "client-classes": [ ],
+ "hostname": "foo.example.org",
+ "hw-address": "01:02:03:04:05:06",
+ "ip-address": "192.0.2.100",
+ "next-server": "192.0.0.2",
+ "option-data": [ ],
+ "server-hostname": "server-hostname.example.org",
+ "subnet-id": 123
+ },
+ {
+ "boot-file-name": "bootfile.efi",
+ "client-classes": [ ],
+ "hostname": "bar.example.org",
+ "hw-address": "01:02:03:04:05:06",
+ "ip-address": "192.0.2.200",
+ "next-server": "192.0.0.2",
+ "option-data": [ ],
+ "server-hostname": "server-hostname.example.org",
+ "subnet-id": 345
+ },
+ ...
+ ]
+ },
+ "result": 0,
+ "text": "5 IPv4 host(s) found."
+ }
+
+The response returned by :isccmd:`reservation-get-by-id` can be long,
+particularly when responses are not limited to a subnet.
+
+The command accepts the ``operation-target`` argument. By default, it gets the
+reservation from both JSON configuration and the hosts database.
+
+.. code-block:: json
+
+ {
+ "command": "reservation-get-by-id",
+ "arguments": {
+ "identifier-type": "hw-address",
+ "identifier": "01:02:03:04:05:06",
+ "operation-target": "alternate"
+ }
+ }
+
+For more information, see :ref:`command-reservation-get-by-id`.
+
+.. isccmd:: reservation-del
+.. _command-reservation-del:
+
+The ``reservation-del`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`reservation-del` can be used to delete a reservation from the host
+database and/or JSON configuration. This command supports two types of parameters:
+(``subnet-id``, ``address``) or (``subnet-id``, ``identifier-type``, ``identifier``). The
+first type of query is used when the address (either IPv4 or IPv6) is
+known, but the details of the reservation are not. One common use for
+this type of query is to remove a reservation (e.g. a specific
+address should no longer be reserved). The second query uses identifiers.
+For maximum flexibility, Kea stores the host identifying information as
+a pair of values: the type and the actual identifier. Currently supported
+identifiers are ``"hw-address"``, ``"duid"``, ``"circuit-id"``, ``"client-id"``, and
+``"flex-id"``. The ``subnet-id`` is mandatory. Use a value
+of zero (0) to delete a global reservation, or the ID of the subnet from
+which the reservation should be deleted.
+
+An example command for deleting a host reservation by (``subnet-id``,
+``address``) pair looks as follows:
+
+::
+
+ {
+ "command": "reservation-del",
+ "arguments": {
+ "subnet-id": 1,
+ "ip-address": "192.0.2.202"
+ }
+ }
+
+An example deletion by (``subnet-id``, ``identifier-type``, ``identifier``) looks as
+follows:
+
+::
+
+ {
+ "command": "reservation-del",
+ "arguments": {
+ "subnet-id": 4,
+ "identifier-type": "hw-address",
+ "identifier": "01:02:03:04:05:06"
+ }
+ }
+
+Command :isccmd:`reservation-del` returns a result of 0 when the host deletion was
+successful, or 1 if it failed. Descriptive text is provided in the event of
+an error. Here are some examples of possible results:
+
+::
+
+ {
+ "result": 1,
+ "text": "Host not deleted (not found)."
+ }
+
+or
+
+::
+
+ {
+ "result": 0,
+ "text": "Host deleted."
+ }
+
+or
+
+::
+
+ {
+ "result": 1,
+ "text": "Unable to delete a host because there is no hosts-database configured."
+ }
+
+The command accepts the ``operation-target`` argument. By default, it removes
+the reservation from the hosts database only.
+
+.. code-block:: json
+
+ {
+ "command": "reservation-del",
+ "arguments": {
+ "subnet-id": 4,
+ "identifier-type": "hw-address",
+ "identifier": "01:02:03:04:05:06",
+ "operation-target": "primary"
+ }
+ }
+
+.. isccmd:: reservation-update
+.. _command-reservation-update:
+
+The ``reservation-update`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`reservation-update` allows for the update of an existing host. It takes the
+same set of arguments as :isccmd:`reservation-add`, and just as well,
+requires a host identifier and a subnet ID to identify the host that is being
+updated. The command can be as simple as having only the two mandatory entries:
+
+.. code-block:: json
+
+ {
+ "command": "reservation-update",
+ "arguments": {
+ "reservation": {
+ "subnet-id": 1,
+ "hw-address": "1a:1b:1c:1d:1e:1f"
+ }
+ }
+ }
+
+In that case, however, it does not assign any resources to the host. An IPv4
+address can be assigned like so:
+
+.. code-block:: json
+
+ {
+ "command": "reservation-update",
+ "arguments": {
+ "reservation": {
+ "subnet-id": 1,
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "ip-address": "192.0.2.202"
+ }
+ }
+ }
+
+It can also take many more parameters, for example:
+
+.. code-block:: json
+
+ {
+ "command": "reservation-update",
+ "arguments": {
+ "reservation": {
+ "subnet-id": 1,
+ "client-id": "01:0a:0b:0c:0d:0e:0f",
+ "ip-address": "192.0.2.205",
+ "next-server": "192.0.2.1",
+ "server-hostname": "hal9000",
+ "boot-file-name": "/dev/null",
+ "option-data": [
+ {
+ "name": "domain-name-servers",
+ "data": "10.1.1.202,10.1.1.203"
+ }
+ ],
+ "client-classes": [
+ "office",
+ "special_snowflake"
+ ]
+ }
+ }
+ }
+
+Here is an example of a complex IPv6 reservation update:
+
+.. code-block:: json
+
+ {
+ "command": "reservation-update",
+ "arguments": {
+ "reservation": {
+ "subnet-id": 1,
+ "duid": "01:02:03:04:05:06:07:08:09:0A",
+ "ip-addresses": [
+ "2001:db8:1:cafe::1"
+ ],
+ "prefixes": [
+ "2001:db8:2:abcd::/64"
+ ],
+ "hostname": "foo.example.com",
+ "option-data": [
+ {
+ "name": "vendor-opts",
+ "data": "4491"
+ },
+ {
+ "name": "tftp-servers",
+ "space": "vendor-4491",
+ "data": "3000:1::234"
+ }
+ ]
+ }
+ }
+ }
+
+The command returns a status that indicates either success (result ``0``) or
+failure (result ``1``) and a text parameter that confirms success or explains
+the cause of the failure. Here's an example of a successful update:
+
+.. code-block:: json
+
+ {
+ "result": 0,
+ "text": "Host updated."
+ }
+
+And here's an example of a failure:
+
+.. code-block:: json
+
+ {
+ "result": 1,
+ "text": "Mandatory 'subnet-id' parameter missing."
+ }
+
+The command accepts the ``operation-target`` argument. By default, it adds the
+reservation to the hosts database only. As :isccmd:`reservation-update` is expected
+to store the host, the ``hosts-databases`` parameter must be specified in the
+configuration, and databases must not run in read-only mode if the operation
+target is not the JSON configuration.
+
+As with other update and set commands, this command overwrites all the contents
+of the entry. If the host previously had a resource assigned to it, and the
+:isccmd:`reservation-update` command is missing the resource, it is deleted from the
+database. If an incremental update of the host is desired, then this can be
+achieved by doing a :isccmd:`reservation-get-by-id` to get the current state of the
+host, picking the host out of the response, modifying it to the required
+outcome, and then issuing the :isccmd:`reservation-update` command with the resulting
+host attached.
+
+.. _hooks-host-cmds-general-mentions:
+
+General Mentions
+~~~~~~~~~~~~~~~~
+
+.. note::
+
+ The host cache and RADIUS hook libraries are two host backends that do not
+ respond to commands that return a collection of host reservations, such as
+ :isccmd:`reservation-get-all`. Commands returning one host entry or dedicated host
+ cache commands should be used instead.
diff --git a/doc/sphinx/arm/hooks-lease-cmds.rst b/doc/sphinx/arm/hooks-lease-cmds.rst
new file mode 100644
index 0000000..61ee755
--- /dev/null
+++ b/doc/sphinx/arm/hooks-lease-cmds.rst
@@ -0,0 +1,1073 @@
+.. ischooklib:: libdhcp_lease_cmds.so
+.. _hooks-lease-cmds:
+
+``libdhcp_lease_cmds.so``: Lease Commands for Easier Lease Management
+=====================================================================
+
+Kea allows users to store lease information in several
+backends (memfile, MySQL, and PostgreSQL), and the Lease Commands library provides an
+interface that can manipulate leases in a unified, safe way.
+In particular, it allows things that were previously impossible: lease
+manipulation in memfile while Kea is running, sanity check changes,
+lease existence checks, and removal of all leases belonging to a
+specific subnet. The hook library can also catch more obscure errors, like an attempt
+to add a lease with a ``subnet-id`` that does not exist in the
+configuration, or configuring a lease to use an address that is outside
+of the subnet to which it is supposed to belong. The library also
+provides a non-programmatic way to manage user contexts associated with
+leases.
+
+.. note::
+
+ :ischooklib:`libdhcp_lease_cmds.so` is part of the open source code and is
+ available to every Kea user.
+
+.. note::
+
+ This library can only be loaded by the :iscman:`kea-dhcp4` or the
+ :iscman:`kea-dhcp6` process.
+
+There are many situations where an administrative command may be useful;
+for example, during migration between servers or different vendors, when
+a certain network is being retired, or when a device has been
+disconnected and the system administrator knows that it will not be coming
+back. The ``get`` queries may be useful for automating certain management
+and monitoring tasks, and they can also act as preparatory steps for lease
+updates and removals.
+
+This library provides the following commands:
+
+- :isccmd:`lease4-add` - adds a new IPv4 lease.
+
+- :isccmd:`lease6-add` - adds a new IPv6 lease.
+
+- :isccmd:`lease6-bulk-apply` - creates, updates, and/or deletes multiple
+ IPv6 leases in a single transaction.
+
+- :isccmd:`lease4-get` - checks whether an IPv4 lease with the specified
+ parameters exists and returns it if it does.
+
+- :isccmd:`lease6-get` - checks whether an IPv6 lease with the specified
+ parameters exists and returns it if it does.
+
+- :isccmd:`lease4-get-all` - returns all IPv4 leases or all IPv4 leases for
+ the specified subnets.
+
+- :isccmd:`lease6-get-all` - returns all IPv6 leases or all IPv6 leases for
+ the specified subnets.
+
+- :isccmd:`lease4-get-page` - returns a set ("page") of leases from the list
+ of all IPv4 leases in the database. By iterating through the pages it
+ is possible to retrieve all the leases.
+
+- :isccmd:`lease6-get-page` - returns a set ("page") of leases from the list
+ of all IPv6 leases in the database. By iterating through the pages it
+ is possible to retrieve all the leases.
+
+- :isccmd:`lease4-get-by-hw-address` - returns all IPv4 leases with the specified
+ hardware address.
+
+- :isccmd:`lease4-get-by-client-id` - returns all IPv4 leases with the specified
+ ``client-id``.
+
+- :isccmd:`lease6-get-by-duid` - returns all IPv6 leases with the specified DUID.
+
+- :isccmd:`lease4-get-by-hostname` - returns all IPv4 leases with the specified
+ hostname.
+
+- :isccmd:`lease6-get-by-hostname` - returns all IPv6 leases with the specified
+ hostname.
+
+- :isccmd:`lease4-del` - deletes an IPv4 lease with the specified parameters.
+
+- :isccmd:`lease6-del` - deletes an IPv6 lease with the specified parameters.
+
+- :isccmd:`lease4-update` - updates (replaces) an existing IPv4 lease.
+
+- :isccmd:`lease6-update` - updates (replaces) an existing IPv6 lease.
+
+- :isccmd:`lease4-wipe` - removes all leases from a specific IPv4 subnet or
+ from all subnets.
+
+- :isccmd:`lease6-wipe` - removes all leases from a specific IPv6 subnet or
+ from all subnets.
+
+- :isccmd:`lease4-resend-ddns` - resends a request to update DNS entries for
+ an existing lease.
+
+- :isccmd:`lease6-resend-ddns` - resends a request to update DNS entries for
+ an existing lease.
+
+- :isccmd:`lease4-write` - writes the IPv4 memfile lease database into a file.
+
+- :isccmd:`lease6-write` - writes the IPv6 memfile lease database into a file.
+
+All commands use JSON syntax and can be issued either using the control
+channel (see :ref:`ctrl-channel`) or Control Agent (see
+:ref:`kea-ctrl-agent`).
+
+The library can be loaded in the same way as other hook libraries, and
+it does not take any parameters. It supports both the DHCPv4 and DHCPv6
+servers.
+
+::
+
+ "Dhcp6": {
+ "hooks-libraries": [
+ {
+ "library": "/path/libdhcp_lease_cmds.so"
+ },
+ ...
+ ]
+ }
+
+.. isccmd:: lease4-add
+.. _command-lease4-add:
+
+.. isccmd:: lease6-add
+.. _command-lease6-add:
+
+The ``lease4-add``, ``lease6-add`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :isccmd:`lease4-add` and
+:isccmd:`lease6-add` commands allow a new lease
+to be created. Typically Kea creates a lease when it first sees a new
+device; however, sometimes it may be convenient to create the lease
+manually. The :isccmd:`lease4-add` command requires at least two parameters:
+an IPv4 address and an identifier, i.e. hardware (MAC) address. A third
+parameter, ``subnet-id``, is optional. If the ``subnet-id`` is not specified or
+the specified value is 0, Kea tries to determine the value by running
+a subnet-selection procedure. If specified, however, its value must
+match the existing subnet. The simplest successful call might look as
+follows:
+
+::
+
+ {
+ "command": "lease4-add",
+ "arguments": {
+ "ip-address": "192.0.2.202",
+ "hw-address": "1a:1b:1c:1d:1e:1f"
+ }
+ }
+
+The :isccmd:`lease6-add` command requires three parameters: an IPv6 address,
+an IAID value (identity association identifier, a value sent by
+clients), and a DUID. As with :isccmd:`lease4-add`, the ``subnet-id`` parameter is
+optional. If the ``subnet-id`` is not specified or the provided value is 0,
+Kea tries to determine the value by running a subnet-selection
+procedure. If specified, however, its value must match the existing
+subnet. For example:
+
+::
+
+ {
+ "command": "lease6-add",
+ "arguments": {
+ "subnet-id": 66,
+ "ip-address": "2001:db8::3",
+ "duid": "1a:1b:1c:1d:1e:1f:20:21:22:23:24",
+ "iaid": 1234
+ }
+ }
+
+The :isccmd:`lease6-add` command can also be used to add leases for IPv6 prefixes.
+In this case there are three additional parameters that must be specified:
+``subnet-id``, ``type`` (set to "IA_PD"), and prefix length. The actual
+prefix is set using the ``ip-address`` field. Note that Kea cannot guess
+``subnet-id`` values for prefixes; they must be specified explicitly. For
+example, to configure a lease for prefix 2001:db8:abcd::/48, the
+following command can be used:
+
+::
+
+ {
+ "command": "lease6-add",
+ "arguments": {
+ "subnet-id": 66,
+ "type": "IA_PD",
+ "ip-address": "2001:db8:abcd::",
+ "prefix-len": 48,
+ "duid": "1a:1b:1c:1d:1e:1f:20:21:22:23:24",
+ "iaid": 1234
+ }
+ }
+
+The commands can take several additional optional parameters:
+
+- ``valid-lft`` - specifies the lifetime of the lease, expressed in
+ seconds. If not specified, the value configured in the subnet related
+ to the specified ``subnet-id`` is used.
+
+- ``expire`` - creates a timestamp of the lease expiration time,
+ expressed in UNIX format (seconds since 1 Jan 1970). If not
+ specified, the default value is the current time plus the lease lifetime (the value
+ of ``valid-lft``).
+
+- ``fqdn-fwd`` - specifies whether the lease should be marked as if a
+ forward DNS update were conducted. This only affects the
+ data stored in the lease database, and no DNS update will be
+ performed. If configured, a DNS update to remove the A or AAAA
+ records will be conducted when the lease is removed due to expiration
+ or being released by a client. If not specified, the default value is
+ ``false``. The hostname parameter must be specified if ``fqdn-fwd`` is set to
+ ``true``.
+
+- ``fqdn-rev`` - specifies whether the lease should be marked as if
+ reverse DNS update were conducted. This only affects the
+ data stored in the lease database, and no DNS update will be
+ performed.. If configured, a DNS update to remove the PTR record will
+ be conducted when the lease is removed due to expiration or being
+ released by a client. If not specified, the default value is ``false``.
+ The hostname parameter must be specified if ``fqdn-fwd`` is set to ``true``.
+
+- ``hostname`` - specifies the hostname to be associated with this
+ lease. Its value must be non-empty if either ``fqdn-fwd`` or ``fqdn-rev`` are
+ set to ``true``. If not specified, the default value is an empty string.
+
+- ``hw-address`` - optionally specifies a hardware (MAC) address for an
+ IPv6 lease. It is a mandatory parameter for an IPv4 lease.
+
+- ``client-id`` - optionally specifies a client identifier for an IPv4
+ lease.
+
+- ``preferred-lft`` - optionally specifies a preferred lifetime for
+ IPv6 leases. If not specified, the value configured for the subnet
+ corresponding to the specified ``subnet-id`` is used. This parameter is
+ not used when adding an IPv4 lease.
+
+- ``state`` - specifies the state of an added lease, which can be 0 for ``default``,
+ 1 for ``declined``, and 2 for the ``expired-reclaimed`` state. Any other
+ value causes an error. Using 1 for a ``"IA_PD"`` lease type is
+ illegal and will be rejected.
+
+- ``user-context`` - specifies the user context to be associated with
+ this lease. It must be a JSON map.
+
+Here is an example of a fairly complex lease addition:
+
+::
+
+ {
+ "command": "lease6-add",
+ "arguments": {
+ "subnet-id": 66,
+ "ip-address": "2001:db8::3",
+ "duid": "01:02:03:04:05:06:07:08",
+ "iaid": 1234,
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "preferred-lft": 500,
+ "valid-lft": 1000,
+ "expire": 12345678,
+ "fqdn-fwd": true,
+ "fqdn-rev": true,
+ "state": 0,
+ "hostname": "urania.example.org",
+ "user-context": { "version": 1 }
+ }
+ }
+
+The command returns a status that indicates either success (result 0)
+or failure (result 1). A failed command always includes a text
+parameter that explains the cause of failure. For example:
+
+::
+
+ { "result": 0, "text": "Lease added." }
+
+Example failure:
+
+::
+
+ { "result": 1, "text": "missing parameter 'ip-address' (<string>:3:19)" }
+
+
+.. isccmd:: lease6-bulk-apply
+.. _command-lease6-bulk-apply:
+
+The ``lease6-bulk-apply`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :isccmd:`lease6-bulk-apply` was implemented to address
+the performance penalty in High-Availability mode when a single DHCPv6
+transaction resulted in multiple lease updates sent to the partner, if
+multiple address and/or prefix leases were allocated. Consider the case
+when a DHCPv6 client requests the assignment of two IPv6 addresses and two IPv6
+prefixes: it may result in the allocation of four leases. In addition,
+DHCPv6 may assign a different address than the one requested by the client during
+the renew or rebind stage, and delete the leases previously used by this client.
+There are six lease changes sent between the HA partners in this case.
+Sending these updates as individual commands, e.g. via :isccmd:`lease6-update`,
+is highly inefficient and produces unnecessary delays in communication,
+both between the HA partners and in sending the response to the DHCPv6 client.
+
+The :isccmd:`lease6-bulk-apply` command deals with this
+problem by aggregating all lease changes - both deleted leases and
+new or updated leases - in a single command.
+The receiving server iterates over the deleted leases and deletes them
+from its lease database. Next, it iterates over the new/updated leases
+and adds them to the database or updates them if they already exist.
+
+Even though High Availability is the major application for
+this command, it can be freely used in all cases when it is desirable to
+send multiple lease changes in a single command.
+
+In the following example, we delete two leases and add
+or update two other leases in the database:
+
+
+::
+
+ {
+ "command": "lease6-bulk-apply",
+ "arguments": {
+ "deleted-leases": [
+ {
+ "ip-address": "2001:db8:abcd::",
+ "type": "IA_PD",
+ ...
+ },
+ {
+ "ip-address": "2001:db8:abcd::234",
+ "type": "IA_NA",
+ ...
+ }
+ ],
+ "leases": [
+ {
+ "subnet-id": 66,
+ "ip-address": "2001:db8:cafe::",
+ "type": "IA_PD",
+ ...
+ },
+ {
+ "subnet-id": 66,
+ "ip-address": "2001:db8:abcd::333",
+ "type": "IA_NA",
+ ...
+ }
+ ]
+ }
+ }
+
+If any of the leases are malformed, no lease changes are applied
+to the lease database. If the leases are well-formed but there is a
+failure to apply any of the lease changes to the database, the command
+continues to be processed for other leases. All the leases for which
+the command was unable to apply the changes in the database are
+listed in the response. For example:
+
+::
+
+ {
+ "result": 0,
+ "text": "Bulk apply of 2 IPv6 leases completed",
+ "arguments": {
+ "failed-deleted-leases": [
+ {
+ "ip-address": "2001:db8:abcd::",
+ "type": "IA_PD",
+ "result": 3,
+ "error-message": "no lease found"
+ }
+ ],
+ "failed-leases": [
+ {
+ "ip-address": "2001:db8:cafe::",
+ "type": "IA_PD",
+ "result": 1,
+ "error-message": "unable to communicate with the lease database"
+ }
+ ]
+ }
+ }
+
+The response above indicates that the hook library was unable to
+delete the lease for prefix "2001:db8:abcd::" and add or update the lease
+for prefix "2001:db8:cafe::". However, there are two other lease changes
+which have been applied as indicated by the text message. The
+``result`` is the status constant that indicates the type
+of the error experienced for the particular lease. The meanings of the
+returned codes are the same as the results returned for the commands.
+In particular, the result of 1 indicates an error while processing the
+lease, e.g. a communication error with the database. The result of 3
+indicates that an attempt to delete the lease was unsuccessful because
+such a lease doesn't exist (an empty result).
+
+.. isccmd:: lease4-get
+.. _command-lease4-get:
+
+.. isccmd:: lease6-get
+.. _command-lease6-get:
+
+The ``lease4-get``, ``lease6-get`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`lease4-get` and :isccmd:`lease6-get` can be used to query the lease database
+and retrieve existing leases. There are two types of parameters the
+:isccmd:`lease4-get` command supports: (``address``) or (``subnet-id``,
+``identifier-type``, ``identifier``). There are also two types for
+:isccmd:`lease6-get`: (``address``, ``type``) or (``subnet-id``, ``identifier-type``,
+``identifier``, ``IAID``, ``type``). The first type of query is used when the
+address (either IPv4 or IPv6) is known, but the details of the lease are
+not; one common use case of this type of query is to find out whether a
+given address is being used. The second query uses identifiers;
+currently supported identifiers for leases are: ``"hw-address"`` (IPv4
+only), ``"client-id"`` (IPv4 only), and ``"duid"`` (IPv6 only).
+
+An example :isccmd:`lease4-get` command for getting a lease using an IPv4
+address is:
+
+::
+
+ {
+ "command": "lease4-get",
+ "arguments": {
+ "ip-address": "192.0.2.1"
+ }
+ }
+
+An example of the :isccmd:`lease6-get` query is:
+
+::
+
+ {
+ "command": "lease6-get",
+ "arguments": {
+ "ip-address": "2001:db8:1234:ab::",
+ "type": "IA_PD"
+ }
+ }
+
+An example query by ``"hw-address"`` for an IPv4 lease looks as follows:
+
+::
+
+ {
+ "command": "lease4-get",
+ "arguments": {
+ "identifier-type": "hw-address",
+ "identifier": "08:08:08:08:08:08",
+ "subnet-id": 44
+ }
+ }
+
+An example query by ``"client-id"`` for an IPv4 lease looks as follows:
+
+::
+
+ {
+ "command": "lease4-get",
+ "arguments": {
+ "identifier-type": "client-id",
+ "identifier": "01:01:02:03:04:05:06",
+ "subnet-id": 44
+ }
+ }
+
+An example query by (``subnet-id``, ``identifier-type``, ``identifier``, ``iaid``, ``type``)
+for an IPv6 lease is:
+
+::
+
+ {
+ "command": "lease4-get",
+ "arguments": {
+ "identifier-type": "duid",
+ "identifier": "08:08:08:08:08:08",
+ "iaid": 1234567,
+ "type": "IA_NA",
+ "subnet-id": 44
+ }
+ }
+
+The ``type`` is an optional parameter. Supported values are: ``IA_NA``
+(non-temporary address) and ``IA_PD`` (IPv6 prefix). If not specified, ``IA_NA``
+is assumed.
+
+:isccmd:`lease4-get` and :isccmd:`lease6-get` return an indication of the result of the operation
+and lease details, if found. The result has one of the following values: 0
+(success), 1 (error), or 3 (empty). An empty result means that a query
+has been completed properly, but the object (a lease in this case) has
+not been found.
+The lease parameters, if found, are returned as arguments.
+``client-id`` is not returned if empty.
+
+An example result returned when the host was found:
+
+::
+
+ {
+ "arguments": {
+ "client-id": "42:42:42:42:42:42:42:42",
+ "cltt": 12345678,
+ "fqdn-fwd": false,
+ "fqdn-rev": true,
+ "hostname": "myhost.example.com.",
+ "hw-address": "08:08:08:08:08:08",
+ "ip-address": "192.0.2.1",
+ "state": 0,
+ "subnet-id": 44,
+ "valid-lft": 3600
+ },
+ "result": 0,
+ "text": "IPv4 lease found."
+ }
+
+.. note::
+
+ The client last transaction time (``cltt`` field) is bound to the
+ valid lifetime (``valid-lft``) and to the expire date (not reported
+ here but stored in databases) by the equation
+ :math:`cltt + valid\_lft = expire`
+
+ at the exception of the infinite valid lifetime coded by the
+ 0xfffffff (4294967295) special value which makes the expire value
+ to overflow on MySQL and old PostgreSQL backends where timestamps
+ are 32 bit long. So in these lease databases the expire date is the
+ same as the cltt i.e.
+ :math:`cltt = expire` when :math:`valid\_lft = 4294967295` and the
+ lease backend is MySQL or PostgreSQL.
+
+.. isccmd:: lease4-get-all
+.. _command-lease4-get-all:
+
+.. isccmd:: lease6-get-all
+.. _command-lease6-get-all:
+
+The ``lease4-get-all``, ``lease6-get-all`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`lease4-get-all` and :isccmd:`lease6-get-all` are used to retrieve all IPv4
+or IPv6 leases, or all leases for the specified set of subnets. All
+leases are returned when there are no arguments specified with the
+command, as in the following example:
+
+::
+
+ {
+ "command": "lease4-get-all"
+ }
+
+If arguments are provided, it is expected that they contain the
+``"subnets"`` parameter, which is a list of subnet identifiers for which
+leases should be returned. For example, to retrieve all IPv6
+leases belonging to the subnets with identifiers 1, 2, 3, and 4:
+
+::
+
+ {
+ "command": "lease6-get-all",
+ "arguments": {
+ "subnets": [ 1, 2, 3, 4 ]
+ }
+ }
+
+The returned response contains a detailed list of leases in the
+following format:
+
+::
+
+ {
+ "arguments": {
+ "leases": [
+ {
+ "cltt": 12345678,
+ "duid": "42:42:42:42:42:42:42:42",
+ "fqdn-fwd": false,
+ "fqdn-rev": true,
+ "hostname": "myhost.example.com.",
+ "hw-address": "08:08:08:08:08:08",
+ "iaid": 1,
+ "ip-address": "2001:db8:2::1",
+ "preferred-lft": 500,
+ "state": 0,
+ "subnet-id": 44,
+ "type": "IA_NA",
+ "valid-lft": 3600
+ },
+ {
+ "cltt": 12345678,
+ "duid": "21:21:21:21:21:21:21:21",
+ "fqdn-fwd": false,
+ "fqdn-rev": true,
+ "hostname": "",
+ "iaid": 1,
+ "ip-address": "2001:db8:0:0:2::",
+ "preferred-lft": 500,
+ "prefix-len": 80,
+ "state": 0,
+ "subnet-id": 44,
+ "type": "IA_PD",
+ "valid-lft": 3600
+ }
+ ]
+ },
+ "result": 0,
+ "text": "2 IPv6 lease(s) found."
+ }
+
+.. warning::
+
+ The :isccmd:`lease4-get-all` and
+ :isccmd:`lease6-get-all` commands may result in
+ very large responses. This may have a negative impact on the DHCP
+ server's responsiveness while the response is generated and
+ transmitted over the control channel, as the server imposes no
+ restriction on the number of leases returned as a result of this
+ command.
+
+.. isccmd:: lease4-get-page
+.. _command-lease4-get-page:
+
+.. isccmd:: lease6-get-page
+.. _command-lease6-get-page:
+
+The ``lease4-get-page``, ``lease6-get-page`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :isccmd:`lease4-get-all` and
+:isccmd:`lease6-get-all` commands may result in
+very large responses; generating such a response may consume CPU
+bandwidth as well as memory. It may even cause the server to become
+unresponsive. In the case of large lease databases it is usually better to
+retrieve leases in chunks, using the paging mechanism.
+:isccmd:`lease4-get-page` and :isccmd:`lease6-get-page` implement a paging mechanism
+for DHCPv4 and DHCPv6 servers, respectively. The following command
+retrieves the first 1024 IPv4 leases:
+
+::
+
+ {
+ "command": "lease4-get-page",
+ "arguments": {
+ "from": "start",
+ "limit": 1024
+ }
+ }
+
+The keyword ``start`` denotes that the first page of leases should be
+retrieved. Alternatively, an IPv4 zero address can be specified to
+retrieve the first page:
+
+::
+
+ {
+ "command": "lease4-get-page",
+ "arguments": {
+ "from": "0.0.0.0",
+ "limit": 1024
+ }
+ }
+
+Similarly, the IPv6 zero address can be specified in the
+:isccmd:`lease6-get-page` command:
+
+::
+
+ {
+ "command": "lease6-get-page",
+ "arguments": {
+ "from": "::",
+ "limit": 6
+ }
+ }
+
+The response has the following structure:
+
+::
+
+ {
+ "arguments": {
+ "leases": [
+ {
+ "ip-address": "2001:db8:2::1",
+ ...
+ },
+ {
+ "ip-address": "2001:db8:2::9",
+ ...
+ },
+ {
+ "ip-address": "2001:db8:3::1",
+ ...
+ },
+ {
+ "ip-address": "2001:db8:5::3",
+ ...
+ },
+ {
+ "ip-address": "2001:db8:4::1",
+ ...
+ },
+ {
+ "ip-address": "2001:db8:2::7",
+ ...
+ },
+ ...
+ ],
+ "count": 6
+ },
+ "result": 0,
+ "text": "6 IPv6 lease(s) found."
+ }
+
+Note that the leases' details were excluded from the response above for
+brevity.
+
+Generally, the returned list is not sorted in any particular order. Some
+lease database backends may sort leases in ascending order of addresses,
+but the controlling client must not rely on this behavior.
+
+The ``count`` parameter contains the number of returned leases on the
+page.
+
+To fetch the next page, the client must use the last address of the
+current page as an input to the next :isccmd:`lease4-get-page` or
+:isccmd:`lease6-get-page` command call. In this example it is:
+
+::
+
+ {
+ "command": "lease6-get-page",
+ "arguments": {
+ "from": "2001:db8:2::7",
+ "count": 6
+ }
+ }
+
+because 2001:db8:2::7 is the last address on the current page.
+
+The client may assume that it has reached the last page when the
+``count`` value is lower than that specified in the command; this
+includes the case when the ``count`` is equal to 0, meaning that no
+leases were found.
+
+.. isccmd:: lease4-get-by-hw-address
+.. _command-lease4-get-by-hw-address:
+
+.. isccmd:: lease4-get-by-client-id
+.. _command-lease4-get-by-client-id:
+
+.. isccmd:: lease6-get-by-duid
+.. _command-lease6-get-by-duid:
+
+.. isccmd:: lease4-get-by-hostname
+.. _command-lease4-get-by-hostname:
+
+.. isccmd:: lease6-get-by-hostname
+.. _command-lease6-get-by-hostname:
+
+The ``lease4-get-by-*``, ``lease6-get-by-*`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``lease4-get-by-*`` and ``lease6-get-by-*`` can be used to query the lease database and
+retrieve all existing leases matching a given feature (denoted by the ``*``). These
+can include a specified hardware address (IPv4
+only), ``client-id`` IPv4 only), ``duid`` (IPv6 only) identifiers, or hostname.
+
+An example :isccmd:`lease4-get-by-hw-address` command for getting IPv4 leases
+with a given hardware address is:
+
+::
+
+ {
+ "command": "lease4-get-by-hw-address",
+ "arguments": {
+ "hw-address": "08:08:08:08:08:08"
+ }
+ }
+
+An example of the :isccmd:`lease6-get-by-hostname` is:
+
+::
+
+ {
+ "command": "lease6-get-by-hostname",
+ "arguments": {
+ "hostname": "myhost.example.org"
+ }
+ }
+
+The ``by`` key is the only parameter. The returned response contains a detailed
+list of leases in the same format as :isccmd:`lease4-get-all` or :isccmd:`lease6-get-all`. This list can be
+empty and is usually not large.
+
+.. isccmd:: lease4-del
+.. _command-lease4-del:
+
+.. isccmd:: lease6-del
+.. _command-lease6-del:
+
+The ``lease4-del``, ``lease6-del`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`lease4-del` and :isccmd:`lease6-del` can be used to delete a lease from the lease database.
+There are two types of parameters these commands support, similar to the
+:isccmd:`lease4-get` and :isccmd:`lease6-get` commands: (``address``) for both v4 and v6, (``subnet-id``,
+``identifier-type``, ``identifier``) for v4, and (``subnet-id``, ``identifier-type``,
+``identifier``, ``type``, ``IAID``) for v6. The first type of query is used when the
+address (either IPv4 or IPv6) is known, but the details of the lease are
+not. One common use case is where an administrator wants a specified
+address to no longer be used. The second form of the command uses
+identifiers. For maximum flexibility, this interface uses identifiers as
+a pair of values: the type and the actual identifier. The currently
+supported identifiers are ``"hw-address"`` (IPv4 only), ``"client-id"`` (IPv4
+only), and ``"duid"`` (IPv6 only).
+
+An example command for deleting a lease by address is:
+
+::
+
+ {
+ "command": "lease4-del",
+ "arguments": {
+ "ip-address": "192.0.2.202"
+ }
+ }
+
+An example IPv4 lease deletion by ``"hw-address"`` is:
+
+::
+
+ {
+ "command": "lease4-del",
+ "arguments": {
+ "identifier": "08:08:08:08:08:08",
+ "identifier-type": "hw-address",
+ "subnet-id": 44
+ }
+ }
+
+
+Another parameter called ``update-ddns``, when ``true``, instructs the server to
+queue a request to :iscman:`kea-dhcp-ddns` to remove DNS entries after the lease is
+successfully deleted if:
+
+- DDNS updating is enabled (i.e. ``"dhcp-ddns":{ "enable-updates": true }``).
+- The lease's hostname is not empty.
+- At least one of the lease's DNS direction flags (``fqdn_fwd`` or ``fqdn_rev``) is true.
+
+This parameter defaults to ``false``. An example of its use is shown below:
+
+::
+
+ {
+ "command": "lease4-del",
+ "arguments": {
+ "ip-address": "192.0.2.202",
+ "update-ddns": true
+ }
+ }
+
+
+Commands :isccmd:`lease4-del` and :isccmd:`lease6-del` return a result that indicates the outcome
+of the operation. It has one of the following values: 0 (success), 1 (error),
+or 3 (empty). The empty result means that a query has been completed properly,
+but the object (a lease, in this case) has not been found.
+
+.. isccmd:: lease4-update
+.. _command-lease4-update:
+
+.. isccmd:: lease6-update
+.. _command-lease6-update:
+
+The ``lease4-update``, ``lease6-update`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :isccmd:`lease4-update` and
+:isccmd:`lease6-update` commands can be used to
+update existing leases. Since all lease database backends are indexed by
+IP addresses, it is not possible to update an address, but all other
+fields may be altered. If an address needs to be changed, please use
+:isccmd:`lease4-del` / :isccmd:`lease6-del` followed by :isccmd:`lease4-add` / :isccmd:`lease6-add`.
+
+The ``subnet-id`` parameter is optional. If not specified, or if the
+specified value is 0, Kea tries to determine its value by running a
+subnet-selection procedure. If specified, however, its value must match
+the existing subnet.
+
+The optional boolean parameter ``"force-create"`` specifies whether the
+lease should be created if it does not exist in the database. It defaults
+to ``false``, which indicates that the lease is not created if it does not
+exist. In such a case, an error is returned when trying to
+update a non-existing lease. If the ``"force-create"`` parameter is set to
+``true`` and the updated lease does not exist, the new lease is created as a
+result of receiving the :isccmd:`lease4-update` / :isccmd:`lease6-update` command.
+
+An example of a command to update an IPv4 lease is:
+
+::
+
+ {
+ "command": "lease4-update",
+ "arguments": {
+ "ip-address": "192.0.2.1",
+ "hostname": "newhostname.example.org",
+ "hw-address": "1a:1b:1c:1d:1e:1f",
+ "subnet-id": 44,
+ "force-create": true
+ }
+ }
+
+An example of a command to update an IPv6 lease is:
+
+::
+
+ {
+ "command": "lease6-update",
+ "arguments": {
+ "ip-address": "2001:db8::1",
+ "duid": "88:88:88:88:88:88:88:88",
+ "iaid": 7654321,
+ "hostname": "newhostname.example.org",
+ "subnet-id": 66,
+ "force-create": false
+ }
+ }
+
+As with other update commands, this command overwrites all the contents of the
+entry. If the lease previously had a resource assigned to it, and the
+:isccmd:`lease4-update` / :isccmd:`lease6-update` command is missing the resource, it is
+deleted from the lease database. If an incremental update of the lease is
+desired, then this can be achieved by doing a :isccmd:`lease4-get` / :isccmd:`lease6-get`
+command to get the current state of the lease, picking the lease out of the
+response, modifying it to the required outcome, and then issuing the
+:isccmd:`lease4-update` / :isccmd:`lease6-update` command with the resulting lease attached.
+
+.. isccmd:: lease4-wipe
+.. _command-lease4-wipe:
+
+.. isccmd:: lease6-wipe
+.. _command-lease6-wipe:
+
+The ``lease4-wipe``, ``lease6-wipe`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`lease4-wipe` and :isccmd:`lease6-wipe` are designed to remove all leases
+associated with a given subnet. This administrative task is expected to
+be used when an existing subnet is being retired. The leases
+are not properly expired; no DNS updates are carried out, no log
+messages are created, and hooks are not called for the leases being
+removed.
+
+An example of :isccmd:`lease4-wipe` is:
+
+::
+
+ {
+ "command": "lease4-wipe",
+ "arguments": {
+ "subnet-id": 44
+ }
+ }
+
+An example of :isccmd:`lease6-wipe` is:
+
+::
+
+ {
+ "command": "lease6-wipe",
+ "arguments": {
+ "subnet-id": 66
+ }
+ }
+
+The commands return a text description of the number of leases removed,
+plus the status code 0 (success) if any leases were removed or 3 (empty)
+if there were no leases. Status code 1 (error) may be returned if the
+parameters are incorrect or some other exception is encountered.
+
+``subnet-id`` 0 has a special meaning; it tells Kea to delete leases from
+all configured subnets. Also, the ``subnet-id`` parameter may be omitted. If
+not specified, leases from all subnets are wiped.
+
+Note: currently only memfile lease storage supports this command.
+
+.. isccmd:: lease4-resend-ddns
+.. _command-lease4-resend-ddns:
+
+.. isccmd:: lease6-resend-ddns
+.. _command-lease6-resend-ddns:
+
+The ``lease4-resend-ddns``, ``lease6-resend-ddns`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`lease4-resend-ddns` and :isccmd:`lease6-resend-ddns` can be used to generate
+a request to :iscman:`kea-dhcp-ddns` to update the DNS entries for an existing
+lease. The desired lease is selected by a single parameter, ``"ip-address"``.
+For an update request to be generated, DDNS updating must be enabled
+and DNS entries must have already been made (or attempted) for the lease.
+In other words, all of the following must be true:
+
+- DDNS updating must be enabled (i.e. ``"dhcp-ddns":{ "enable-updates": true"}``).
+- The lease's hostname must not be empty.
+- At least one of the lease's DNS direction flags (``fqdn_fwd`` or ``fqdn_rev``) must be true.
+
+An example :isccmd:`lease4-resend-ddns` command for getting a lease using an IPv4
+address is:
+
+::
+
+ {
+ "command": "lease4-resend-ddns",
+ "arguments": {
+ "ip-address": "192.0.2.1"
+ }
+ }
+
+An example of the :isccmd:`lease6-resend-ddns` query is:
+
+::
+
+ {
+ "command": "lease6-resend-ddns",
+ "arguments": {
+ "ip-address": "2001:db8:1::1"
+ }
+ }
+
+Commands :isccmd:`lease4-resend-ddns` and :isccmd:`lease6-resend-ddns` return an indication of the
+result of the operation. It has one of the following values: 0 (success), 1 (error),
+or 3 (empty). An empty result means that a query has been completed properly, but the
+object (a lease in this case) has not been found.
+
+A successful result does not mean that DNS has been successfully updated; it
+indicates that a request to update DNS has been successfully created and
+queued for transmission to :iscman:`kea-dhcp-ddns`.
+
+Here's an example of a result returned when the lease was found:
+
+::
+
+ {
+ "result": 0,
+ "text": "NCR generated for: 2001:db8:1::1, hostname: example.com."
+ }
+
+.. isccmd:: lease4-write
+.. _command-lease4-write:
+
+.. isccmd:: lease6-write
+.. _command-lease6-write:
+
+The ``lease4-write``, ``lease6-write`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:isccmd:`lease4-write` and :isccmd:`lease6-write` can be used for recovery in emergency
+situations where the memfile lease file is damaged, e.g. removed by
+accident or truncated by a full file system, but the in-memory database
+is still valid. These commands are supported only by the memfile database
+backend and write the lease database into a CSV file. They take the path
+of the file as the ``filename`` argument. If the specified output file
+is the same as the configured memfile one, the backend closes and reopens
+the file in an attempt to synchronize both the files and the in-memory images
+of the lease database. The extension ``.bak`` with server PID number is added
+to the previous filename. For example ``.bak14326``.
+
+.. note::
+
+ These commands do not replace the LFC mechanism; they should be used
+ only in exceptional circumstances, such as when recovering after
+ running out of disk space.
diff --git a/doc/sphinx/arm/hooks-lease-query.rst b/doc/sphinx/arm/hooks-lease-query.rst
new file mode 100644
index 0000000..046001f
--- /dev/null
+++ b/doc/sphinx/arm/hooks-lease-query.rst
@@ -0,0 +1,675 @@
+.. ischooklib:: libdhcp_lease_query.so
+.. _hooks-lease-query:
+
+``libdhcp_lease_query.so``: Leasequery Support
+==============================================
+
+This library provides support for DHCPv4 Leasequery as described in
+`RFC 4388 <https://tools.ietf.org/html/rfc4388>`__; and for DHCPv6
+Leasequery as described in (`RFC 5007 <https://tools.ietf.org/html/rfc5007>`__).
+
+.. note::
+
+ :ischooklib:`libdhcp_lease_query.so` is available only to ISC customers with
+ a paid support contract. For more information on subscription options,
+ please complete the form at https://www.isc.org/contact.
+
+.. note::
+
+ This library can only be loaded by the :iscman:`kea-dhcp4` or
+ :iscman:`kea-dhcp6` process.
+
+Kea version 2.3.4 added support for DHCPv6 Bulk Leasequery
+(`RFC 5460 <https://tools.ietf.org/html/rfc5460>`__);
+Kea version 2.3.5 added support for DHCPv4 Bulk Leasequery
+(`RFC 6926 <https://tools.ietf.org/html/rfc6926>`__) using
+the memfile lease backend.
+
+.. _lease-query-dhcpv4:
+
+DHCPv4 Leasequery
+~~~~~~~~~~~~~~~~~
+
+DHCPv4 simple Leasequery provides a requester the ability to query for
+active lease information for either a single IP address or a single client.
+RFC 4388 calls for three such queries:
+
+- Query by IP address
+
+ The IP address of interest is contained within the ``ciaddr`` field of
+ the query.
+- Query by hardware address
+
+ The hardware address of interest is contained with the ``chaddr`` field
+ of the query.
+- Query by client identifier
+
+ The client identifier of interest is sent in the ``dhcp-client-identifier``
+ option (61) of the query.
+
+The inbound DHCPLEASEQUERY packet must supply only one of the three values
+above. Queries which supply more than one of these values are dropped.
+
+In addition, the query must contain the IP address of the requester in
+``giaddr``. This value is used not only as the destination for the
+query response but also to validate the requester against a known
+list of IP addresses which are permitted to query. This list of valid
+requester addresses is specified as part of the Leasequery hook library's
+configuration (see the section on configuration below).
+
+In response to a valid query, the server returns one of three message
+types:
+
+- DHCPLEASEUNKNOWN
+
+ Returned when the IP address of interest is not one the server knows
+ about (query by IP address); or there are no active leases for the
+ client of interest (query by hardware address or client ID).
+
+- DHCPLEASEUNASSIGNED
+
+ Returned when the IP address is one the server knows of but for which
+ there are no active leases (applies only to query by IP address).
+
+- DHCPLEASEACTIVE
+
+ Returned when there is at least one active lease found matching the
+ criteria.
+
+For both DHCPLEASEUNKNOWN and DHCPLEASEUNASSIGNED responses, the only
+information sent back to the requester in response is the query parameter
+itself (i.e. one of: IP address, hardware address, or client identifier).
+
+For DHCPLEASEACTIVE the server provides the following information
+for the newest active lease that matches the criteria, in the response:
+
+- ``ciaddr`` - set to the lease's IP address
+- ``chaddr`` - set to the lease's hardware address
+
+In addition, one or more of the following options are included:
+
+.. tabularcolumns:: |p{0.2\linewidth}|p{0.1\linewidth}|p{0.7\linewidth}|
+
+.. table:: DHCPLEASEACTIVE options
+ :class: longtable
+ :widths: 30 10 70
+
+ +------------------------------+-------+-----------------------------------------------+
+ | Option | Code | Content |
+ +==============================+=======+===============================================+
+ | dhcp-client-identifier | 61 | copied from the lease (if appropriate) |
+ +------------------------------+-------+-----------------------------------------------+
+ | client-last-transaction-time | 91 | the amount of time that has elapsed since the |
+ | | | lease's client-last-transaction-time (CLTT). |
+ | | | This value is also used by the server to |
+ | | | adjust lifetime and timer values. |
+ +------------------------------+-------+-----------------------------------------------+
+ | dhcp-lease-time | 51 | lease's lifetime reduced by CLTT |
+ +------------------------------+-------+-----------------------------------------------+
+ | dhcp-renewal-time | 58 | as controlled by kea-dhcp4 configuration and |
+ | | | then reduced by CLTT |
+ +------------------------------+-------+-----------------------------------------------+
+ | dhcp-rebind-time | 59 | as dictated by kea-dhcp4 configuration and |
+ | | | then reduced by CLTT |
+ +------------------------------+-------+-----------------------------------------------+
+ | dhcp-agent-options | 82 | if stored on the lease. (See |
+ | | | :ref:`dhcp4-store-extended-info`) |
+ +------------------------------+-------+-----------------------------------------------+
+ | associated-ip | 92 | a list of all other IP addresses for which |
+ | | | the client has active leases. (Does not apply |
+ | | | to query by IP address) |
+ +------------------------------+-------+-----------------------------------------------+
+
+The ``dhcp-server-identifier`` option (54) is returned in all responses in keeping with
+RFC 2131, section 4.3.1.
+
+RFC 4388 allows requesters to ask for specific options via the
+``dhcp-parameter-request-list`` (PRL, option 55). This is not currently supported in Kea.
+
+.. _lease-query-dhcpv4-config:
+
+DHCPv4 Leasequery Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Configuring the Leasequery hook library for use is straightforward. It
+supports a single parameter, ``requesters``, which is a list of IP addresses from
+which DHCPLEASEQUERY packets are accepted. In other words, it is a list of
+known requesters. The following code shows an example configuration with two requester
+addresses:
+
+::
+
+ {
+ "hooks-libraries": [
+ {
+ "library": "lib/kea/hooks/libdhcp_lease_query.so",
+ "parameters": {
+ "requesters": [ "192.0.1.1", "10.0.0.2" ]
+ }
+ }
+ ],
+ ...
+ }
+
+.. note::
+
+ For security purposes, there is no way to specify wildcards. Each requester address
+ must be explicitly listed.
+
+.. _lease-query-dhcpv6:
+
+DHCPv6 Leasequery
+~~~~~~~~~~~~~~~~~
+
+DHCPv6 simple Leasequery gives a requester the ability to query for
+active lease information for either a single IP address or a single client
+DUID. The query type and parameters are conveyed in an ``lq-query`` option (44)
+attached to a ``DHCPV6_LEASEQUERY`` message:
+
+- ``query-type``
+
+ This is either ``query-by-address`` (1) or ``query-by-clientid`` (2)
+
+- ``link-address``
+
+ The global link address, when not empty, instructs the query to be
+ limited to leases within that "link." Kea uses this value to
+ select only leases that belong to subnets whose prefix matches
+ this value. Active leases for prefix delegations for
+ a matched subnet are included in the query reply, even if the
+ delegated prefix itself falls outside the subnet prefix.
+
+- ``query-options``
+
+ A single ``iaaddr`` option (12) must be supplied when querying by address.
+ When querying by client ID, a single ``clientid`` option (1) must be
+ supplied. RFC 5007 also calls for an optional, ``oro`` option (6), to
+ request specific options be returned for matched leases. This is
+ not currently implemented.
+
+.. note::
+
+ `RFC 5007, Section 3.3 <https://tools.ietf.org/html/rfc5007#section-3.3>`__
+ states that querying by IP address should return either a lease (e.g.
+ binding) for the address itself or a lease for a delegated prefix that
+ contains the address. The latter case is not supported by releases
+ prior to Kea 2.3.7.
+
+``DHCPV6_LEASEQUERY`` queries are only honored if the source address of
+the query matches an entry in a list of known IP addresses which are
+permitted to query. This list of valid requester addresses is specified
+as part of the Leasequery hook library’s configuration (see the section
+on configuration below). Queries received from unknown requesters are
+logged and dropped.
+
+In response to a valid query, the server carries out the requisite
+activities and returns a ``DHCPV6_LEASEQUERY_REPLY``. All replies contain
+at least a ``status-code`` option (13) that indicates the outcome of the query
+as detailed in the following table:
+
+.. tabularcolumns:: |p{0.5\linewidth}|p{0.3\linewidth}|p{0.1\linewidth}|p{0.3\linewidth}|
+
+.. table:: DHCPV6_LEASEQUERY_REPLY status option values per query outcome
+ :class: longtable
+ :widths: 50 30 10 30
+
+ +--------------------------------------+-------------------------+--------+------------------------------+
+ | | Status | Status | Status |
+ | Query Outcome | Label | Code | Text |
+ +======================================+=========================+========+==============================+
+ | Invalid query type field | STATUS_UnknownQueryType | 7 | "unknown query-type" |
+ +--------------------------------------+-------------------------+--------+------------------------------+
+ | Query by IP address that does not | STATUS_Malformed | 10 | "missing D6O_IAADDR" |
+ | contain an address option | | | |
+ +--------------------------------------+-------------------------+--------+------------------------------+
+ | Query by IP address for an address | STATUS_NotConfigured | 9 | "address not in a configured |
+ | that does fall within any configured | | | pool" |
+ | pools | | | |
+ +--------------------------------------+-------------------------+--------+------------------------------+
+ | Query by IP address which found only | STATUS_Success | 0 | "inactive lease exists" |
+ | an inactive lease (e.g. expired, | | | |
+ | declined, reclaimed-expired) | | | |
+ +--------------------------------------+-------------------------+--------+------------------------------+
+ | Query by IP address that found no | STATUS_Success | 0 | "no active lease" |
+ | leases (active or otherwise) | | | |
+ +--------------------------------------+-------------------------+--------+------------------------------+
+ | Query by IP address that found an | STATUS_Success | 0 | "active lease found" |
+ | active lease for the address | | | |
+ +--------------------------------------+-------------------------+--------+------------------------------+
+ | Query by Client ID that does not | STATUS_Malformed | 10 | "missing D6O_CLIENTID" |
+ | contain a client ID option | | | |
+ +--------------------------------------+-------------------------+--------+------------------------------+
+ | Query by Client ID with a link | STATUS_NotConfigured | 9 | "not a configured link" |
+ | address that does not match any | | | |
+ | configured subnets | | | |
+ +--------------------------------------+-------------------------+--------+------------------------------+
+ | Query by client ID which found no | STATUS_Success | 0 | "no active leases" |
+ | matching leases | | | |
+ +--------------------------------------+-------------------------+--------+------------------------------+
+ | Query by client ID which found one | STATUS_Success | 0 | "active lease(s) found" |
+ | or more active leases | | | |
+ +--------------------------------------+-------------------------+--------+------------------------------+
+
+For those scenarios where the query was either invalid or for which no matching active
+leases were found, the ``DHCPV6_LEASEQUERY_REPLY`` only contains the ``status-code``
+option (12) per the above table.
+
+When a query finds active leases in more than one subnet and the query's ``link-address``
+is empty, then, in addition to the status-code, the ``DHCPV6_LEASEQUERY_REPLY``
+contains an ``lq-client-link`` option (48). The ``lq-client-link`` contains a list of
+IPv6 addresses, one for each subnet in which a lease was found (see
+`RFC 5007, Section 4.1.2.5 <https://tools.ietf.org/html/rfc5007#section-4.1.2.5>`__)
+If, however, the query's ``link-address`` is not empty, the list of queries is
+pruned to contain only leases that belong to that subnet.
+
+When the query results in one or more active leases which all belong to a single
+subnet, in addition to the ``status-code``, the ``DHCPV6_LEASEQUERY_REPLY`` contains a
+``client-data`` option (45) (see
+`RFC 5007, Section 4.1.2.2 <https://tools.ietf.org/html/rfc5007#section-4.1.2.2>`__).
+The client-data option encapsulates the following options:
+
+.. tabularcolumns:: |p{0.2\linewidth}|p{0.1\linewidth}|p{0.7\linewidth}|
+
+.. table:: OPTION_CLIENT_DATA returned when active lease(s) are found
+ :class: longtable
+ :widths: 30 10 70
+
+ +------------------------------+-------+-----------------------------------------------+
+ | Option | Code | Content |
+ +==============================+=======+===============================================+
+ | clientid | 1 | copied from the lease (if one exists) |
+ +------------------------------+-------+-----------------------------------------------+
+ | clt-time | 46 | amount of time that has elapsed since the |
+ | | | lease's client-last-transaction-time (CLTT). |
+ | | | This value will also be used by the server to |
+ | | | adjust lifetime and timer values. |
+ +------------------------------+-------+-----------------------------------------------+
+ | iaaddr | 5 | One option per matched address. Fields in |
+ | | | each option: |
+ | | | - lease address |
+ | | | - valid lifetime reduced by CLTT |
+ | | | - preferred lifetime reduced by CLTT |
+ +------------------------------+-------+-----------------------------------------------+
+ | iaprefix | 26 | One option per matched prefix. Fields in |
+ | | | each option: |
+ | | | - prefix |
+ | | | - prefix length |
+ | | | - valid lifetime reduced by CLTT |
+ | | | - preferred lifetime reduced by CLTT |
+ +------------------------------+-------+-----------------------------------------------+
+
+If the lease with the most recent client-last-transaction-time (CLTT)
+value has relay information in its user-context (see
+:ref:`store-extended-info-v6`), then an ``OPTION_LQ_RELAY_DATA`` option is
+added to the reply (see
+`RFC 5007, Section 4.1.2.4 <https://tools.ietf.org/html/rfc5007#section-4.1.2.4>`__).
+
+The relay information on the lease is a list with an entry for each
+relay layer the client packet (e.g. ``DHCPV6_REQUEST``) traversed, with the
+first entry in the list being the outermost layer (closest to the server). The
+``peer-address`` field of the ``lq-rely-option`` is set to the peer address of this
+relay. The list of relays is then used to construct a ``DHCPV6_RELAY_FORW`` message
+equivalent to that which contained the client packet, minus the client packet.
+This message is stored in the ``DHCP-relay-message`` field of the ``lq-relay-data`` option.
+
+.. _lease-query-dhcpv6-config:
+
+DHCPv6 Leasequery Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Configuring the Leasequery hook library for use is straightforward. It
+supports a single parameter, ``requesters``, which is a list of IP addresses from
+which DHCPV6_LEASEQUERY packets are accepted. In other words, it is a list of
+known requesters. The following code shows an example configuration with two requester
+addresses:
+
+::
+
+ {
+ "hooks-libraries": [
+ {
+ "library": "lib/kea/hooks/libdhcp_lease_query.so",
+ "parameters": {
+ "requesters": [ "2001:db8:1::1", "2001:db8:2::1" ],
+ "prefix-lengths": [ 72 ]
+ }
+ }
+ ],
+ ...
+ }
+
+.. note::
+
+ For security purposes, there is no way to specify wildcards. Each requester address
+ must be explicitly listed.
+
+When a query by IP address does not match an existing address lease,
+a search for a matching delegated prefix is conducted. This is carried
+out by iterating over a list of prefix lengths, in descending order,
+extracting a prefix of that length from the query address and searching
+for a delegation matching the resulting prefix. This continues for each
+length in the list until a match is found or the list is exhausted.
+
+By default, the list of prefix lengths to use in the search is determined
+dynamically after (re)configuration events. This resulting list will
+contain unique values of ``delegated-len`` gleaned from the currently
+configured set of PD pools.
+
+There is an optional parameter, ``prefix-lengths``, shown above which
+provides the ability to explicitly configure the list rather than having
+it be determined dynamically. This provides tighter control over which
+prefix lengths are searched. In the above example, the prefix length
+search will be restricted to single pass, using a length of 72, regardless
+of whether or not there are pools using other values for ``delegated-len``.
+Specifying an empty list, as shown below:
+
+::
+
+ :
+ "prefix-lengths": [ ]
+ :
+
+disables the search for delegated prefixes for query by IP address.
+
+.. _bulk-lease-query-dhcpv4:
+
+DHCPv4 Bulk Leasequery
+~~~~~~~~~~~~~~~~~~~~~~
+
+DHCPv4 Bulk Leasequery gives a requester the ability to query for
+active lease information over a TCP connection. This allows the server
+to return all leases matching a given query.
+
+Two of the query types identified by RFC 4388 - Query by MAC address and
+Query by Client-identifier - are Bulk Leasequery types specified by RFC
+6926. That RFC also defines these new Bulk Leasequery types:
+
+- Query by Relay Identifier
+
+ The query carries an RAI (dhcp-agent-options (82)) option with
+ a relay-id (12) sub-option.
+
+- Query by Remote ID
+
+ The query carries an RAI (dhcp-agent-options (82) option) with
+ a remote-id (2) sub-option.
+
+- Query for All Configured IP Addresses
+
+ This query type is selected when no other query type is specified.
+
+RFC 6926 also defines new options for Bulk Leasequery:
+
+- status-code (151)
+
+ This reply option carries a status code such as MalformedQuery or
+ NotAllowed, with an optional text message.
+
+- base-time (152)
+
+ This reply option carries the absolute current time that the response
+ was created. All other time-based reply options are related to
+ this value.
+
+- start-time-of-state (153)
+
+ This reply option carries the time of the lease's transition into its
+ current state.
+
+- query-start-time (154)
+
+ This query option specifies a start query time; replies will only
+ contain leases that are older than this value.
+
+- query-end-time (155)
+
+ This query option specifies an end query time; replies will only
+ contain leases that are newer than this value.
+
+- dhcp-state (156)
+
+ This reply option carries the lease state.
+
+- data-source (157)
+
+ This reply option carries the source of the data as a remote flag.
+
+RFC 6926 reuses and extends the Virtual Subnet Selection option (221)
+defined in RFC 6607.
+
+.. note::
+
+ Kea does not yet support querying for all configured IP addresses,
+ so the dhcp-state option cannot be used, as only active leases can be
+ returned in replies. Kea does not keep the start time of the lease's state,
+ nor the local/remote information, so it cannot emit the corresponding
+ start-time-of-state and data-source options. Kea does not support VPNs
+ so the presence of option 221 in the query is considered a
+ (NotAllowed) error.
+
+.. note::
+
+ The new query types are only supported with the memfile lease backend.
+
+.. _bulk-lease-query-dhcpv6:
+
+DHCPv6 Bulk Leasequery
+~~~~~~~~~~~~~~~~~~~~~~
+
+DHCPv6 Bulk Leasequery gives a requester the ability to query for
+active lease information over a TCP connection. This allows the server
+to return all active leases matching a query.
+
+New query types are available: ``query-by-relay-id`` (3),
+``query-by-link-address`` (4), and ``query-by-remote-id`` (5).
+
+A new status code, ``STATUS_QueryTerminated`` (11), has been defined but it is
+not yet used by the hook library.
+
+.. note::
+
+ Kea attempts to map link address parameters to the prefixes of configured
+ subnets. If a given address falls outside all configured subnet prefixes,
+ the query fails with a status code of ``STATUS_NotConfigured``. If
+ the link address parameter for ``query-by-relay-id`` or ``query-by-remote-id``
+ is not ``::`` (i.e. not empty), only delegated prefixes that lie within matching
+ subnet prefixes are returned. Currently, ``query-by-address`` does not
+ support finding delegated prefixes by specifying an address that lies within
+ the prefix.
+
+.. note::
+
+ The new query types are only supported with the memfile lease backend.
+
+.. _bulk-lease-query-dhcpv6-config:
+
+Bulk Leasequery Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Bulk Leasequery configuration is done via a new map parameter, ``advanced``,
+with these possible entries:
+
+- ``bulk-query-enabled``
+
+ When ``true``, Kea accepts connections from IP addresses in the requesters
+ list and processes received Bulk Leasequeries. The default is ``false``.
+
+- ``active-query-enabled``
+
+ This is an anticipated parameter: if set, it must be ``false``.
+
+- ``extended-info-tables-enabled``
+
+ When ``true``, the lease backend manages DHCPv6 lease extended info
+ (relay info) in tables to support the new DHCPv6 Bulk Leasequery
+ by-relay-id and by-remote-id types. The default is to use the
+ same value as ``bulk-query-enabled``.
+
+- ``lease-query-ip``
+
+ This is the IP address upon which to listen for connections. The address must be
+ of the same family as the server, e.g. IPv6 for DHCPv6 server.
+
+- ``lease-query-port``
+
+ This is the port upon which to listen. The default is 67 for IPv4 and 547 for IPv6,
+ i.e. the same value as for the UDP DHCP service but for TCP.
+
+- ``max-bulk-query-threads``
+
+ This indicates the maximum number of threads that Bulk Leasequery processing
+ should use. A value of 0 instructs the server to use the same number of
+ threads that the Kea core is using for DHCP multi-threading.
+ The default is 0.
+
+- ``max-requester-connections``
+
+ This is the maximum number of concurrent requester connections. The default
+ is 10; the value must be greater than 0.
+
+- ``max-concurrent-queries``
+
+ This is the maximum number of concurrent queries per connection. The value 0
+ allows Kea to determine the number, and is the default.
+
+- ``max-requester-idle-time``
+
+ This is the amount of time that may elapse after receiving data from a requester
+ before its connection is closed as idle, in seconds. The default
+ is 300.
+
+- ``max-leases-per-fetch``
+
+ This is the maximum number of leases to return in a single fetch. The default is 100.
+
+Once TLS is supported, we expect to implement common TLS parameters.
+
+For instance, for DHCPv4:
+
+::
+
+ {
+ "hooks-libraries": [
+ {
+ "library": "lib/kea/hooks/libdhcp_lease_query.so",
+ "parameters": {
+ "requesters": [ "192.0.2.1", "192.0.2.2" ],
+ "advanced" : {
+ "bulk-query-enabled": true,
+ "active-query-enabled": false,
+
+ "lease-query-ip": "127.0.0.1",
+ "lease-query-tcp-port": 67,
+
+ "max-bulk-query-threads": 0,
+ "max-requester-connections": 10,
+ "max-concurrent-queries": 4,
+ "max-requester-idle-time": 300,
+ "max-leases-per-fetch": 100
+ }
+ }
+ }
+ ],
+ ...
+ }
+
+or for DHCPv6:
+
+::
+
+ {
+ "hooks-libraries": [
+ {
+ "library": "lib/kea/hooks/libdhcp_lease_query.so",
+ "parameters": {
+ "requesters": [ "2001:db8:1::1", "2001:db8:2::1" ],
+ "advanced" : {
+ "bulk-query-enabled": true,
+ "active-query-enabled": false,
+
+ "extended-info-tables-enabled": true,
+
+ "lease-query-ip": "::1",
+ "lease-query-tcp-port": 547,
+
+ "max-bulk-query-threads": 0,
+ "max-requester-connections": 10,
+ "max-concurrent-queries": 4,
+ "max-requester-idle-time": 300,
+ "max-leases-per-fetch": 100
+ }
+ }
+ }
+ ],
+ ...
+ }
+
+.. _updating-existing-leases:
+
+Updating Existing Leases in SQL Lease Backends
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Bulk Lease Query required additions to the lease data stored. With SQL lease
+backends, leases created prior to the server being configured for Bulk Lease
+Query will not contain the new data required. In order to populate this data
+it is necessary to run API commands:
+
+.. isccmd:: extended-info4-upgrade
+.. _command-extended-info4-upgrade:
+
+For DHCPv4 lease data, the command is:
+
+::
+
+ {
+ "command": "extended-info4-upgrade"
+ }
+
+For DHCPv6 lease data, the command is:
+
+.. isccmd:: extended-info6-upgrade
+.. _command-extended-info6-upgrade:
+
+for extended info used for by relay id and by remote id the command is:
+
+::
+
+ {
+ "command": "extended-info6-upgrade"
+ }
+
+
+In all cases the response will indicate whether it succeeded or failed
+and include either the count of leases updated or the nature of the failure:
+
+::
+
+ {
+ "result": 0,
+ "text": "Upgraded 1000 leases"
+ }
+
+
+This ``extended-info6-upgrade`` command must be called when:
+
+- the database schema was upgraded from a previous version
+
+- Bulk Lease Query was not enabled (tables are maintained only when v6 BLQ is
+ enabled)
+
+- data in tables do not seem to be consistent (tables are not maintained in
+ an atomic way so consistency is not guaranteed. For instance when a database
+ is shared between several servers races can happen between updates)
+
+The operation of extended info command is governed by ``extended-info-checks``
+parameter under the sanity-checks element. Please see :ref:`sanity-checks4`
+or :ref:`sanity-checks6`.
+
+For large numbers of leases this command may take some time to complete.
+
+.. note::
+
+ Existing leases must have been created by Kea with ``store-extended-info``
+ enabled in order for the new data from extended info to be extracted
+ and stored.
+
diff --git a/doc/sphinx/arm/hooks-legal-log.rst b/doc/sphinx/arm/hooks-legal-log.rst
new file mode 100644
index 0000000..be2a07c
--- /dev/null
+++ b/doc/sphinx/arm/hooks-legal-log.rst
@@ -0,0 +1,1094 @@
+.. ischooklib:: libdhcp_legal_log.so
+.. _hooks-legal-log:
+
+``libdhcp_legal_log.so``: 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.
+
+.. note::
+
+ :ischooklib:`libdhcp_legal_log.so` is available as a premium
+ hook library from ISC. Please visit https://www.isc.org/shop/ to purchase
+ the premium hook libraries, or contact us at https://www.isc.org/contact for
+ more information.
+
+.. note::
+
+ This library can only be loaded by the :iscman:`kea-dhcp4` or :iscman:`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. :ischooklib:`libdhcp_legal_log.so`
+can save logs to a text file or to a database (created using
+:iscman:`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, :iscman:`kea-dhcp4` can be configured to load
+:ischooklib:`libdhcp_legal_log.so` 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 :iscman:`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": [
+ {
+ "id": 1,
+ "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": [
+ {
+ "id": 1,
+ "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: :isccmd:`lease4-add`, :isccmd:`lease4-update`, and :isccmd:`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
+
+If High availability module is enabled, the partner will periodically send lease
+commands which have a similar format, the only difference is that the issuer of
+the command is 'HA partner' instead of 'Administrator'.
+
+::
+
+ *timestamp* HA partner added ...
+
+or
+
+::
+
+ *timestamp* HA partner updated ...
+
+or
+
+::
+
+ *timestamp* HA partner deleted ...
+
+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, ':'), '')), '')"
+ }
+
+Details:
+
+.. 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, ':'), '')), ''))"
+ }
+
+Details:
+
+.. 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: :isccmd:`lease6-add`, :isccmd:`lease6-update`, and :isccmd:`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
+
+If High availability module is enabled, the partner will periodically send lease
+commands which have a similar format, the only difference is that the issuer of
+the command is 'HA partner' instead of 'Administrator'.
+
+::
+
+ *timestamp* HA partner added ...
+
+or
+
+::
+
+ *timestamp* HA partner updated ...
+
+or
+
+::
+
+ *timestamp* HA partner deleted ...
+
+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, ':'), '')), ''), '')"
+ }
+
+Details:
+
+.. 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, ':'), '')), ''), ''))"
+ }
+
+Details:
+
+.. 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 different than for
+:ischooklib:`libdhcp_lease_cmds.so`, :ischooklib:`libdhcp_host_cmds.so`, and
+:ischooklib:`libdhcp_cb_cmds.so`, 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.
+
+During server startup, the inability to connect to any of the configured
+backends is considered fatal only if ``retry-on-startup`` is set to ``false``
+(the default). A fatal error is logged and the server exits, based on the idea
+that the configuration should be valid at startup. Exiting to the operating
+system allows nanny scripts to detect the problem.
+If ``retry-on-startup`` is set to ``true``, the server will start reconnection
+attempts even at server startup or on reconfigure events, and will honor the
+action specified in the ``on-fail`` parameter.
diff --git a/doc/sphinx/arm/hooks-limits.rst b/doc/sphinx/arm/hooks-limits.rst
new file mode 100644
index 0000000..42bb39a
--- /dev/null
+++ b/doc/sphinx/arm/hooks-limits.rst
@@ -0,0 +1,211 @@
+.. ischooklib:: libdhcp_limits.so
+.. _hooks-limits:
+
+``libdhcp_limits.so``: Limits to Manage Lease Allocation and Packet Processing
+==============================================================================
+
+This hook library enables two types of limits:
+
+1. Lease limiting: allow a maximum of ``n`` leases assigned at any one time.
+2. Rate limiting: allow a maximum of ``n`` packets per ``time_unit`` to receive a response.
+
+.. note::
+
+ :ischooklib:`libdhcp_limits.so` is available only to ISC customers with
+ a paid support contract. For more information on subscription options,
+ please complete the form at https://www.isc.org/contact.
+
+.. _hooks-limits-configuration:
+
+Configuration
+~~~~~~~~~~~~~
+
+The following examples are for :iscman:`kea-dhcp6`, but they apply equally to
+:iscman:`kea-dhcp4`. The wildcards ``"<limit-type>"`` and ``"<limit-value>"`` need to be replaced
+with the respective keys and values for each limit type described in the sections following this
+one.
+
+The library can be loaded by both :iscman:`kea-dhcp4` and :iscman:`kea-dhcp6` servers by adding its path in the
+``"hooks-libraries"`` element of the server's configuration.
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/libdhcp_limits.so"
+ }
+ ]
+ }
+ }
+
+This alone does not limit anything. The desired limits are added to the user context in the
+configuration portion of the element that identifies the clients to be limited: a client class or a
+subnet. Upon reconfiguration, if Kea picked up on the configured limits, it logs one line for
+each configured limit. The log message contains ``LIMITS_CONFIGURED`` in its identifier.
+
+This is how a lease limit is defined for a client class:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "client-classes": [
+ {
+ "name": "cable-modem-1",
+ "test": "option[123].hex == 0x000C4B1E",
+ "user-context": {
+ "limits": {
+ "<limit>": "<limit-value>"
+ }
+ }
+ }
+ ]
+ }
+ }
+
+This is how a lease limit is defined for a global subnet:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8::/64",
+ "user-context": {
+ "limits": {
+ "<limit>": "<limit-value>"
+ }
+ }
+ }
+ ]
+ }
+ }
+
+This is how a lease limit is defined for a subnet inside a shared network:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "shared-networks": [
+ {
+ "subnet6": [
+ {
+ "id": 1,
+ "subnet": "2001:db8::/64",
+ "user-context": {
+ "limits": {
+ "<limit>": "<limit-value>"
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+.. note::
+
+ The Limits hook library uses the class name to identify a client class and the subnet ID to
+ identify a subnet. Changing a test expression in a client class or the network range of a
+ subnet while leaving the name or ID unchanged does not reset the lease count for the
+ respective client class or subnet. To reset the lease count, change the client class name
+ or the subnet ID.
+
+.. note::
+
+ Database connection retries are not attempted on startup if the
+ :ischooklib:`libdhcp_limits.so` is loaded because the hook library requires a
+ valid connection to the database to check if JSON format is supported and to
+ recount class limits.
+
+.. _hooks-limits-lease-limiting:
+
+Lease Limiting
+~~~~~~~~~~~~~~
+
+It is possible to limit the number of leases that a group of clients can get from a Kea DHCP server
+or from a set of collaborating Kea DHCP servers.
+
+The value of a lease limit can be specified as an unsigned integer in 32 bits, i.e. between ``0`` and
+``4,294,967,295``. Each lease type can be limited individually. IPv4 leases and IPv6 IA_NA leases
+are limited through the ``"address-limit"`` configuration entry. IPv6 IA_PD leases are limited
+through the ``"prefix-limit"`` configuration entry. Here are some examples:
+
+* ``"address-limit": 4``
+* ``"prefix-limit": 2``
+
+For lease limiting, client classes and the associated lease counts - which are
+checked against the configured limits - are updated for each lease in the following hook callouts:
+
+* ``lease4_select``
+* ``lease4_renew``
+* ``lease6_select``
+* ``lease6_renew``
+* ``lease6_rebind``
+
+As a result, classes for which ``"only-if-required"`` is "true" cannot be lease-limited.
+Please refer to :ref:`the classification steps <classify-classification-steps>` for more information on which
+client classes can be used to limit the number of leases.
+
+.. note::
+
+ Under load, a Kea DHCP server may allocate more leases than the limit strictly allows. This only has a chance of
+ happening during high traffic surges, coming from clients belonging to the same class or the
+ same subnet, depending on what is limited. Users may be interested in following the development of
+ `atomic lease limits <https://gitlab.isc.org/isc-projects/kea/-/issues/2449>`__ in ISC's GitLab instance.
+
+.. _hooks-limits-rate-limiting:
+
+Rate Limiting
+~~~~~~~~~~~~~
+
+It is possible to limit the frequency or rate at which inbound packets receive a response.
+
+The value of a rate limit can be specified in the format ``"<p> packets per <time-unit>"``. ``<p>``
+is any number that can be represented by an unsigned integer in 32 bits, i.e. between ``0`` and
+``4,294,967,295``. ``<time-unit>`` can be any of ``second``, ``minute``, ``hour``, ``day``,
+``week``, ``month``, or ``year``. A ``month`` is considered to be 30 days for
+simplicity; similarly, a ``year`` is 365 days for limiting purposes. This syntax
+covers a wide range of rates, from one lease per year to four billion leases per
+second. This value is assigned to the ``"rate-limit"`` configuration entry.
+Here are some examples:
+
+* ``"rate-limit": 1 packet per second``
+* ``"rate-limit": 4 packets per minute``
+* ``"rate-limit": 16 packets per hour``
+
+The configured value of ``0`` packets is a convenient way of disabling packet processing for certain
+clients entirely. As such, it means its literal value and is not a special value for disabling
+limiting altogether, as might be imagined. Disabling limiting entirely is achieved by removing
+the ``"rate-limit"`` leaf configuration entry, the ``"limits"`` map or user context
+around it, or the hook library configuration. The same applies to the value of ``0`` in lease
+limiting. However, that use case is best achieved with rate limiting; it puts less computational
+strain on Kea, since the action of dropping the request or sending a NAK is decided earlier.
+
+In terms of rate limiting, client classes are evaluated at the ``pkt4_receive`` and the
+``pkt6_receive`` callout, respectively, so that rate limits are checked as early as possible in the
+packet-processing cycle. Thus, only those classes which are assigned to the packet solely via an
+independent test expression can be used. Classes that depend on host reservations or the special
+``BOOTP`` or ``KNOWN`` classes, and classes that are marked with ``"only-if-required": true``,
+cannot be rate limited. See :ref:`the classification steps <classify-classification-steps>` for
+more details on which client classes can be used to limit the packet rate.
+
+Rate limits based on subnet are enforced only on the initially selected subnet for a given packet.
+If the selected subnet is subsequently changed, as may be the case for subnets in a
+shared network or when reselection is enabled in libraries such as the RADIUS hook, rate
+limits on the newly selected subnet are ignored. In other words, packets are gated only by
+the rate limit on the original subnet.
+
+.. note::
+
+ It may seem logical to think that assigning a rate limit of ``n`` packets per time unit results
+ in ``n`` DORA or ``n`` SARR exchanges. However, by default, all inbound packets are counted - meaning
+ that a full message exchange accounts for two packets. To achieve the effect of counting an
+ exchange only once, use client-class rate-limiting with a test expression that binds
+ ``pkt4.msgtype`` to DHCPDISCOVER messages or ``pkt6.msgtype`` to SOLICIT messages.
diff --git a/doc/sphinx/arm/hooks-perfmon.rst b/doc/sphinx/arm/hooks-perfmon.rst
new file mode 100644
index 0000000..3c039dc
--- /dev/null
+++ b/doc/sphinx/arm/hooks-perfmon.rst
@@ -0,0 +1,39 @@
+.. ischooklib:: libdhcp_perfmon.so
+.. _hooks-perfmon:
+
+``libdhcp_perfmon.so``: PerfMon
+===============================
+
+This hook library can be loaded by either kea-dhcp4 or kea-dhcp6 servers
+to extend them with the ability to track and report performance related data.
+
+.. note::
+
+ This library is currently under development and not yet functional.
+
+Overview
+~~~~~~~~
+
+The library, added in Kea 2.5.6, can be loaded by the :iscman:`kea-dhcp4` or
+:iscman:`kea-dhcp6` daemon by adding it to the ``hooks-libraries`` element of
+the server's configuration:
+
+.. code-block:: javascript
+
+ {
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/libdhcp_perfmon.so",
+ "parameters": {
+ ...
+ }
+ },
+ ...
+ ],
+ ...
+ }
+
+Configuration
+~~~~~~~~~~~~~
+
+ TBD
diff --git a/doc/sphinx/arm/hooks-ping-check.rst b/doc/sphinx/arm/hooks-ping-check.rst
new file mode 100644
index 0000000..efa2fef
--- /dev/null
+++ b/doc/sphinx/arm/hooks-ping-check.rst
@@ -0,0 +1,169 @@
+.. ischooklib:: libdhcp_ping_check.so
+.. _hooks-ping-check:
+
+``libdhcp_ping_check.so``: Ping Check
+=====================================
+
+This hook library adds the ability to perform a "ping check" of a candidate
+IPv4 address prior to offering it to a DHCP client. This feature is similar
+to a behavior available in ISC DHCP and one suggested in `RFC
+2131 <https://tools.ietf.org/html/rfc2131>`__ , see section 3.1, item 2.
+
+.. note::
+
+ :ischooklib:`libdhcp_ping_check.so` is available only to ISC customers
+ with a paid support contract. For more information on subscription options,
+ please complete the form at https://www.isc.org/contact.
+
+Overview
+~~~~~~~~
+
+The library, added in Kea 2.5.4, can be loaded by the :iscman:`kea-dhcp4` daemon
+by adding it to the ``hooks-libraries`` element of the server's configuration:
+
+.. code-block:: javascript
+
+ {
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/libdhcp_ping_check.so",
+ "parameters": {
+ ...
+ }
+ },
+ ...
+ ],
+ ...
+ }
+
+When the library is loaded :iscman:`kea-dhcp4` will conduct a ping-check prior to
+offering a lease to client if all of the following conditions are true:
+
+1. Ping check hook library is loaded.
+
+2. Ping checking is enabled.
+
+3. The server is responding to a DHCPDISCOVER.
+
+4. The candidate lease is neither active nor reserved.
+
+5. Any of the following are true:
+
+ a. This is the first offer of this lease to this client. This check
+ can only be done if `offer-lifetime` is greater than zero (i.e. temporary
+ allocation on DHCPDISCOVER is enabled). If `offer-lifetime` is zero
+ ping checks are done for every DHCPOFFER as the server has no way to
+ know it has made prior offers.
+
+ b. The lease is being offered to a client other than its previous owner.
+
+ c. The lease is being offered to its previous owner and more than a
+ configurable number of seconds, `ping-cltt-secs`, have elapsed since
+ CLTT of the original lease.
+
+When the ping check library is loaded, in response to a DHCPDISCOVER the
+:iscman:`kea-dhcp4` will:
+
+1. Select a candidate IPv4 address through normal processes and use it to
+construct a DHCPOFFER.
+
+2. Park the DHCPOFFER and request a ping-check from the ping-check hook
+library via its `lease4_offer` callout.
+
+3. The callout will test conditions described above. If they are not
+satisfied it will return without conducting a check, and the server
+will send the DHCPOFFER to the client. Otherwise the callout will
+initiate a ping-check for the lease address.
+
+4. Upon conclusion of the ping-check, the server will either send the DHCPOFFER
+to the client if the check concluded that the address is available, or discard
+the DHCPFOFFER and create a DECLINED lease for the address.
+
+Each ping-check consists of the following steps:
+
+1. If the number of ECHO REPLYs sent is less than the configured
+minimum number to send, send an ICMP ECHO REQUEST to the lease address.
+Otherwise, conclude that the address is available.
+
+2. If no ECHO REPLY is received within a configurable amount of time
+return to step 1.
+
+3. Upon receipt of an ICMP ECHO REPLY, conclude that the lease is NOT available.
+
+4. Any of the following occur:
+
+ a. Receipt of an ICMP DESTINATION UNREACHABLE message
+ b. Send fail of an ICMP ECHO REQUEST due to a network error (e.g. network is unreachable)
+ c. Send fail of an ICMP ECHO REQUEST due to a permissions error (e.g. lease address is a broadcast address)
+ d. Send fail of an ICMP ECHO REQUEST with socket buffer full error
+
+ In each of these instances the address could not be checked and is treated as
+ available.
+
+.. note::
+
+ Socket buffer full of errors indicates that the OS rate limits on ICMP are
+ being exceeded. The server will not retry them as this would likely only
+ exacerbate the situation. If this occurs continuously then the client load
+ on the server may be too high to accommodate ping checking. Ping checking is
+ not recommended for systems with high throughput demands.
+
+Configuration
+~~~~~~~~~~~~~
+
+The ping-check hook library currently supports the following configuration parameters
+that may be set at the global and subnet levels. Subnet values override global values.
+
+- `enable-ping-check` - Enables or disables ping checking at a given scope.
+
+- `min-ping-requests` - The minimum number of ECHO REQUESTs sent without receiving a reply needed to declare an address available. The default is 1, it must be greater than zero.
+
+- `reply-timeout` - The maximum amount of time to wait for a reply to a single ECHO REQUEST. Specified in milliseconds, it must be greater than zero, it defaults to 100.
+
+- `ping-cltt-secs` - The number of seconds that must elapse after the lease's CLTT before a ping check will be conducted when the client is the lease's previous owner. The default value is sixty seconds.
+
+The following parameter is only supported at the global level:
+
+- `ping-channel-threads` - In multi-threaded mode, this is the number of threads in the channel's thread pool. The default is 0 which instructs the library to use the same number of threads as Kea core. The value is ignored if given when Kea is in single-threaded mode.
+
+The following configuration excerpt illustrates global level configuration:
+
+.. code-block:: javascript
+
+ {
+ "hooks-libraries": [{
+ "library": "lib/kea/hooks/libdhcp_ping_check.so",
+ "parameters": {
+ "enable-ping-check" : true,
+ "min-ping-requests" : 1,
+ "reply-timeout" : 100,
+ "ping-cltt-secs" : 60,
+ "ping-channel-threads" : 0
+ }
+ }]
+ }
+
+The following excerpt demonstrates subnet level configuration:
+
+.. code-block:: javascript
+
+ {
+ "subnet4": [{
+ "subnet": "192.0.2.0/24",
+ "pools": [{
+ "pool": "192.0.2.10 - 192.0.2.20"
+ }],
+
+ "user-context": {
+ "enable-ping-check" : true,
+ "min-ping-requests" : 2,
+ "reply-timeout" : 250,
+ "ping-cltt-secs" : 120
+ }
+ }]
+ }
+
+.. note::
+
+ Ping checking is an experimental feature. It is not currently recommended for
+ production environments.
diff --git a/doc/sphinx/arm/hooks-radius.rst b/doc/sphinx/arm/hooks-radius.rst
new file mode 100644
index 0000000..8f1336d
--- /dev/null
+++ b/doc/sphinx/arm/hooks-radius.rst
@@ -0,0 +1,16 @@
+.. ischooklib:: libdhcp_radius.so
+.. _hooks-radius:
+
+``libdhcp_radius.so``: RADIUS Server Support
+============================================
+
+This hook library allows the Kea DHCP servers to use the RADIUS protocol to
+authorize DHCP clients through the access service or for DHCP lease journaling
+through the accounting service. For details on RADIUS in Kea, please see
+:ref:`radius`.
+
+.. note::
+
+ :ischooklib:`libdhcp_radius.so` is available only to ISC customers with
+ a paid support contract. For more information on subscription options,
+ please complete the form at https://www.isc.org/contact.
diff --git a/doc/sphinx/arm/hooks-rbac.rst b/doc/sphinx/arm/hooks-rbac.rst
new file mode 100644
index 0000000..1df4dd5
--- /dev/null
+++ b/doc/sphinx/arm/hooks-rbac.rst
@@ -0,0 +1,587 @@
+.. ischooklib:: libca_rbac.so
+.. _hooks-RBAC:
+
+``libca_rbac.so``: Role-Based Access Control
+============================================
+
+.. _hooks-RBAC-overview:
+
+Role-Based Access Control (RBAC) Overview
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Before the processing of commands in received HTTP requests, :ischooklib:`libca_rbac.so`
+takes specific parameters, e.g. the common-name part of the client
+certificate subject name, to assign a role to the request.
+The configuration associated with this role is used to accept or reject
+the command. After processing, the response can be rewritten, e.g.
+parts can be removed.
+
+Here is a summary of the steps in processing a request:
+ - The HTTP library records some information to be used later, e.g.
+ the remote address.
+ - When TLS is required but the request was not protected by TLS,
+ the request is rejected by sending an "unauthorized" response.
+ - The command is extracted from the request.
+ - A role is assigned using recorded information in the request.
+ - The role is used to accept (pass through) or reject (send
+ a forbidden response to) the command.
+
+Here is a summary of the steps in processing a response:
+ - The information attached to the request is retrieved during the
+ request processing (when the request was accepted).
+ - Request filters are applied to the response.
+
+.. note::
+
+ :ischooklib:`libca_rbac.so` is available only to ISC customers with
+ a paid support contract. For more information on subscription options,
+ please complete the form at https://www.isc.org/contact.
+
+.. _hooks-RBAC-config:
+
+Role-Based Access Control Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Role Assignment
+---------------
+
+Role assignment is governed by the configured role-assignment method.
+
+.. table:: Role assignment methods
+
+ +----------------------+---------------------------------------------------------+
+ | Name | Description |
+ +----------------------+---------------------------------------------------------+
+ | remote-address | remote/client IP address |
+ +----------------------+---------------------------------------------------------+
+ | cert-subject | common-name part of the client certificate subject name |
+ +----------------------+---------------------------------------------------------+
+ | cert-issuer | common-name part of the client certificate issuer name |
+ +----------------------+---------------------------------------------------------+
+ | basic-authentication | user ID of basic HTTP authentication |
+ +----------------------+---------------------------------------------------------+
+ | custom-value | another role can be designed in external hooks |
+ +----------------------+---------------------------------------------------------+
+
+Role Configuration
+------------------
+
+.. table:: Role configuration parameters
+
+ +------------------+----------------------------------------------------+
+ | Name | Description |
+ +------------------+----------------------------------------------------+
+ | name | the role name (with the exception of the default |
+ | | and unknown roles) |
+ +------------------+----------------------------------------------------+
+ | accept-commands | the accept access list |
+ +------------------+----------------------------------------------------+
+ | reject-commands | the reject access list |
+ +------------------+----------------------------------------------------+
+ | other-commands | specifies what to do for commands not matching |
+ | | accept and reject lists (default: reject) |
+ +------------------+----------------------------------------------------+
+ | list-match-first | specifies what to do for commands matching both |
+ | | the accept and reject list by giving the list to |
+ | | check and apply first (default: accept) |
+ +------------------+----------------------------------------------------+
+ | response-filters | the filters to apply to responses |
+ +------------------+----------------------------------------------------+
+
+.. note::
+
+ The role assignment may fail, for instance with ``cert-subject`` when
+ the client certificate was not required, or it may have no subject common
+ name and instead have a DNS alternative subject name. In this case, the role
+ assignment returns the empty role and the ``default-role`` entry is used.
+
+ The role assignment can return an unexpected value, e.g. with an
+ unregistered role name or a typing error. In this case the ``unknown-role``
+ entry is used.
+
+ The default for both ``default-role`` and ``unknown-role`` is to reject all commands.
+
+API Commands
+------------
+
+All commands of the REST API are described in files in the source directory
+``src/share/api``, or in installed Kea
+in ``.../share/kea/api``. :ischooklib:`libca_rbac.so` reads these files to take the name,
+the access right (i.e. ``read`` or ``write``), and the hook name. The access right
+can be modified in the file but changes are only applied after the Control Agent
+restarts. Removing command definitions from ``.../share/kea/api`` has
+consequences: if the access control list is based on ``read`` or ``write`` and
+the definition file is missing, the Control Agent always rejects such
+a command. If the access controls list is using ``commands`` to specify the
+name of a command and the definition file from ``.../share/kea/api`` of this
+particular command is missing, the Control Agent logs an error on startup
+and exit.
+
+
+.. table:: Extra command-definition parameters
+
+ +--------+---------------------------------------------------------+
+ | Name | Description |
+ +--------+---------------------------------------------------------+
+ | name | (mandatory) the command name |
+ +--------+---------------------------------------------------------+
+ | access | (mandatory) the access right i.e. ``read`` or ``write`` |
+ +--------+---------------------------------------------------------+
+ | hook | (optional) the hook name (empty or not-present for |
+ | | commands of servers or agents) |
+ +--------+---------------------------------------------------------+
+
+.. note::
+
+ These command description files are security-sensitive, e.g. with
+ too-permissive access rights a local attacker may modify them and
+ defeat the RBAC goal.
+
+Access Control Lists
+--------------------
+
+Access control lists can be specified using a name (string) or a
+single entry map.
+
+.. table:: Predefined named access list
+
+ +-------+----------------------------------------------+
+ | Name | Description |
+ +-------+----------------------------------------------+
+ | ALL | matches everything |
+ +-------+----------------------------------------------+
+ | NONE | matches nothing |
+ +-------+----------------------------------------------+
+ | READ | matches commands with the read-access right |
+ +-------+----------------------------------------------+
+ | WRITE | matches commands with the write-access right |
+ +-------+----------------------------------------------+
+
+Map access list specifications use a list type in the name of the single entry
+and parameter in the value.
+
+.. table:: Access list types
+
+ +---------+-----------------+--------------------------------------+
+ | Name | Description | Parameter |
+ +---------+-----------------+--------------------------------------+
+ | not | logical not | access list |
+ +---------+-----------------+--------------------------------------+
+ | and | logical and | list of access lists |
+ +---------+-----------------+--------------------------------------+
+ | or | logical or | list of access lists |
+ +---------+-----------------+--------------------------------------+
+ | command | explicit list | list of command names |
+ +---------+-----------------+--------------------------------------+
+ | access | by access right | access right (``read`` or ``write``) |
+ +---------+-----------------+--------------------------------------+
+ | hook | by hook | hook name (can be empty) |
+ +---------+-----------------+--------------------------------------+
+
+Response Filters
+----------------
+
+.. table:: Predefined response filters
+
+ +---------------+---------------------------------------+
+ | Name | Description |
+ +---------------+---------------------------------------+
+ | list-commands | Removes not-allowed commands from the |
+ | | list-commands response |
+ +---------------+---------------------------------------+
+
+Global Parameters
+-----------------
+
+The global parameters are:
+
+- ``assign-role-method``: the name of the method
+ which is used for role assignment. This parameter is mandatory.
+
+- ``api-files``: the path of the directory where
+ the API files describing commands can be found. This parameter is mandatory.
+
+- ``require-tls``: the specification of whether received requests on HTTP (vs HTTPS) are
+ rejected. It defaults to ``false`` when the role-assignment method is not
+ based on certificates.
+
+- ``commands``: the list of extra command configurations.
+
+- ``access-control-lists``: the named access control list definitions
+ (each definition is a single entry map; the name of the entry is
+ the name of the access list, and the value is the specification).
+ The name is used in other parts of the configuration, such as "accept-commands".
+
+- ``roles``: the role configurations.
+
+- ``default-role``: the configuration of the default role (used
+ when "" is assigned).
+
+- ``unknown-role``: the configuration of the unknown role
+ (used when the not-empty assigned role has no configuration).
+
+Sample Configuration
+~~~~~~~~~~~~~~~~~~~~
+
+A sample configuration is available in ``doc/examples/agent/rbac.json``
+in the Kea source and is copied below.
+
+.. code-block:: javascript
+ :linenos:
+ :emphasize-lines: 31-85
+
+ {
+ "Control-agent": {
+ // We need to specify where the agent should listen to incoming HTTP
+ // queries.
+ "http-host": "127.0.0.1",
+
+ // If enabling HA and multi-threading, the 8000 port is used by the HA
+ // hook library http listener. When using HA hook library with
+ // multi-threading to function, make sure the port used by dedicated
+ // listener is different (e.g. 8001) than the one used by CA. Note
+ // the commands should still be sent via CA. The dedicated listener
+ // is specifically for HA updates only.
+ "http-port": 8000,
+
+ // TLS trust anchor (Certificate Authority). This is a file name or
+ // (for OpenSSL only) a directory path.
+ "trust-anchor": "my-ca",
+
+ // TLS server certificate file name.
+ "cert-file": "my-cert",
+
+ // TLS server private key file name.
+ "key-file": "my-key",
+
+ // TLS require client certificates flag. Default is true and means
+ // require client certificates. False means they are optional.
+ "cert-required": true,
+
+ // Add hooks here.
+ "hooks-libraries": [
+ {
+ "library": "/opt/lib/libca_rbac.so",
+ "parameters": {
+ // This section configures the RBAC hook library.
+ // Mandatory parameters.
+ "assign-role-method": "cert-subject",
+ "api-files": "/opt/share/kea/api",
+ // Optional parameters.
+ "require-tls": true,
+ "commands": [
+ {
+ "name": "my-command",
+ "access": "read",
+ "hook": "my-hook"
+ } ],
+ "access-control-lists": [
+ {
+ "my-none": { "not": "ALL" }
+ },{
+ "another-none": { "and": [ "ALL", "NONE" ] }
+ },{
+ "my-read": { "access": "read" }
+ } ],
+ "roles": [
+ {
+ "name": "kea-client",
+ "accept-commands":
+ {
+ "commands": [ "list-commands", "status-get" ]
+ },
+ "reject-commands": "NONE",
+ "other-commands": "reject",
+ "list-match-first": "accept",
+ "response-filters": [ "list-commands" ]
+ },{
+ "name": "admin",
+ "accept-commands": "ALL",
+ "reject-commands":
+ {
+ "hook": "cb_cmds"
+ },
+ "list-match-first": "reject"
+ } ],
+ "default-role":
+ {
+ "accept-commands": "NONE",
+ "reject-commands": "ALL"
+ },
+ "unknown-role":
+ {
+ "accept-commands": "READ",
+ "reject-commands": "WRITE"
+ }
+ }
+ } ]
+
+ // Additional parameters, such as logging and others
+ // omitted for clarity.
+
+ }
+ }
+
+Accept/Reject Algorithm
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This is the pseudo-code of the accept/reject decision algorithm which returns
+``true`` (accept) or ``false`` (reject).
+
+.. code-block:: c
+
+ bool match(command) {
+ if (list-match-first == accept) {
+ if (accept_list && accept_list->match(command)) {
+ return (true);
+ }
+ if (reject_list && reject_list->match(command)) {
+ return (false);
+ }
+ } else {
+ if (reject_list && reject_list->match(command)) {
+ return (false);
+ }
+ if (accept_list && accept_list->match(command)) {
+ return (true);
+ }
+ }
+ if (others == reject) {
+ return (false);
+ } else {
+ return (true);
+ }
+ }
+
+Custom Hook Commands and Command Redefinition
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+It is possible to have a custom hook with new commands. In this case,
+Role Based Access Control can be used to manage a new command in two ways.
+
+The ``command`` global parameter can be used to define its name, access type,
+and hook name:
+
+.. code-block:: javascript
+
+ {
+ "commands": [
+ {
+ "name": "my-new-command",
+ "access": "write",
+ "hook": "my-custom-hook"
+ }
+ ],
+ ...
+ }
+
+The new command can then be specified in ``roles``:
+
+.. code-block:: javascript
+
+ {
+ "roles": [
+ {
+ "name": "user1",
+ "accept-commands": {
+ "commands": [ "my-new-command" ] },
+ "reject-commands": "WRITE",
+ "list-match-first": "accept"
+ },
+ {
+ "name": "user2",
+ "accept-commands": { "hook": "my-custom-hook" },
+ "reject-commands": "ALL",
+ "list-match-first": "accept"
+ }
+ ],
+ ...
+ }
+
+The second method is to create a custom file in ``.../share/kea/api`` and define
+the access type of the custom command(s).
+
+It is also possible to redefine an existing command by removing its definition
+file from ``.../share/kea/api`` and defining it in the ``commands`` global parameter:
+
+.. code-block:: javascript
+
+ {
+ "commands": [
+ {
+ "name": "dhcp-disable",
+ "access": "read",
+ "hook": "my-custom-hook-3"
+ }
+ ]
+ }
+
+With this approach, an administrator can put the configurations of all existing
+commands inside the Control Agent's configuration file.
+
+Extensive Example
+~~~~~~~~~~~~~~~~~
+
+Here is an extensive example for a role accepting all read commands, with
+the exception of :isccmd:`config-get`, e.g. for hiding passwords. For any remote
+user who is not recognized as "user1", all commands should be rejected.
+
+The first option is to put the allowed commands in the "accept-commands"
+list and to reject anything else:
+
+.. code-block:: javascript
+
+ {
+ "roles": [
+ {
+ "name": "user1",
+ "accept-commands":
+ {
+ "and": [
+ "READ",
+ { "not":
+ { "commands": [ "config-get" ] }
+ }
+ ]
+ },
+ "reject-commands": "ALL",
+ // This is the default but as the config relies on it
+ // it is explicitly set.
+ "list-match-first": "accept"
+ },
+ ...
+ ],
+ ...
+ }
+
+A common alternative is not to set the "reject-commands" list, i.e. leave
+it empty and rely on "other-commands" to reject anything else.
+
+.. code-block:: javascript
+
+ {
+ "roles": [
+ {
+ "name": "user2",
+ "accept-commands":
+ {
+ "and": [
+ "READ",
+ { "not":
+ { "commands": [ "config-get" ] }
+ }
+ ]
+ },
+ // This is the default but as the config relies on it
+ // it is explicitly set.
+ "other-commands": "reject"
+ },
+ ...
+ ],
+ ...
+ }
+
+It is also possible to do the opposite, i.e. to set only the "reject-commands" list:
+
+.. code-block:: javascript
+
+ {
+ "roles": [
+ {
+ "name": "user3",
+ "reject-commands":
+ {
+ "or": [
+ "WRITE",
+ { "commands": [ "config-get" ] }
+ ]
+ },
+ "other-commands": "accept"
+ },
+ ...
+ ],
+ ...
+ }
+
+Or use both lists with the exception in the "reject-commands" list,
+which must be checked first as "config-get" has the read-access right.
+
+.. code-block:: javascript
+
+ {
+ "roles": [
+ {
+ "name": "user4",
+ "accept-commands": "READ",
+ "reject-commands": { "commands": [ "config-get" ] },
+ "list-match-first": "reject"
+ },
+ ...
+ ],
+ ...
+ }
+
+To check any configuration, it is a good idea to use the "list-commands"
+response filter, which shows errors such as missing (rejected) commands
+and extra (accepted) commands.
+
+``access-control-lists`` can be used for definitions of access control lists
+and later reused in ``roles``:
+
+ .. code-block:: javascript
+
+ {
+ "access-control-lists":[
+ {
+ "my-list-one":{
+ "or":[
+ {
+ "hook": "subnet_cmds"
+ },
+ {
+ "commands":[ "list-commands" ]
+ }
+ ]
+ }
+ },
+ {
+ "my-list-two":{
+ "and":[
+ "READ",
+ {
+ "not":{
+ "commands":[ "config-get" ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "my-list-three":{
+ "or":[
+ { "hook":"subnet_cmds" },
+ { "hook":"class_cmds" },
+ { "hook":"lease_cmds" }
+ ]
+ }
+ }
+ ],
+ "roles":[
+ {
+ "name":"admin",
+ "accept-commands":"my-list-one",
+ "reject-commands":"ALL",
+ "list-match-first":"accept"
+ },
+ {
+ "name":"admin2",
+ "accept-commands":"my-list-two",
+ "reject-commands":"ALL",
+ "list-match-first":"accept"
+ }
+ ],
+ "unknown-role":{
+ "accept-commands":"my-list-three",
+ "reject-commands":"ALL"
+ },
+ ...
+ }
diff --git a/doc/sphinx/arm/hooks-run-script.rst b/doc/sphinx/arm/hooks-run-script.rst
new file mode 100644
index 0000000..3d11724
--- /dev/null
+++ b/doc/sphinx/arm/hooks-run-script.rst
@@ -0,0 +1,626 @@
+.. ischooklib:: libdhcp_run_script.so
+.. _hooks-run-script:
+
+``libdhcp_run_script.so``: Run Script Support for External Hook Scripts
+=======================================================================
+
+The Run Script hook library adds support for calling an external script for specific
+packet-processing hook points.
+
+.. note::
+
+ :ischooklib:`libdhcp_run_script.so` is part of the open source code and is
+ available to every Kea user.
+
+The library, which was added in Kea 1.9.5, can be loaded in a
+similar way to other hook libraries by the :iscman:`kea-dhcp4` and
+:iscman:`kea-dhcp6` processes.
+
+.. code-block:: json
+
+ {
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/libdhcp_run_script.so",
+ "parameters": {
+ "name": "/full_path_to/script_name.sh",
+ "sync": false
+ }
+ }
+ ]
+ }
+
+The parameters contain the ``name``, which indicates the full path to the external
+script to be called on each hook point, and also the ``sync`` option, to be able
+to wait synchronously for the script to finish execution.
+If the ``sync`` parameter is ``false``, then the script will launch and Kea
+will not wait for the execution to finish, causing all the OUT parameters of
+the script (including the next step) to be ignored.
+
+.. note::
+
+ The script inherits all privileges from the server which calls it.
+
+.. note::
+
+ Currently, enabling synchronous calls to external scripts is not supported.
+
+.. _hooks-run-script-hook-points:
+
+This library has several hook-point functions implemented, which are
+called at the specific packet-processing stage.
+
+The dhcpv4 hook points:
+
+::
+
+ lease4_renew
+ lease4_expire
+ lease4_recover
+ leases4_committed
+ lease4_release
+ lease4_decline
+
+
+The dhcpv6 hook points:
+
+::
+
+ lease6_renew
+ lease6_rebind
+ lease6_expire
+ lease6_recover
+ leases6_committed
+ lease6_release
+ lease6_decline
+
+Each hook point extracts the Kea internal data and exports it as string
+environment variables. These parameters are shared with the target script
+using the child process environment.
+The only parameter passed to the call of the target script is the name of
+the hook point.
+
+An example of a script implementing all hook points is presented below:
+
+::
+
+ #!/bin/bash
+
+ unknown_handle() {
+ echo "Unhandled function call ${*}"
+ exit 123
+ }
+
+
+ lease4_renew () {
+ ...
+ }
+
+ lease4_expire () {
+ ...
+ }
+
+ lease4_recover () {
+ ...
+ }
+
+ leases4_committed () {
+ ...
+ }
+
+ lease4_release () {
+ ...
+ }
+
+ lease4_decline () {
+ ...
+ }
+
+ lease6_renew () {
+ ...
+ }
+
+ lease6_rebind () {
+ ...
+ }
+
+ lease6_expire () {
+ ...
+ }
+
+ lease6_recover () {
+ ...
+ }
+
+ leases6_committed () {
+ ...
+ }
+
+ lease6_release () {
+ ...
+ }
+
+ lease6_decline () {
+ ...
+ }
+
+ case "$1" in
+ "lease4_renew")
+ lease4_renew
+ ;;
+ "lease4_expire")
+ lease4_expire
+ ;;
+ "lease4_recover")
+ lease4_recover
+ ;;
+ "leases4_committed")
+ leases4_committed
+ ;;
+ "lease4_release")
+ lease4_release
+ ;;
+ "lease4_decline")
+ lease4_decline
+ ;;
+ "lease6_renew")
+ lease6_renew
+ ;;
+ "lease6_rebind")
+ lease6_rebind
+ ;;
+ "lease6_expire")
+ lease6_expire
+ ;;
+ "lease6_recover")
+ lease6_recover
+ ;;
+ "leases6_committed")
+ leases6_committed
+ ;;
+ "lease6_release")
+ lease6_release
+ ;;
+ "lease6_decline")
+ lease6_decline
+ ;;
+ *)
+ unknown_handle "${@}"
+ ;;
+ esac
+
+
+.. _hooks-run-script-exported-environment-variables:
+
+Available parameters for each hook point are presented below.
+
+DHCPv4:
+
+``lease4_renew``
+
+::
+
+ QUERY4_TYPE
+ QUERY4_TXID
+ QUERY4_LOCAL_ADDR
+ QUERY4_LOCAL_PORT
+ QUERY4_REMOTE_ADDR
+ QUERY4_REMOTE_PORT
+ QUERY4_IFACE_INDEX
+ QUERY4_IFACE_NAME
+ QUERY4_HOPS
+ QUERY4_SECS
+ QUERY4_FLAGS
+ QUERY4_CIADDR
+ QUERY4_SIADDR
+ QUERY4_YIADDR
+ QUERY4_GIADDR
+ QUERY4_RELAYED
+ QUERY4_HWADDR
+ QUERY4_HWADDR_TYPE
+ QUERY4_LOCAL_HWADDR
+ QUERY4_LOCAL_HWADDR_TYPE
+ QUERY4_REMOTE_HWADDR
+ QUERY4_REMOTE_HWADDR_TYPE
+ QUERY4_OPTION_82
+ QUERY4_OPTION_82_SUB_OPTION_1
+ QUERY4_OPTION_82_SUB_OPTION_2
+ SUBNET4_ID
+ SUBNET4_NAME
+ SUBNET4_PREFIX
+ SUBNET4_PREFIX_LEN
+ PKT4_CLIENT_ID
+ PKT4_HWADDR
+ PKT4_HWADDR_TYPE
+ LEASE4_ADDRESS
+ LEASE4_CLTT
+ LEASE4_HOSTNAME
+ LEASE4_HWADDR
+ LEASE4_HWADDR_TYPE
+ LEASE4_STATE
+ LEASE4_SUBNET_ID
+ LEASE4_VALID_LIFETIME
+ LEASE4_CLIENT_ID
+
+``lease4_expire``
+
+::
+
+ LEASE4_ADDRESS
+ LEASE4_CLTT
+ LEASE4_HOSTNAME
+ LEASE4_HWADDR
+ LEASE4_HWADDR_TYPE
+ LEASE4_STATE
+ LEASE4_SUBNET_ID
+ LEASE4_VALID_LIFETIME
+ LEASE4_CLIENT_ID
+ REMOVE_LEASE
+
+``lease4_recover``
+
+::
+
+ LEASE4_ADDRESS
+ LEASE4_CLTT
+ LEASE4_HOSTNAME
+ LEASE4_HWADDR
+ LEASE4_HWADDR_TYPE
+ LEASE4_STATE
+ LEASE4_SUBNET_ID
+ LEASE4_VALID_LIFETIME
+ LEASE4_CLIENT_ID
+
+``leases4_committed``
+
+::
+
+ QUERY4_TYPE
+ QUERY4_TXID
+ QUERY4_LOCAL_ADDR
+ QUERY4_LOCAL_PORT
+ QUERY4_REMOTE_ADDR
+ QUERY4_REMOTE_PORT
+ QUERY4_IFACE_INDEX
+ QUERY4_IFACE_NAME
+ QUERY4_HOPS
+ QUERY4_SECS
+ QUERY4_FLAGS
+ QUERY4_CIADDR
+ QUERY4_SIADDR
+ QUERY4_YIADDR
+ QUERY4_GIADDR
+ QUERY4_RELAYED
+ QUERY4_HWADDR
+ QUERY4_HWADDR_TYPE
+ QUERY4_LOCAL_HWADDR
+ QUERY4_LOCAL_HWADDR_TYPE
+ QUERY4_REMOTE_HWADDR
+ QUERY4_REMOTE_HWADDR_TYPE
+ QUERY4_OPTION_82
+ QUERY4_OPTION_82_SUB_OPTION_1
+ QUERY4_OPTION_82_SUB_OPTION_2
+ LEASES4_SIZE
+ DELETED_LEASES4_SIZE
+
+If ``LEASES4_SIZE`` or ``DELETED_LEASES4_SIZE`` is non-zero, then each lease
+has its own unique identifier, as shown below. The first index starts
+at 0.
+
+::
+
+ LEASES4_AT0_ADDRESS
+ LEASES4_AT0_CLTT
+ LEASES4_AT0_HOSTNAME
+ LEASES4_AT0_HWADDR
+ LEASES4_AT0_HWADDR_TYPE
+ LEASES4_AT0_STATE
+ LEASES4_AT0_SUBNET_ID
+ LEASES4_AT0_VALID_LIFETIME
+ LEASES4_AT0_CLIENT_ID
+ DELETED_LEASES4_AT0_ADDRESS
+ DELETED_LEASES4_AT0_CLTT
+ DELETED_LEASES4_AT0_HOSTNAME
+ DELETED_LEASES4_AT0_HWADDR
+ DELETED_LEASES4_AT0_HWADDR_TYPE
+ DELETED_LEASES4_AT0_STATE
+ DELETED_LEASES4_AT0_SUBNET_ID
+ DELETED_LEASES4_AT0_VALID_LIFETIME
+ DELETED_LEASES4_AT0_CLIENT_ID
+
+``lease4_release``
+
+::
+
+ QUERY4_TYPE
+ QUERY4_TXID
+ QUERY4_LOCAL_ADDR
+ QUERY4_LOCAL_PORT
+ QUERY4_REMOTE_ADDR
+ QUERY4_REMOTE_PORT
+ QUERY4_IFACE_INDEX
+ QUERY4_IFACE_NAME
+ QUERY4_HOPS
+ QUERY4_SECS
+ QUERY4_FLAGS
+ QUERY4_CIADDR
+ QUERY4_SIADDR
+ QUERY4_YIADDR
+ QUERY4_GIADDR
+ QUERY4_RELAYED
+ QUERY4_HWADDR
+ QUERY4_HWADDR_TYPE
+ QUERY4_LOCAL_HWADDR
+ QUERY4_LOCAL_HWADDR_TYPE
+ QUERY4_REMOTE_HWADDR
+ QUERY4_REMOTE_HWADDR_TYPE
+ QUERY4_OPTION_82
+ QUERY4_OPTION_82_SUB_OPTION_1
+ QUERY4_OPTION_82_SUB_OPTION_2
+ LEASE4_ADDRESS
+ LEASE4_CLTT
+ LEASE4_HOSTNAME
+ LEASE4_HWADDR
+ LEASE4_HWADDR_TYPE
+ LEASE4_STATE
+ LEASE4_SUBNET_ID
+ LEASE4_VALID_LIFETIME
+ LEASE4_CLIENT_ID
+
+``lease4_decline``
+
+::
+
+ QUERY4_TYPE
+ QUERY4_TXID
+ QUERY4_LOCAL_ADDR
+ QUERY4_LOCAL_PORT
+ QUERY4_REMOTE_ADDR
+ QUERY4_REMOTE_PORT
+ QUERY4_IFACE_INDEX
+ QUERY4_IFACE_NAME
+ QUERY4_HOPS
+ QUERY4_SECS
+ QUERY4_FLAGS
+ QUERY4_CIADDR
+ QUERY4_SIADDR
+ QUERY4_YIADDR
+ QUERY4_GIADDR
+ QUERY4_RELAYED
+ QUERY4_HWADDR
+ QUERY4_HWADDR_TYPE
+ QUERY4_LOCAL_HWADDR
+ QUERY4_LOCAL_HWADDR_TYPE
+ QUERY4_REMOTE_HWADDR
+ QUERY4_REMOTE_HWADDR_TYPE
+ QUERY4_OPTION_82
+ QUERY4_OPTION_82_SUB_OPTION_1
+ QUERY4_OPTION_82_SUB_OPTION_2
+ LEASE4_ADDRESS
+ LEASE4_CLTT
+ LEASE4_HOSTNAME
+ LEASE4_HWADDR
+ LEASE4_HWADDR_TYPE
+ LEASE4_STATE
+ LEASE4_SUBNET_ID
+ LEASE4_VALID_LIFETIME
+ LEASE4_CLIENT_ID
+
+DHCPv6:
+
+``lease6_renew``
+
+::
+
+ QUERY6_TYPE
+ QUERY6_TXID
+ QUERY6_LOCAL_ADDR
+ QUERY6_LOCAL_PORT
+ QUERY6_REMOTE_ADDR
+ QUERY6_REMOTE_PORT
+ QUERY6_IFACE_INDEX
+ QUERY6_IFACE_NAME
+ QUERY6_REMOTE_HWADDR
+ QUERY6_REMOTE_HWADDR_TYPE
+ QUERY6_PROTO
+ QUERY6_CLIENT_ID
+ LEASE6_ADDRESS
+ LEASE6_CLTT
+ LEASE6_HOSTNAME
+ LEASE6_HWADDR
+ LEASE6_HWADDR_TYPE
+ LEASE6_STATE
+ LEASE6_SUBNET_ID
+ LEASE6_VALID_LIFETIME
+ LEASE6_DUID
+ LEASE6_IAID
+ LEASE6_PREFERRED_LIFETIME
+ LEASE6_PREFIX_LEN
+ LEASE6_TYPE
+ PKT6_IA_IAID
+ PKT6_IA_IA_TYPE
+ PKT6_IA_IA_T1
+ PKT6_IA_IA_T2
+
+``lease6_rebind``
+
+::
+
+ QUERY6_TYPE
+ QUERY6_TXID
+ QUERY6_LOCAL_ADDR
+ QUERY6_LOCAL_PORT
+ QUERY6_REMOTE_ADDR
+ QUERY6_REMOTE_PORT
+ QUERY6_IFACE_INDEX
+ QUERY6_IFACE_NAME
+ QUERY6_REMOTE_HWADDR
+ QUERY6_REMOTE_HWADDR_TYPE
+ QUERY6_PROTO
+ QUERY6_CLIENT_ID
+ LEASE6_ADDRESS
+ LEASE6_CLTT
+ LEASE6_HOSTNAME
+ LEASE6_HWADDR
+ LEASE6_HWADDR_TYPE
+ LEASE6_STATE
+ LEASE6_SUBNET_ID
+ LEASE6_VALID_LIFETIME
+ LEASE6_DUID
+ LEASE6_IAID
+ LEASE6_PREFERRED_LIFETIME
+ LEASE6_PREFIX_LEN
+ LEASE6_TYPE
+ PKT6_IA_IAID
+ PKT6_IA_IA_TYPE
+ PKT6_IA_IA_T1
+ PKT6_IA_IA_T2
+
+``lease6_expire``
+
+::
+
+ LEASE6_ADDRESS
+ LEASE6_CLTT
+ LEASE6_HOSTNAME
+ LEASE6_HWADDR
+ LEASE6_HWADDR_TYPE
+ LEASE6_STATE
+ LEASE6_SUBNET_ID
+ LEASE6_VALID_LIFETIME
+ LEASE6_DUID
+ LEASE6_IAID
+ LEASE6_PREFERRED_LIFETIME
+ LEASE6_PREFIX_LEN
+ LEASE6_TYPE
+ REMOVE_LEASE
+
+``lease6_recover``
+
+::
+
+ LEASE6_ADDRESS
+ LEASE6_CLTT
+ LEASE6_HOSTNAME
+ LEASE6_HWADDR
+ LEASE6_HWADDR_TYPE
+ LEASE6_STATE
+ LEASE6_SUBNET_ID
+ LEASE6_VALID_LIFETIME
+ LEASE6_DUID
+ LEASE6_IAID
+ LEASE6_PREFERRED_LIFETIME
+ LEASE6_PREFIX_LEN
+ LEASE6_TYPE
+
+``leases6_committed``
+
+::
+
+ QUERY6_TYPE
+ QUERY6_TXID
+ QUERY6_LOCAL_ADDR
+ QUERY6_LOCAL_PORT
+ QUERY6_REMOTE_ADDR
+ QUERY6_REMOTE_PORT
+ QUERY6_IFACE_INDEX
+ QUERY6_IFACE_NAME
+ QUERY6_REMOTE_HWADDR
+ QUERY6_REMOTE_HWADDR_TYPE
+ QUERY6_PROTO
+ QUERY6_CLIENT_ID
+ LEASES6_SIZE
+ DELETED_LEASES6_SIZE
+
+If ``LEASES6_SIZE`` or ``DELETED_LEASES6_SIZE`` is non-zero, then each lease
+has its own unique identifier, as shown below. The first index starts
+at 0.
+
+::
+
+ LEASES6_AT0_ADDRESS
+ LEASES6_AT0_CLTT
+ LEASES6_AT0_HOSTNAME
+ LEASES6_AT0_HWADDR
+ LEASES6_AT0_HWADDR_TYPE
+ LEASES6_AT0_STATE
+ LEASES6_AT0_SUBNET_ID
+ LEASES6_AT0_VALID_LIFETIME
+ LEASES6_AT0_DUID
+ LEASES6_AT0_IAID
+ LEASES6_AT0_PREFERRED_LIFETIME
+ LEASES6_AT0_PREFIX_LEN
+ LEASES6_AT0_TYPE
+ DELETED_LEASES6_AT0_ADDRESS
+ DELETED_LEASES6_AT0_CLTT
+ DELETED_LEASES6_AT0_HOSTNAME
+ DELETED_LEASES6_AT0_HWADDR
+ DELETED_LEASES6_AT0_HWADDR_TYPE
+ DELETED_LEASES6_AT0_STATE
+ DELETED_LEASES6_AT0_SUBNET_ID
+ DELETED_LEASES6_AT0_VALID_LIFETIME
+ DELETED_LEASES6_AT0_DUID
+ DELETED_LEASES6_AT0_IAID
+ DELETED_LEASES6_AT0_PREFERRED_LIFETIME
+ DELETED_LEASES6_AT0_PREFIX_LEN
+ DELETED_LEASES6_AT0_TYPE
+
+``lease6_release``
+
+::
+
+ QUERY6_TYPE
+ QUERY6_TXID
+ QUERY6_LOCAL_ADDR
+ QUERY6_LOCAL_PORT
+ QUERY6_REMOTE_ADDR
+ QUERY6_REMOTE_PORT
+ QUERY6_IFACE_INDEX
+ QUERY6_IFACE_NAME
+ QUERY6_REMOTE_HWADDR
+ QUERY6_REMOTE_HWADDR_TYPE
+ QUERY6_PROTO
+ QUERY6_CLIENT_ID
+ LEASE6_ADDRESS
+ LEASE6_CLTT
+ LEASE6_HOSTNAME
+ LEASE6_HWADDR
+ LEASE6_HWADDR_TYPE
+ LEASE6_STATE
+ LEASE6_SUBNET_ID
+ LEASE6_VALID_LIFETIME
+ LEASE6_DUID
+ LEASE6_IAID
+ LEASE6_PREFERRED_LIFETIME
+ LEASE6_PREFIX_LEN
+ LEASE6_TYPE
+
+``lease6_decline``
+
+::
+
+ QUERY6_TYPE
+ QUERY6_TXID
+ QUERY6_LOCAL_ADDR
+ QUERY6_LOCAL_PORT
+ QUERY6_REMOTE_ADDR
+ QUERY6_REMOTE_PORT
+ QUERY6_IFACE_INDEX
+ QUERY6_IFACE_NAME
+ QUERY6_REMOTE_HWADDR
+ QUERY6_REMOTE_HWADDR_TYPE
+ QUERY6_PROTO
+ QUERY6_CLIENT_ID
+ LEASE6_ADDRESS
+ LEASE6_CLTT
+ LEASE6_HOSTNAME
+ LEASE6_HWADDR
+ LEASE6_HWADDR_TYPE
+ LEASE6_STATE
+ LEASE6_SUBNET_ID
+ LEASE6_VALID_LIFETIME
+ LEASE6_DUID
+ LEASE6_IAID
+ LEASE6_PREFERRED_LIFETIME
+ LEASE6_PREFIX_LEN
+ LEASE6_TYPE
diff --git a/doc/sphinx/arm/hooks-stat-cmds.rst b/doc/sphinx/arm/hooks-stat-cmds.rst
new file mode 100644
index 0000000..fce8cfa
--- /dev/null
+++ b/doc/sphinx/arm/hooks-stat-cmds.rst
@@ -0,0 +1,252 @@
+.. ischooklib:: libdhcp_stat_cmds.so
+.. _hooks-stat-cmds:
+
+``libdhcp_stat_cmds.so``: Statistics Commands for Supplemental Lease Statistics
+===============================================================================
+
+This library provides additional commands for retrieving lease
+statistics from Kea DHCP servers. These commands were added to address
+an issue with obtaining accurate lease statistics in deployments running
+multiple Kea servers that use a shared lease backend. The in-memory
+statistics kept by individual servers only track lease changes made by
+that server; thus, in a deployment with multiple servers (e.g. two
+:iscman:`kea-dhcp6` servers using the same PostgreSQL database for lease storage),
+these statistics are incomplete. The MySQL and PostgreSQL backends in
+Kea track lease allocation changes as they occur via database triggers.
+Additionally, all the lease backends were extended to support
+retrieving lease statistics for a single subnet, a range
+of subnets, or all subnets. Finally, this library provides commands
+for retrieving these statistics.
+
+.. note::
+
+ :ischooklib:`libdhcp_stat_cmds.so` is part of the open source code and is
+ available to every Kea user.
+
+.. note::
+
+ This library can only be loaded by the :iscman:`kea-dhcp4` or
+ :iscman:`kea-dhcp6` process.
+
+The commands provided by this library are:
+
+- :isccmd:`stat-lease4-get` - fetches DHCPv4 lease statistics.
+
+- :isccmd:`stat-lease6-get` - fetches DHCPv6 lease statistics.
+
+The Statistics Commands library is part of the open source code and is
+available to every Kea user.
+
+All commands use JSON syntax and can be issued directly to the servers
+via either the control channel (see :ref:`ctrl-channel`) or the
+Control Agent (see :ref:`kea-ctrl-agent`).
+
+This library is loaded in the same way as other libraries and currently has no
+parameters:
+
+::
+
+ "Dhcp6": {
+ "hooks-libraries": [
+ {
+ "library": "/path/libdhcp_stat_cmds.so"
+ },
+ ...
+ ]
+ }
+
+In a deployment with multiple Kea DHCP servers sharing a common lease
+storage, this hook library can be loaded by any or all of the servers. However,
+a server's response to a :isccmd:`stat-lease4-get` / :isccmd:`stat-lease6-get`
+command will only contain data for subnets known to
+that server. In other words, if a subnet does not appear in a server's
+configuration, Kea will not retrieve statistics for it.
+
+.. isccmd:: stat-lease4-get
+.. _command-stat-lease4-get:
+
+.. isccmd:: stat-lease6-get
+.. _command-stat-lease6-get:
+
+The ``stat-lease4-get``, ``stat-lease6-get`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :isccmd:`stat-lease4-get` and
+:isccmd:`stat-lease6-get` commands fetch lease
+statistics for a range of known subnets. The range of subnets is
+determined through the use of optional command input parameters:
+
+- ``subnet-id`` - the ID of the subnet for which lease statistics
+ should be fetched; used to get statistics for a single subnet. If
+ the subnet does not exist, the command result code is 3 (i.e.
+ ``CONTROL_RESULT_EMPTY``).
+
+- ``subnet-range`` - a pair of subnet IDs which describe an inclusive
+ range of subnets for which statistics should be retrieved. The range
+ may include one or more IDs that correspond to no subnet; in this
+ case, the command only outputs lease statistics for those that
+ exist. However, if the range does not include any known subnets, the
+ command result code is 3 (i.e. ``CONTROL_RESULT_EMPTY``).
+
+ - ``first-subnet-id`` - the ID of the first subnet in the range.
+
+ - ``last-subnet-id`` - the ID of the last subnet in the range.
+
+The use of ``subnet-id`` and ``subnet-range`` are mutually exclusive. If no
+parameters are given, the result will contain data for all known
+subnets. Note that in configurations with many subnets, this
+can result in a large response.
+
+The following command fetches lease statistics for all known subnets
+from a :iscman:`kea-dhcp4` server:
+
+::
+
+ {
+ "command": "stat-lease4-get"
+ }
+
+The following command fetches lease statistics for subnet ID 10 from a
+:iscman:`kea-dhcp6` server:
+
+::
+
+ {
+ "command": "stat-lease6-get",
+ "arguments": {
+ "subnet-id" : 10
+ }
+ }
+
+The following command fetches lease statistics for all subnets with IDs
+in the range 10 through 50 from a :iscman:`kea-dhcp4` server:
+
+.. code-block:: json
+
+ {
+ "command": "stat-lease4-get",
+ "arguments": {
+ "subnet-range": {
+ "first-subnet-id": 10,
+ "last-subnet-id": 50
+ }
+ }
+ }
+
+The response to either command will contain three elements:
+
+- ``result`` - a numeric value indicating the outcome of the command
+ where:
+
+ - ``0`` - the command was successful;
+
+ - ``1`` - an error occurred, and an explanation is the "text"
+ element; or
+
+ - ``2`` - the fetch found no matching data.
+
+- ``text`` - an explanation of the command outcome. When the command
+ succeeds, it contains the command name along with the number of
+ rows returned.
+
+- ``arguments`` - a map containing the data returned by the command as
+ the element "result-set", which is patterned after SQL statement
+ responses:
+
+ - ``columns`` - a list of text column labels.
+
+ The columns returned for DHCPv4 are:
+
+ - ``subnet-id`` - the ID of the subnet.
+
+ - ``total-addresses`` - the total number of addresses available for
+ DHCPv4 management in the subnet. In other words, this is the
+ count of all addresses in all the configured pools in the subnet.
+
+ - ``cumulative-assigned-addresses`` - the cumulative number of addresses
+ in the subnet that have been assigned to a client by the server
+ since it started.
+
+ - ``assigned-addresses`` - the number of addresses in the subnet that
+ are currently assigned to a client.
+
+ - ``declined-addresses`` - the number of addresses in the subnet that
+ are currently declined and are thus unavailable for assignment.
+
+ The columns returned for DHCPv6 are:
+
+ - ``subnet-id`` - the ID of the subnet.
+
+ - ``total-nas`` - the number of NA addresses available for DHCPv6
+ management in the subnet. In other words, this is the count of
+ all the NA addresses in all the configured NA pools in the
+ subnet.
+
+ - ``cumulative-assigned-nas`` - the cumulative number of NA addresses
+ in the subnet that have been assigned to a client by the server
+ since it started.
+
+ - ``assigned-nas`` - the number of NA addresses in the subnet that
+ are currently assigned to a client.
+
+ - ``declined-addresses`` - the number of NA addresses that are currently
+ declined and are thus unavailable for assignment.
+
+ - ``total-pds`` - the total number of PD prefixes available of DHCPv6
+ management in the subnet. In other words, this is the count of
+ all prefixes in all the configured prefix pools in the subnet.
+
+ - ``cumulative-assigned-pds`` - the cumulative number of PD prefixes
+ in the subnet that have been assigned to a client by the server
+ since it started.
+
+ - ``assigned-pds`` - the number of PD prefixes in the subnet that are
+ currently assigned to a client.
+
+ - ``rows`` - a list of rows, one per subnet ID. Each row contains a
+ data value corresponding to and in the same order as each column
+ listed in "columns" for a given subnet.
+
+ - ``timestamp`` - the textual date and time the data were fetched,
+ expressed as GMT.
+
+The response to a DHCPv4 command might look as follows:
+
+::
+
+ {
+ "result": 0,
+ "text": "stat-lease4-get: 2 rows found",
+ "arguments": {
+ "result-set": {
+ "columns": [ "subnet-id", "total-addresses", "cumulative-assigned-addresses", "assigned-addresses", "declined-addresses" ],
+ "rows": [
+ [ 10, 256, 300, 111, 0 ],
+ [ 20, 4098, 2034, 2034, 4 ]
+ ],
+ "timestamp": "2018-05-04 15:03:37.000000"
+ }
+ }
+ }
+
+The response to a DHCPv6 command might look as follows, assuming subnet 10 has no
+prefix pools, subnet 20 has no NA pools, and subnet 30 has both NA and
+PD pools:
+
+::
+
+ {
+ "result": 0,
+ "text": "stat-lease6-get: 2 rows found",
+ "arguments": {
+ "result-set": {
+ "columns": [ "subnet-id", "total-nas", "cumulative-assigned-nas", "assigned-nas", "declined-addresses", "total-pds", "cumulative-assigned-pds", "assigned-pds" ],
+ "rows": [
+ [ 10, 4096, 5000, 2400, 3, 0, 0, 0],
+ [ 20, 0, 0, 0, 0, 1048, 300, 233 ],
+ [ 30, 256, 60, 60, 0, 1048, 15, 15 ]
+ ],
+ "timestamp": "2018-05-04 15:03:37.000000"
+ }
+ }
+ }
diff --git a/doc/sphinx/arm/hooks-subnet-cmds.rst b/doc/sphinx/arm/hooks-subnet-cmds.rst
new file mode 100644
index 0000000..63cedfe
--- /dev/null
+++ b/doc/sphinx/arm/hooks-subnet-cmds.rst
@@ -0,0 +1,1326 @@
+.. ischooklib:: libdhcp_subnet_cmds.so
+.. _hooks-subnet-cmds:
+
+``libdhcp_subnet_cmds.so``: Subnet Commands to Manage Subnets and Shared Networks
+=================================================================================
+
+This library offers commands used to query and manipulate subnet and shared network
+configurations in Kea. These can be very useful in deployments
+with a large number of subnets being managed by the DHCP servers,
+when those subnets are frequently updated. The commands offer a lightweight
+approach for manipulating subnets without needing to fully reconfigure
+the server, and without affecting existing servers' configurations. An
+ability to manage shared networks (listing, retrieving details, adding
+new ones, removing existing ones, and adding subnets to and removing them from
+shared networks) is also provided.
+
+.. note::
+
+ :ischooklib:`libdhcp_subnet_cmds.so` is available only to ISC customers with
+ a paid support contract. For more information on subscription options,
+ please complete the form at https://www.isc.org/contact.
+
+.. note::
+
+ This library can only be loaded by the :iscman:`kea-dhcp4` or :iscman:`kea-dhcp6`
+ process.
+
+The following commands are currently supported:
+
+- :isccmd:`subnet4-list` / :isccmd:`subnet6-list` - lists all configured subnets.
+
+- :isccmd:`subnet4-get` / :isccmd:`subnet6-get` - retrieves detailed information about a
+ specified subnet.
+
+- :isccmd:`subnet4-add` / :isccmd:`subnet6-add` - adds a new subnet into the server's
+ configuration.
+
+- :isccmd:`subnet4-update` / :isccmd:`subnet6-update` - updates (replaces) a single subnet in
+ the server's configuration.
+
+- :isccmd:`subnet4-del` / :isccmd:`subnet6-del` - removes a subnet from the server's
+ configuration.
+
+- :isccmd:`subnet4-delta-add` / :isccmd:`subnet6-delta-add` - updates (replaces) parts of a
+ single subnet in the server's configuration.
+
+- :isccmd:`subnet4-delta-del` / :isccmd:`subnet6-delta-del` - removes parts of a single subnet in
+ the server's configuration.
+
+- :isccmd:`network4-list` / :isccmd:`network6-list` - lists all configured shared networks.
+
+- :isccmd:`network4-get` / :isccmd:`network6-get` - retrieves detailed information about a
+ specified shared network.
+
+- :isccmd:`network4-add` / :isccmd:`network6-add` - adds a new shared network to the
+ server's configuration.
+
+- :isccmd:`network4-del` / :isccmd:`network6-del` - removes a shared network from the
+ server's configuration.
+
+- :isccmd:`network4-subnet-add` / :isccmd:`network6-subnet-add` - adds an existing subnet to
+ an existing shared network.
+
+- :isccmd:`network4-subnet-del` / :isccmd:`network6-subnet-del` - removes a subnet from
+ an existing shared network and demotes it to a plain subnet.
+
+.. isccmd:: subnet4-list
+.. _command-subnet4-list:
+
+The ``subnet4-list`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to list all currently configured subnets. Each
+subnet is returned with a subnet identifier and
+subnet prefix. To retrieve
+detailed information about the subnet, use the :isccmd:`subnet4-get` command.
+
+This command has a simple structure:
+
+::
+
+ {
+ "command": "subnet4-list"
+ }
+
+The list of subnets is returned in the following format:
+
+::
+
+ {
+ "result": 0,
+ "text": "2 IPv4 subnets found",
+ "arguments": {
+ "subnets": [
+ {
+ "id": 10,
+ "subnet": "10.0.0.0/8"
+ },
+ {
+ "id": 100,
+ "subnet": "192.0.2.0/24"
+ }
+ ]
+ }
+ }
+
+If no IPv4 subnets are found, an error code is returned along with the
+error description.
+
+.. isccmd:: subnet6-list
+.. _command-subnet6-list:
+
+The ``subnet6-list`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to list all currently configured subnets. Each
+subnet is returned with a subnet identifier and
+subnet prefix. To retrieve
+detailed information about the subnet, use the :isccmd:`subnet6-get` command.
+
+This command has a simple structure:
+
+::
+
+ {
+ "command": "subnet6-list"
+ }
+
+The list of subnets is returned in the following format:
+
+::
+
+ {
+ "result": 0,
+ "text": "2 IPv6 subnets found",
+ "arguments": {
+ "subnets": [
+ {
+ "id": 11,
+ "subnet": "2001:db8:1::/64"
+ },
+ {
+ "id": 233,
+ "subnet": "3000::/16"
+ }
+ ]
+ }
+ }
+
+If no IPv6 subnets are found, an error code is returned along with the
+error description.
+
+.. isccmd:: subnet4-get
+.. _command-subnet4-get:
+
+The ``subnet4-get`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to retrieve detailed information about the
+specified subnet. This command usually follows :isccmd:`subnet4-list`,
+which is used to discover available subnets with their respective subnet
+identifiers and prefixes. Any of those parameters can then be used in
+:isccmd:`subnet4-get` to fetch subnet information:
+
+::
+
+ {
+ "command": "subnet4-get",
+ "arguments": {
+ "id": 10
+ }
+ }
+
+or
+
+::
+
+ {
+ "command": "subnet4-get",
+ "arguments": {
+ "subnet": "10.0.0.0/8"
+ }
+ }
+
+If the subnet exists, the response will be similar to this:
+
+::
+
+ {
+ "result": 0,
+ "text": "Info about IPv4 subnet 10.0.0.0/8 (id 10) returned",
+ "arguments": {
+ "subnets": [
+ {
+ "subnet": "10.0.0.0/8",
+ "id": 1,
+ "option-data": [
+ {
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+ ]
+ }
+ }
+
+.. isccmd:: subnet6-get
+.. _command-subnet6-get:
+
+The ``subnet6-get`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to retrieve detailed information about the
+specified subnet. This command usually follows :isccmd:`subnet6-list`,
+which is used to discover available subnets with their respective subnet
+identifiers and prefixes. Any of those parameters can be then used in
+:isccmd:`subnet6-get` to fetch subnet information:
+
+::
+
+ {
+ "command": "subnet6-get",
+ "arguments": {
+ "id": 11
+ }
+ }
+
+or
+
+::
+
+ {
+ "command": "subnet6-get",
+ "arguments": {
+ "subnet": "2001:db8:1::/64"
+ }
+ }
+
+If the subnet exists, the response will be similar to this:
+
+::
+
+ {
+ "result": 0,
+ "text": "Info about IPv6 subnet 2001:db8:1::/64 (id 11) returned",
+ "arguments": {
+ "subnets": [
+ {
+ "subnet": "2001:db8:1::/64",
+ "id": 1,
+ "option-data": [
+ {
+ ...
+ },
+ ...
+ ],
+ ...
+ }
+ ]
+ }
+ }
+
+.. isccmd:: subnet4-add
+.. _command-subnet4-add:
+
+The ``subnet4-add`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to create and add a new subnet to the existing server
+configuration. This operation has no impact on other subnets. The subnet
+identifier must be specified and must be unique among all subnets. If
+the identifier or a subnet prefix is not unique, an error is reported and
+the subnet is not added.
+
+The subnet information within this command has the same structure as the
+subnet information in the server configuration file, with the exception
+that static host reservations cannot be specified within
+:isccmd:`subnet4-add`. The commands described in :ref:`hooks-host-cmds` should be used to
+add, remove, and modify static reservations.
+
+::
+
+ {
+ "command": "subnet4-add",
+ "arguments": {
+ "subnet4": [ {
+ "id": 123,
+ "subnet": "10.20.30.0/24",
+ ...
+ } ]
+ }
+ }
+
+The response to this command has the following structure:
+
+::
+
+ {
+ "result": 0,
+ "text": "IPv4 subnet added",
+ "arguments": {
+ "subnet4": [
+ {
+ "id": 123,
+ "subnet": "10.20.30.0/24"
+ }
+ ]
+ }
+ }
+
+.. isccmd:: subnet6-add
+.. _command-subnet6-add:
+
+The ``subnet6-add`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to create and add a new subnet to the existing server
+configuration. This operation has no impact on other subnets. The subnet
+identifier must be specified and must be unique among all subnets. If
+the identifier or a subnet prefix is not unique, an error is reported and
+the subnet is not added.
+
+The subnet information within this command has the same structure as the
+subnet information in the server configuration file, with the exception
+that static host reservations cannot be specified within
+:isccmd:`subnet6-add`. The commands described in :ref:`hooks-host-cmds` should be used
+to add, remove, and modify static reservations.
+
+::
+
+ {
+ "command": "subnet6-add",
+ "arguments": {
+ "subnet6": [ {
+ "id": 234,
+ "subnet": "2001:db8:1::/64",
+ ...
+ } ]
+ }
+ }
+
+The response to this command has the following structure:
+
+::
+
+ {
+ "result": 0,
+ "text": "IPv6 subnet added",
+ "arguments": {
+ "subnet6": [
+ {
+ "id": 234,
+ "subnet": "2001:db8:1::/64"
+ }
+ ]
+ }
+ }
+
+It is recommended, but not mandatory, to specify the subnet ID. If not
+specified, Kea will try to assign the next ``subnet-id`` value. This
+automatic ID value generator is simple; it returns the previous
+automatically assigned value, increased by 1. This works well, unless
+a subnet is manually created with a larger value than one previously used. For
+example, if :isccmd:`subnet4-add` is called five times, each without an ID, Kea will
+assign IDs 1, 2, 3, 4, and 5 and it will work just fine. However, if
+:isccmd:`subnet4-add` is called five times, with the first subnet having the
+``subnet-id`` of value 3 and the remaining ones having no ``subnet-id``, the operation will
+fail. The first command (with the explicit value) will use ``subnet-id`` 3; the
+second command will create a subnet with and ID of 1; the third will use a
+value of 2; and finally the fourth will have its ``subnet-id`` value
+auto-generated as 3. However, since there is already a subnet with that
+ID, the process will fail.
+
+The general recommendation is either never to use explicit values, so
+that auto-generated values will always work; or always use explicit
+values, so that auto-generation is never used. The two
+approaches can be mixed only if the administrator understands how internal
+automatic ``subnet-id`` generation works in Kea.
+
+.. note::
+
+ Subnet IDs must be greater than zero and less than 4294967295.
+
+.. isccmd:: subnet4-update
+.. _command-subnet4-update:
+
+The ``subnet4-update`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to update (overwrite) a single subnet in the existing
+server configuration. This operation has no impact on other subnets. The
+subnet identifier is used to identify the subnet to replace; it must be
+specified and must be unique among all subnets. The subnet prefix should
+not be updated.
+
+The subnet information within this command has the same structure as the
+subnet information in the server configuration file, with the exception
+that static host reservations cannot be specified within
+:isccmd:`subnet4-update`. The commands described in :ref:`hooks-host-cmds` should be
+used to update, remove, and modify static reservations.
+
+::
+
+ {
+ "command": "subnet4-update",
+ "arguments": {
+ "subnet4": [ {
+ "id": 123,
+ "subnet": "10.20.30.0/24",
+ ...
+ } ]
+ }
+ }
+
+The response to this command has the following structure:
+
+::
+
+ {
+ "result": 0,
+ "text": "IPv4 subnet updated",
+ "arguments": {
+ "subnet4": [
+ {
+ "id": 123,
+ "subnet": "10.20.30.0/24"
+ }
+ ]
+ }
+ }
+
+As with other update commands, this command overwrites all the contents of the
+entry. If the IPv4 subnet previously had a resource assigned to it, and the
+:isccmd:`subnet4-update` command is missing the resource, it is deleted from the
+server configuration. If an incremental update of the subnet is desired, then
+this can be achieved with :isccmd:`subnet4-delta-add`.
+
+.. isccmd:: subnet6-update
+.. _command-subnet6-update:
+
+The ``subnet6-update`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to update (overwrite) a single subnet in the existing
+server configuration. This operation has no impact on other subnets. The
+subnet identifier is used to identify the subnet to replace; it must be
+specified and must be unique among all subnets. The subnet prefix should
+not be updated.
+
+The subnet information within this command has the same structure as the
+subnet information in the server configuration file, with the exception
+that static host reservations cannot be specified within
+:isccmd:`subnet6-update`. The commands described in :ref:`hooks-host-cmds` should be
+used to update, remove, and modify static reservations.
+
+::
+
+ {
+ "command": "subnet6-update",
+ "arguments": {
+ "subnet6": [ {
+ "id": 234,
+ "subnet": "2001:db8:1::/64",
+ ...
+ } ]
+ }
+ }
+
+The response to this command has the following structure:
+
+::
+
+ {
+ "result": 0,
+ "text": "IPv6 subnet updated",
+ "arguments": {
+ "subnet6": [
+ {
+ "id": 234,
+ "subnet": "2001:db8:1::/64"
+ }
+ ]
+ }
+ }
+
+As with other update commands, this command overwrites all the contents of the
+entry. If the IPv6 subnet previously had a resource assigned to it, and the
+:isccmd:`subnet6-update` command is missing the resource, it is deleted from the
+server configuration. If an incremental update of the subnet is desired, then
+this can be achieved with :isccmd:`subnet6-delta-add`.
+
+.. isccmd:: subnet4-del
+.. _command-subnet4-del:
+
+The ``subnet4-del`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to remove a subnet from the server's configuration.
+This command has no effect on other configured subnets, but removing a
+subnet does have certain implications.
+
+In most cases the server has assigned some leases to the clients
+belonging to the subnet. The server may also be configured with static
+host reservations which are associated with this subnet. The current
+implementation of the :isccmd:`subnet4-del` command removes neither the leases nor
+the host reservations associated with a subnet. This is the safest approach
+because the server does not lose track of leases assigned to clients
+from this subnet. However, removal of the subnet may still cause
+configuration errors and conflicts. For example: after removal of the
+subnet, the server administrator may update a new subnet with the ID
+used previously for the removed subnet. This means that the existing
+leases and static reservations will be in conflict with this new subnet.
+Thus, we recommend that this command be used with extreme caution.
+
+This command can also be used to completely delete an IPv4 subnet that
+is part of a shared network. To simply remove the subnet
+from a shared network and keep the subnet configuration, use the
+:isccmd:`network4-subnet-del` command instead.
+
+The command has the following structure:
+
+::
+
+ {
+ "command": "subnet4-del",
+ "arguments": {
+ "id": 123
+ }
+ }
+
+A successful response may look like this:
+
+::
+
+ {
+ "result": 0,
+ "text": "IPv4 subnet 192.0.2.0/24 (id 123) deleted",
+ "arguments": {
+ "subnets": [
+ {
+ "id": 123,
+ "subnet": "192.0.2.0/24"
+ }
+ ]
+ }
+ }
+
+.. isccmd:: subnet6-del
+.. _command-subnet6-del:
+
+The ``subnet6-del`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to remove a subnet from the server's configuration.
+This command has no effect on other configured subnets, but removing a
+subnet does have certain implications.
+
+In most cases the server has assigned some leases to the clients
+belonging to the subnet. The server may also be configured with static
+host reservations which are associated with this subnet. The current
+implementation of the :isccmd:`subnet6-del` command removes neither the leases nor
+the host reservations associated with a subnet. This is the safest approach
+because the server does not lose track of leases assigned to clients
+from this subnet. However, removal of the subnet may still cause
+configuration errors and conflicts. For example: after removal of the
+subnet, the server administrator may add a new subnet with the ID used
+previously for the removed subnet. This means that the existing leases
+and static reservations will be in conflict with this new subnet. Thus,
+we recommend that this command be used with extreme caution.
+
+This command can also be used to completely delete an IPv6 subnet that
+is part of a shared network. To simply remove the subnet
+from a shared network and keep the subnet configuration, use the
+:isccmd:`network6-subnet-del` command instead.
+
+The command has the following structure:
+
+::
+
+ {
+ "command": "subnet6-del",
+ "arguments": {
+ "id": 234
+ }
+ }
+
+A successful response may look like this:
+
+::
+
+ {
+ "result": 0,
+ "text": "IPv6 subnet 2001:db8:1::/64 (id 234) deleted",
+ "subnets": [
+ {
+ "id": 234,
+ "subnet": "2001:db8:1::/64"
+ }
+ ]
+ }
+
+.. isccmd:: subnet4-delta-add
+.. _command-subnet4-delta-add:
+
+The ``subnet4-delta-add`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to update a subnet by adding or overwriting its parts in
+the existing server configuration. This operation has no impact on other
+subnets. The subnet identifier is used to identify the subnet to update; it must
+be specified and must be unique among all subnets. The subnet prefix should not
+be updated.
+
+The subnet information within this command has the same structure as the
+subnet information in the server configuration file, with the exception
+that static host reservations cannot be specified within
+:isccmd:`subnet4-delta-add`. The commands described in :ref:`hooks-host-cmds` should
+be used to update, remove, and modify static reservations.
+
+::
+
+ {
+ "command": "subnet4-delta-add",
+ "arguments": {
+ "subnet4": [ {
+ "valid-lifetime": 120,
+ "id": 123,
+ "subnet": "10.20.30.0/24",
+ "option-data": [
+ {
+ "always-send": false,
+ "code": 3,
+ "csv-format": true,
+ "data": "192.0.3.1",
+ "name": "routers",
+ "space": "dhcp4"
+ }
+ ],
+ "pools": [
+ {
+ "pool": "10.20.30.1-10.20.30.10",
+ "option-data": [
+ {
+ "always-send": false,
+ "code": 4,
+ "csv-format": true,
+ "data": "192.0.4.1",
+ "name": "time-servers",
+ "space": "dhcp4"
+ }
+ ]
+ }
+ ]
+ } ]
+ }
+ }
+
+The response to this command has the following structure:
+
+::
+
+ {
+ "result": 0,
+ "text": "IPv4 subnet updated",
+ "arguments": {
+ "subnet4": [
+ {
+ "id": 123,
+ "subnet": "10.20.30.0/24"
+ }
+ ]
+ }
+ }
+
+The command updates subnet "10.20.30.0/24" with id 123 by changing the valid
+lifetime, adding or changing the subnet level option 3 ("routers"), by adding
+or changing the pool "10.20.30.1-10.20.30.10" and by adding or changing the pool
+level option 4 ("time-servers").
+
+.. isccmd:: subnet6-delta-add
+.. _command-subnet6-delta-add:
+
+The ``subnet6-delta-add`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to update a subnet by adding or overwriting its parts in
+the existing server configuration. This operation has no impact on other
+subnets. The subnet identifier is used to identify the subnet to update; it must
+be specified and must be unique among all subnets. The subnet prefix should not
+be updated.
+
+The subnet information within this command has the same structure as the
+subnet information in the server configuration file, with the exception
+that static host reservations cannot be specified within
+:isccmd:`subnet6-delta-add`. The commands described in :ref:`hooks-host-cmds` should
+be used to update, remove, and modify static reservations.
+
+::
+
+ {
+ "command": "subnet6-delta-add",
+ "arguments": {
+ "subnet6": [ {
+ "valid-lifetime": 120,
+ "id": 243,
+ "subnet": "2001:db8:1::/64",
+ "option-data": [
+ {
+ "always-send": false,
+ "code": 23,
+ "csv-format": true,
+ "data": "3000::3:1",
+ "name": "dns-servers",
+ "space": "dhcp6"
+ }
+ ],
+ "pd-pools": [
+ {
+ "prefix": "2001:db8:2::",
+ "prefix-len": 48,
+ "delegated-len": 64,
+ "option-data": [
+ {
+ "always-send": false,
+ "code": 22,
+ "csv-format": true,
+ "data": "3000::4:1",
+ "name": "sip-server-addr",
+ "space": "dhcp6"
+ }
+ ]
+ }
+ ],
+ "pools": [
+ {
+ "pool": "2001:db8:1::1-2001:db8:1::10",
+ "option-data": [
+ {
+ "always-send": false,
+ "code": 31,
+ "csv-format": true,
+ "data": "3000::5:1",
+ "name": "sntp-servers",
+ "space": "dhcp6"
+ }
+ ]
+ }
+ ]
+ } ]
+ }
+ }
+
+The response to this command has the following structure:
+
+::
+
+ {
+ "result": 0,
+ "text": "IPv6 subnet updated",
+ "arguments": {
+ "subnet6": [
+ {
+ "id": 234,
+ "subnet": "2001:db8:1::/64"
+ }
+ ]
+ }
+ }
+
+The command updates subnet "2001:db8:1::/64" with id 243 by changing the valid
+lifetime, adding or changing the subnet level option 23 ("dns-servers"), by
+adding or changing the pool "2001:db8:1::1-2001:db8:1::10", by adding or
+changing the pool level option 31 ("sntp-servers"), by adding or changing the
+pd-pool "2001:db8:2::" with prefix-len 48 and by adding or changing the pd-pool
+level option 22 ("sip-server-addr").
+
+.. isccmd:: subnet4-delta-del
+.. _command-subnet4-delta-del:
+
+The ``subnet4-delta-del`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to update a subnet by removing its parts in the existing
+server configuration. This operation has no impact on other subnets.
+The subnet identifier is used to identify the subnet to update; it must be
+specified and must be unique among all subnets. The subnet prefix should not be
+updated.
+
+The subnet information within this command has the same structure as the
+subnet information in the server configuration file, with the exception
+that static host reservations cannot be specified within
+:isccmd:`subnet4-delta-del`. The commands described in :ref:`hooks-host-cmds` should
+be used to update, remove, and modify static reservations.
+
+The command is flexible and can delete the part of the subnet by either
+specifying the entire object that needs to be deleted, or just the keys
+identifying the respective object. The address pools are identified by the
+'pool' parameter, the options are identified by the 'name' or 'code' and
+'space' parameters. The 'space' parameter can be omitted if the option belongs
+to the default 'dhcp4' space.
+
+::
+
+ {
+ "command": "subnet4-delta-del",
+ "arguments": {
+ "subnet4": [ {
+ "valid-lifetime": 0,
+ "id": 123,
+ "subnet": "10.20.30.0/24",
+ "option-data": [
+ { "name": "routers" }
+ ],
+ "pools": [
+ {
+ "option-data": [
+ { "code": 4 }
+ ],
+ "pool": "10.20.30.11-10.20.30.20"
+ },
+ {
+ "pool": "10.20.30.21-10.20.30.30"
+ }
+ ]
+ } ]
+ }
+ }
+
+The response to this command has the following structure:
+
+::
+
+ {
+ "result": 0,
+ "text": "IPv4 subnet updated",
+ "arguments": {
+ "subnet4": [
+ {
+ "id": 123,
+ "subnet": "10.20.30.0/24"
+ }
+ ]
+ }
+ }
+
+The command updates subnet "10.20.30.0/24" with id 123 by removing the valid
+lifetime, removing the subnet level option 3 ("routers"), by removing the pool
+"10.20.30.21-10.20.30.30" and by removing the pool level option 4
+("time-servers") in pool "10.20.30.11-10.20.30.20".
+The scalar values don't need to match what is configured, but still need to be
+present to maintain a valid json structure and to be a valid value to be able to
+be parsed.
+
+.. isccmd:: subnet6-delta-del
+.. _command-subnet6-delta-del:
+
+The ``subnet6-delta-del`` Command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This command is used to update a subnet by removing its parts in the existing
+server configuration. This operation has no impact on other subnets.
+The subnet identifier is used to identify the subnet to update; it must be
+specified and must be unique among all subnets. The subnet prefix should not be
+updated.
+
+The subnet information within this command has the same structure as the
+subnet information in the server configuration file, with the exception
+that static host reservations cannot be specified within
+:isccmd:`subnet6-delta-del`. The commands described in :ref:`hooks-host-cmds` should
+be used to update, remove, and modify static reservations.
+
+The command is flexible and can delete the part of the subnet by either
+specifying the entire object that needs to be deleted, or just the keys
+identifying the respective object. The address pools are identified by the
+'pool' parameter, the prefix pools are identified by the "prefix", "prefix-len"
+and "delegated-len" parameters, the options are identified by the 'name' or
+'code' and 'space' parameters. The 'space' parameter can be omitted if the
+option belongs to the default 'dhcp6' space.
+
+.. code-block:: json
+
+ {
+ "command": "subnet6-delta-del",
+ "arguments": {
+ "subnet6": [ {
+ "valid-lifetime": 0,
+ "id": 234,
+ "subnet": "2001:db8:1::/64",
+ "option-data": [
+ { "name": "dns-servers" }
+ ],
+ "pd-pools": [
+ {
+ "prefix": "2001:db8:3::",
+ "prefix-len": 48,
+ "delegated-len": 64,
+ "option-data": [
+ { "code": 22 }
+ ]
+ },
+ {
+ "prefix": "2001:db8:4::",
+ "prefix-len": 48,
+ "delegated-len": 64
+ }
+ ],
+ "pools": [
+ {
+ "option-data": [
+ { "code": 31 }
+ ],
+ "pool": "2001:db8:1::11-2001:db8:1::20"
+ },
+ {
+ "pool": "2001:db8:1::21-2001:db8:1::30"
+ }
+ ]
+ } ]
+ }
+ }
+
+The response to this command has the following structure:
+
+::
+
+ {
+ "result": 0,
+ "text": "IPv6 subnet updated",
+ "arguments": {
+ "subnet6": [
+ {
+ "id": 234,
+ "subnet": "2001:db8:1::/64"
+ }
+ ]
+ }
+ }
+
+The command updates subnet "2001:db8:1::/64" with id 243 by removing the valid
+lifetime, removing the subnet level option 23 ("dns-servers"), by removing the
+pool "2001:db8:1::21-2001:db8:1::30", by removing the pool level option 31
+("sntp-servers") in pool "2001:db8:1::11-2001:db8:1::20", by removing the
+pd-pool "2001:db8:4::" with prefix-len 48, by removing the pd-pool level option
+22 ("sip-server-addr") in pd-pool "2001:db8:3::" with prefix-len 48.
+The scalar values don't need to match what is configured, but still need to be
+present to maintain a valid json structure and to be a valid value to be able to
+be parsed.
+
+.. isccmd:: network4-list
+.. _command-network4-list:
+
+.. isccmd:: network6-list
+.. _command-network6-list:
+
+The ``network4-list``, ``network6-list`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to retrieve the full list of currently configured
+shared networks. The list contains only very basic information about
+each shared network. If more details are needed, please use
+:isccmd:`network4-get` or :isccmd:`network6-get` to retrieve all information
+available. This command does not require any parameters and its
+invocation is very simple:
+
+::
+
+ {
+ "command": "network4-list"
+ }
+
+An example response for :isccmd:`network4-list` looks as follows:
+
+::
+
+ {
+ "arguments": {
+ "shared-networks": [
+ { "name": "floor1" },
+ { "name": "office" }
+ ]
+ },
+ "result": 0,
+ "text": "2 IPv4 network(s) found"
+ }
+
+The :isccmd:`network6-list` command uses exactly the same syntax for both the
+command and the response.
+
+.. isccmd:: network4-get
+.. _command-network4-get:
+
+.. isccmd:: network6-get
+.. _command-network6-get:
+
+The ``network4-get``, ``network6-get`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to retrieve detailed information about shared
+networks, including subnets that are currently part of a given network.
+Both commands take one mandatory parameter, ``name``, which specifies the
+name of the shared network. An example command to retrieve details about
+an IPv4 shared network with the name "floor13" looks as follows:
+
+::
+
+ {
+ "command": "network4-get",
+ "arguments": {
+ "name": "floor13"
+ }
+ }
+
+An example response could look as follows:
+
+::
+
+ {
+ "result": 0,
+ "text": "Info about IPv4 shared network 'floor13' returned",
+ "arguments": {
+ "shared-networks": [
+ {
+ "match-client-id": true,
+ "name": "floor13",
+ "option-data": [ ],
+ "rebind-timer": 90,
+ "relay": {
+ "ip-address": "0.0.0.0"
+ },
+ "renew-timer": 60,
+ # "reservation-mode": "all",
+ # It is replaced by the "reservations-global",
+ # "reservations-in-subnet", and "reservations-out-of-pool"
+ # parameters.
+ # Specify if the server should look up global reservations.
+ "reservations-global": false,
+ # Specify if the server should look up in-subnet reservations.
+ "reservations-in-subnet": true,
+ # Specify if the server can assume that all reserved addresses
+ # are out-of-pool.
+ "reservations-out-of-pool": false,
+ "subnet4": [
+ {
+ "subnet": "192.0.2.0/24",
+ "id": 5,
+ ...
+ # many other subnet-specific details here
+ },
+ {
+ "id": 6,
+ "subnet": "192.0.3.0/31",
+ ...
+ # many other subnet-specific details here
+ }
+ ],
+ "valid-lifetime": 120
+ }
+ ]
+ }
+ }
+
+The actual response contains many additional fields that are
+omitted here for clarity. The response format is exactly the same as
+used in :isccmd:`config-get`, just limited to returning the shared network's
+information.
+
+.. isccmd:: network4-add
+.. _command-network4-add:
+
+.. isccmd:: network6-add
+.. _command-network6-add:
+
+The ``network4-add``, ``network6-add`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to add a new shared network, which must
+have a unique name. This command requires one parameter,
+``shared-networks``, which is a list and should contain exactly one
+entry that defines the network. The only mandatory element for a network
+is its name. Although it does not make operational sense, it is possible
+to add an empty shared network that does not have any subnets in it.
+That is allowed for testing purposes, but having empty networks (or networks with
+only one subnet) is discouraged in production environments. For details
+regarding syntax, see :ref:`shared-network4` and
+:ref:`shared-network6`.
+
+.. note::
+
+ As opposed to parameter inheritance during the processing of a full new
+ configuration, this command does not fully handle parameter inheritance.
+ Any missing parameters will be filled with default values, rather
+ than inherited from the global scope.
+
+An example that showcases how to add a new IPv4 shared network looks as
+follows:
+
+::
+
+ {
+ "command": "network4-add",
+ "arguments": {
+ "shared-networks": [ {
+ "name": "floor13",
+ "subnet4": [
+ {
+ "id": 100,
+ "pools": [ { "pool": "192.0.2.2-192.0.2.99" } ],
+ "subnet": "192.0.2.0/24",
+ "option-data": [
+ {
+ "name": "routers",
+ "data": "192.0.2.1"
+ }
+ ]
+ },
+ {
+ "id": 101,
+ "pools": [ { "pool": "192.0.3.2-192.0.3.99" } ],
+ "subnet": "192.0.3.0/24",
+ "option-data": [
+ {
+ "name": "routers",
+ "data": "192.0.3.1"
+ }
+ ]
+ } ]
+ } ]
+ }
+ }
+
+Assuming there was no shared network with a name "floor13" and no subnets
+with IDs 100 and 101 previously configured, the command will be
+successful and will return the following response:
+
+::
+
+ {
+ "arguments": {
+ "shared-networks": [ { "name": "floor13" } ]
+ },
+ "result": 0,
+ "text": "A new IPv4 shared network 'floor13' added"
+ }
+
+The :isccmd:`network6-add` command uses the same syntax for both the query and the
+response. However, there are some parameters that are IPv4-only (e.g.
+``match-client-id``) and some that are IPv6-only (e.g. ``interface-id``). The same
+applies to subnets within the network.
+
+.. isccmd:: network4-del
+.. _command-network4-del:
+
+.. isccmd:: network6-del
+.. _command-network6-del:
+
+The ``network4-del``, ``network6-del`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to delete existing shared networks. Both
+commands take exactly one parameter, ``name``, that specifies the name of
+the network to be removed. An example invocation of the :isccmd:`network4-del`
+command looks as follows:
+
+::
+
+ {
+ "command": "network4-del",
+ "arguments": {
+ "name": "floor13"
+ }
+ }
+
+Assuming there was such a network configured, the response will look
+similar to the following:
+
+::
+
+ {
+ "arguments": {
+ "shared-networks": [
+ {
+ "name": "floor13"
+ }
+ ]
+ },
+ "result": 0,
+ "text": "IPv4 shared network 'floor13' deleted"
+ }
+
+The :isccmd:`network6-del` command uses exactly the same syntax for both the
+command and the response.
+
+If there are any subnets belonging to the shared network being deleted,
+they will be demoted to a plain subnet. There is an optional parameter
+called ``subnets-action`` that, if specified, takes one of two possible
+values: ``keep`` (which is the default) and ``delete``. It controls
+whether the subnets are demoted to plain subnets or removed. An example
+usage in the :isccmd:`network6-del` command that deletes the shared network and all
+subnets in it could look as follows:
+
+::
+
+ {
+ "command": "network4-del",
+ "arguments": {
+ "name": "floor13",
+ "subnets-action": "delete"
+ }
+ }
+
+Alternatively, to completely remove the subnets, it is possible to use the
+:isccmd:`subnet4-del` or :isccmd:`subnet6-del` commands.
+
+.. isccmd:: network4-subnet-add
+.. _command-network4-subnet-add:
+
+.. isccmd:: network6-subnet-add
+.. _command-network6-subnet-add:
+
+The ``network4-subnet-add``, ``network6-subnet-add`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to add existing subnets to existing shared
+networks. There are several ways to add a new shared network. The system
+administrator can add the whole shared network at once, either by
+editing a configuration file or by calling the :isccmd:`network4-add` or
+:isccmd:`network6-add` command with the desired subnets in it. This approach
+works well for completely new shared subnets. However, there may be
+cases when an existing subnet is running out of addresses and needs to
+be extended with additional address space; in other words, another subnet
+needs to be added on top of it. For this scenario, a system administrator
+can use :isccmd:`network4-add` or :isccmd:`network6-add`, and then add an existing
+subnet to this newly created shared network using
+:isccmd:`network4-subnet-add` or :isccmd:`network6-subnet-add`.
+
+The :isccmd:`network4-subnet-add` and
+:isccmd:`network6-subnet-add` commands take
+two parameters: ``id``, which is an integer and specifies the ID of
+an existing subnet to be added to a shared network; and ``name``, which
+specifies the name of the shared network to which the subnet will be added. The
+subnet must not belong to any existing network; to
+reassign a subnet from one shared network to another, use the
+:isccmd:`network4-subnet-del` or
+:isccmd:`network6-subnet-del` command commands first.
+
+An example invocation of the :isccmd:`network4-subnet-add` command looks as
+follows:
+
+::
+
+ {
+ "command": "network4-subnet-add",
+ "arguments": {
+ "name": "floor13",
+ "id": 5
+ }
+ }
+
+Assuming there is a network named "floor13", and there is a subnet with
+``subnet-id`` 5 that is not a part of the existing network, the command will
+return a response similar to the following:
+
+::
+
+ {
+ "result": 0,
+ "text": "IPv4 subnet 10.0.0.0/8 (id 5) is now part of shared network 'floor13'"
+ }
+
+The :isccmd:`network6-subnet-add` command uses exactly the same syntax for both the
+command and the response.
+
+.. note::
+
+ As opposed to parameter inheritance during the processing of a full new
+ configuration or when adding a new shared network with new subnets,
+ this command does not fully handle parameter inheritance.
+ Any missing parameters will be filled with default values, rather
+ than inherited from the global scope or from the shared network.
+
+.. isccmd:: network4-subnet-del
+.. _command-network4-subnet-del:
+
+.. isccmd:: network6-subnet-del
+.. _command-network6-subnet-del:
+
+The ``network4-subnet-del``, ``network6-subnet-del`` Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These commands are used to remove a subnet that is part of an existing
+shared network and demote it to a plain, stand-alone subnet.
+To remove a subnet completely, use the :isccmd:`subnet4-del` or :isccmd:`subnet6-del`
+commands instead. The :isccmd:`network4-subnet-del` and
+:isccmd:`network6-subnet-del` commands take two parameters: ``id``, which is
+an integer and specifies the ID of an existing subnet to be removed from
+a shared network; and ``name``, which specifies the name of the shared
+network from which the subnet will be removed.
+
+An example invocation of the :isccmd:`network4-subnet-del` command looks as
+follows:
+
+::
+
+ {
+ "command": "network4-subnet-del",
+ "arguments": {
+ "name": "floor13",
+ "id": 5
+ }
+ }
+
+Assuming there was a subnet with ``subnet-id`` 5, that was part of a
+shared network named "floor13", the response would look similar to the
+following:
+
+::
+
+ {
+ "result": 0,
+ "text": "IPv4 subnet 10.0.0.0/8 (id 5) is now removed from shared network 'floor13'"
+ }
+
+The :isccmd:`network6-subnet-del` command uses exactly the same syntax for both the
+command and the response.
diff --git a/doc/sphinx/arm/hooks-user-chk.rst b/doc/sphinx/arm/hooks-user-chk.rst
new file mode 100644
index 0000000..37f94b4
--- /dev/null
+++ b/doc/sphinx/arm/hooks-user-chk.rst
@@ -0,0 +1,83 @@
+.. ischooklib:: libdhcp_user_chk.so
+.. _hooks-user-chk:
+
+``libdhcp_user_chk.so``: User Check
+===================================
+
+This library serves several purposes:
+
+- To assign "new" or "unregistered" users to a restricted subnet, while
+ "known" or "registered" users are assigned to unrestricted subnets.
+
+- To allow DHCP response options or vendor option values to be
+ customized based on user identity.
+
+- To provide a real-time record of user registration activity, which
+ can be sampled by an external consumer.
+
+- To serve as a demonstration of various capabilities possible using
+ the hooks interface.
+
+.. note::
+
+ :ischooklib:`libdhcp_user_chk.so` is part of the open source code and is
+ available to every Kea user.
+
+Once loaded, the library allows the separation of incoming requests into known
+and unknown clients. For known clients, packets are processed
+as usual, although it is possible to override the sending of certain options
+on a per-host basis. Clients that are not on the known
+hosts list are treated as unknown and are assigned to the last
+subnet defined in the configuration file.
+
+As an example of a use case, this behavior may be implemented to put unknown users
+into a separate subnet that leads to a "walled garden," where they can
+only access a registration portal. Once they fill in necessary data,
+their details are added to the known clients file and they get a proper
+address after their device is restarted.
+
+.. note::
+
+ This library was developed several years before the host reservation
+ mechanism became available. Host reservation is much
+ more powerful and flexible, but the ability of ``user_chk``
+ to consult an external source of information about clients and alter
+ Kea's behavior remains useful and of educational value.
+
+The library reads the ``/tmp/user_chk_registry.txt`` file while being loaded
+and each time an incoming packet is processed. Each line of the file is expected to
+contain a self-contained JSON snippet which must have the
+following two entries:
+
+- ``type`` - whose value is ``"HW_ADDR"`` for IPv4 users or ``"DUID"`` for IPv6
+ users.
+
+- ``id`` - whose value is either the hardware address or the DUID from
+ the request formatted as a string of hex digits, with or without ":"
+ delimiters.
+
+and may have zero or more of the following entries:
+
+- ``bootfile`` - whose value is the pathname of the desired file.
+
+- ``tftp_server`` - whose value is the hostname or IP address of the
+ desired server.
+
+A sample user registry file is shown below:
+
+::
+
+ { "type" : "HW_ADDR", "id" : "0c:0e:0a:01:ff:04", "bootfile" : "/tmp/v4bootfile" }
+ { "type" : "HW_ADDR", "id" : "0c:0e:0a:01:ff:06", "tftp_server" : "tftp.v4.example.com" }
+ { "type" : "DUID", "id" : "00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:04", "bootfile" : "/tmp/v6bootfile" }
+ { "type" : "DUID", "id" : "00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:06", "tftp_server" : "tftp.v6.example.com" }
+
+As with any other hook libraries provided by ISC, internals of the
+``user_chk`` code are well-documented. Users may refer to the `user_chk
+library section of the Kea Developer's Guide
+<https://reports.kea.isc.org/dev_guide/d8/db2/libdhcp_user_chk.html>`__
+for information on how the code works internally. That, together with the
+`Hooks Framework section of the Kea Developer's Guide
+<https://reports.kea.isc.org/dev_guide/index.html#hooksFramework>`__ should give users
+some pointers on how to extend this library and perhaps even write one
+from scratch.
diff --git a/doc/sphinx/arm/hooks.rst b/doc/sphinx/arm/hooks.rst
new file mode 100644
index 0000000..73a26b9
--- /dev/null
+++ b/doc/sphinx/arm/hooks.rst
@@ -0,0 +1,626 @@
+.. _hooks-libraries:
+
+**************
+Hook Libraries
+**************
+
+.. _hooks-libraries-introduction:
+
+Introduction
+============
+
+Kea is both flexible and customizable, via the use of "hooks." This feature
+lets Kea load one or more
+dynamically linked libraries (known as "hook libraries") and call functions
+in them at various points in its processing ("hook points").
+Those functions perform whatever custom processing is required.
+
+The hooks concept allows the core Kea code to remain reasonably small
+by moving features that only some, but not all, users find useful to
+external libraries. Those with no need for certain functions can simply choose not
+to load those libraries.
+
+Hook libraries are loaded by individual Kea processes, not by Kea as a
+whole. This means, among other things, that it is possible to associate one set
+of libraries with the DHCPv4 server and a different set with the DHCPv6
+server.
+
+It is also possible for a process to load
+multiple libraries. When processing reaches a hook point, Kea calls the
+hook library functions attached to it. If multiple libraries have
+attached a function to a given hook point, Kea calls all of them, in the
+order in which the libraries are specified in the configuration file.
+The order may be important; consult the documentation of the libraries
+for specifics.
+
+When a Kea process unloads a library, it expects the ``dlclose`` function
+to remove all library symbols, as well as the library code, from address space.
+Although most OSes implement the ``dlclose`` function, this behavior is not
+required by the POSIX standard and not all systems support it; for example, the musl
+library, used by default by Alpine Linux, implements the ``dlclose`` function
+as a no operation. On such systems a library actually remains loaded for the
+lifetime of the process, which means that it must be restarted
+to update libraries with newer versions; it is not sufficient to simply
+reconfigure or reload the Kea process.
+
+The next sections describe how to install and configure hook libraries. Users who are
+interested in writing their own hook library can find information
+in the `Hooks Developer's Guide section of the Kea Developer's
+Guide <https://reports.kea.isc.org/dev_guide/df/d46/hooksdgDevelopersGuide.html>`__.
+
+Note that some libraries are available under different licenses.
+
+Please also note that some libraries may require additional dependencies and/or
+compilation switches to be enabled.
+
+
+Installing Hook Packages
+========================
+
+.. note::
+
+ For more details about installing the Kea Premium Hooks package, please read
+ `this Knowledgebase article <https://kb.isc.org/docs/aa-01587>`__.
+
+Some hook packages are included in the base Kea sources. There is no
+need to do anything special to compile or install them, as they are covered
+by the usual building and installation procedures. Please
+refer to :ref:`installation` for a general overview of the installation process.
+
+ISC provides several additional premium hooks in the form of packages, which
+follow a similar installation procedure but with several additional steps.
+For our users' convenience, the premium hooks installation procedure is described in this section.
+
+1. Download the package; detailed instructions are provided in the KB article
+above. The package will be a file with a name similar to
+``kea-premium-|release|.tar.gz``. (The name may vary depending on the package
+purchased.)
+
+2. Administrators who have the sources for the corresponding version of the
+open-source Kea package on their system from the initial Kea installation
+should skip this step. Otherwise, extract the Kea source from the original
+tarball that was downloaded. For example, with a download of Kea |release|,
+there should be a tarball called ``kea-|release|.tar.gz`` on the system.
+Unpack this tarball:
+
+.. parsed-literal::
+
+ $ tar -zxvf kea-|release|.tar.gz
+
+This will unpack the tarball into the ``kea-|release|`` subdirectory of
+the current working directory.
+
+3. Unpack the Kea premium hooks tarball into the same directory where the
+original Kea source is located. Once Kea |release| has been unpacked into a ``kea-|release|``
+subdirectory and the Kea premium tarball is in the current directory, the following
+steps will unpack the premium tarball into the correct location:
+
+.. parsed-literal::
+
+ $ cd kea-|release|
+ $ tar -xvf ../kea-premium-|release|.tar.gz
+
+Note that unpacking the Kea premium package puts the files into a
+directory named ``premium``. Regardless of the name of the package, the
+directory is always called ``premium``, although its contents will vary
+depending on the hooks package.
+
+4. Run the ``autoreconf`` tools. This step is necessary to update Kea's build
+script to include the additional directory. If this tool is not already
+available on the system, install the ``automake`` and ``autoconf``
+tools. To generate the configure script, please use:
+
+::
+
+ $ autoreconf -i
+
+5. Rerun ``configure``, using the same configuration options that were used when
+originally building Kea. It is possible to verify that ``configure`` has detected the
+premium package by inspecting the summary printed when it exits. The
+first section of the output should look something like this:
+
+.. parsed-literal::
+
+ Package:
+ Name: kea
+ Version: |release|
+ Extended version: |release| (tarball)
+ OS Family: Linux
+ Using GNU sed: yes
+ Premium package: yes
+ Included Hooks: forensic_log flex_id host_cmds
+
+The last line indicates which specific hooks were detected. Note that
+some hooks may require their own dedicated switches.
+Please consult later sections of this chapter for details.
+
+6. Rebuild Kea.
+
+::
+
+ $ make
+
+If the machine has multiple CPU cores, an interesting option to consider
+here is using the argument ``-j X``, where ``X`` is the number of available cores.
+
+7. Install Kea sources along with the hooks:
+
+::
+
+ $ sudo make install
+
+Note that as part of the installation procedure, the install script
+places additional hook libraries and associated files into the ``premium/`` directory.
+
+The installation location of the hook libraries depends on whether the
+``--prefix`` parameter was specified in the ``configure`` script. If not,
+the default location is ``/usr/local/lib/kea/hooks``. The proper installation
+of the libraries can be verified with this command:
+
+::
+
+ $ ls -l /usr/local/lib/kea/hooks/*.so
+ /usr/local/lib/kea/hooks/libdhcp_class_cmds.so
+ /usr/local/lib/kea/hooks/libdhcp_flex_id.so
+ /usr/local/lib/kea/hooks/libdhcp_flex_option.so
+ /usr/local/lib/kea/hooks/libdhcp_host_cmds.so
+ /usr/local/lib/kea/hooks/libdhcp_lease_cmds.so
+ /usr/local/lib/kea/hooks/libdhcp_legal_log.so
+ /usr/local/lib/kea/hooks/libdhcp_subnet_cmds.so
+
+The exact list returned depends on the packages installed. If the
+directory was specified via ``--prefix``, the hook libraries will be located in
+``{prefix directory}/lib/kea/hooks``.
+
+Configuring Hook Libraries
+===========================
+
+The hook libraries for a given process are configured using the
+``hooks-libraries`` keyword in the configuration for that process. (Note
+that the word "hooks" is plural.) The value of the keyword is an array
+of map structures, with each structure corresponding to a hook library. For
+example, to set up two hook libraries for the DHCPv4 server, the
+configuration would be:
+
+::
+
+ "Dhcp4": {
+ :
+ "hooks-libraries": [
+ {
+ "library": "/opt/first_custom_hooks_example.so"
+ },
+ {
+ "library": "/opt/local/second_custom_hooks_example.so",
+ "parameters": {
+ "mail": "spam@example.com",
+ "floor": 13,
+ "debug": false,
+ "users": [ "alice", "bob", "charlie" ],
+ "languages": {
+ "french": "bonjour",
+ "klingon": "yl'el"
+ }
+ }
+ }
+ ]
+ :
+ }
+
+.. note::
+
+ Libraries are reloaded even if their lists have not changed,
+ because the parameters specified for the library (or the files those
+ parameters point to) may have changed.
+
+Libraries may have additional parameters that are not mandatory, in the
+sense that there may be libraries that do not require them. However, for any
+given library there is often a requirement to specify a certain
+set of parameters. Please consult the documentation for each individual library for
+details. In the example above, the first library (``/opt/first_custom_hooks_example.so``)
+has no parameters. The second library (``/opt/local/second_custom_hooks_example.so``)
+has five parameters: specifying mail (string parameter), floor (integer parameter),
+debug (boolean parameter), lists (list of strings), and maps (containing strings).
+Nested parameters can be used if the library supports it. This topic is explained in detail
+in the `Hooks Developer's Guide section of the Kea Developer's Guide
+<https://reports.kea.isc.org/dev_guide/df/d46/hooksdgDevelopersGuide.html>`__.
+
+Some hooks use user context to set the parameters. See :ref:`user-context-hooks`.
+
+Notes:
+
+- The full path to each library should be given.
+
+- As noted above, the order in which the hooks are called may be important;
+ consult the documentation for each library for specifics.
+
+- An empty list has the same effect as omitting the ``hooks-libraries``
+ configuration element altogether.
+
+ .. note::
+
+ There is one case where this is not true: if Kea is running with a
+ configuration that contains a ``hooks-libraries`` item, and that
+ item is removed and the configuration reloaded, the removal will
+ be ignored and the libraries remain loaded. As a workaround,
+ instead of removing the ``hooks-libraries`` item, change it to an
+ empty list.
+
+At the moment, only the :iscman:`kea-dhcp4` and :iscman:`kea-dhcp6` processes support
+hook libraries.
+
+.. _order-of-configuration-hooks:
+
+Order of Configuration:
+~~~~~~~~~~~~~~~~~~~~~~~
+
+It is important to recognize that the order in which hook libraries are
+configured determines the order in which their callouts will be executed,
+in cases where more than one hook library implements the same callout. For
+example, if you wish to use the flex-id hook library to formulate the client
+IDs in conjunction with HA hook library for load-balanced HA, it is essential
+that the flex-id library be specified first in your server's ``hooks-libraries``
+section. This ensures that the client ID is formulated by the flex-id library
+before the HA library uses it for load-balancing. Similarly it would be best to
+specify forensic logging last, to ensure any other install hooks have made
+their contributions to the packet processing.
+
+.. _user-context-hooks:
+
+User Contexts in Hooks
+~~~~~~~~~~~~~~~~~~~~~~
+
+Hook libraries can have their own configuration parameters, which is
+convenient if the parameter applies to the whole library. However,
+sometimes it is useful to extend certain configuration entities
+with additional configuration data. This is where the concept
+of user contexts comes in. A system administrator can define an arbitrary set of
+data and attach it to Kea structures, as long as the data is specified
+as a JSON map. In particular, it is possible to define fields that are
+integers, strings, boolean, lists, or maps. It is possible to define
+nested structures of arbitrary complexity. Kea does not use that data on
+its own; it simply stores it and makes it available for the hook libraries.
+
+Another use case for user contexts may be storing comments and other
+information that will be retained by Kea. Regular comments are discarded
+when the configuration is loaded, but user contexts are retained. This is
+useful if administrators want their comments to survive :isccmd:`config-set` or :isccmd:`config-get`
+operations, for example.
+
+If user context is supported in a given context, the parser translates
+"comment" entries into user context with a "comment" entry.
+
+User context can store configuration for multiple hooks and comments at once.
+
+Some hooks use user context for a configuration that can be easily edited
+without the need to restart the server.
+
+The DDNS-Tuning Hook uses user-context to configure per subnet behavior. Example:
+
+::
+
+ "subnet4": [{
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "pools": [{
+ "pool": "192.0.2.10 - 192.0.2.20"
+ } ],
+ "user-context": {
+ "ddns-tuning": {
+ "hostname-expr": "'guest-'+int8totext(substring(pkt4.yiaddr, 0,1))+'-' \
+ +int8totext(substring(pkt4.yiaddr, 1,2))+'-' \
+ +int8totext(substring(pkt4.yiaddr, 2,3))+'-' \
+ +int8totext(substring(pkt4.yiaddr, 3,4))"
+ },
+ "last-modified": "2017-09-04 13:32",
+ "phones": [ "x1234", "x2345" ],
+ "devices-registered": 42,
+ "billing": false
+ }
+ }]
+
+
+The Limits hook uses user-context in classes and subnets to set parameters. For example:
+
+.. code-block:: json
+
+ {
+ "Dhcp6": {
+ "client-classes": [
+ {
+ "name": "gold",
+ "user-context": {
+ "limits": {
+ "address-limit": 2,
+ "prefix-limit": 1,
+ "rate-limit": "1000 packets per second"
+ }
+ }
+ }
+ ],
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/libdhcp_limits.so"
+ }
+ ],
+ "subnet6": [
+ {
+ "id": 1,
+ "pools": [
+ {
+ "pool": "2001:db8::/64"
+ }
+ ],
+ "subnet": "2001:db8::/64",
+ "user-context": {
+ "limits": {
+ "address-limit": 4,
+ "prefix-limit": 2,
+ "rate-limit": "10 packets per minute"
+ }
+ }
+ }
+ ]
+ }
+ }
+
+Available Hook Libraries
+========================
+
+As described above, the hook functionality provides a way to customize
+a Kea server without modifying the core code. ISC has chosen to take
+advantage of this feature to provide functions that may only be useful
+to a subset of Kea users. To this end, ISC has created some hook
+libraries, discussed in the following sections.
+
+.. note::
+
+ Some of these libraries are available with the base code, while others are
+ premium libraries available for purchase, or only shared with organizations
+ who contribute to Kea's development through paid ISC support contracts. Paid support
+ includes professional engineering assistance, advance security notifications, input
+ into ISC's roadmap planning, and many other benefits, while helping
+ keep Kea sustainable in the long term. ISC encourages companies and organizations
+ to consider purchasing a paid support contract; further information can be
+ obtained by completing the form at https://www.isc.org/contact.
+
+The following table provides a list of hook libraries currently available
+from ISC. It is important to pay attention to which libraries may be
+loaded by which Kea processes. It is a common mistake to configure the
+:iscman:`kea-ctrl-agent` process to load libraries that should, in fact, be
+loaded by the :iscman:`kea-dhcp4` or :iscman:`kea-dhcp6` processes. If a library
+from ISC does not work as expected, please make sure that it has been
+loaded by the correct process per the table below.
+
+.. tabularcolumns:: |p{0.1\linewidth}|p{0.1\linewidth}|p{0.8\linewidth}|
+
+.. table:: List of available hook libraries
+ :class: longtable
+ :widths: 10 10 80
+
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | Name | Availability | Description |
+ +===========================================================+==============+==============================================================+
+ | :ref:`BOOTP <hooks-bootp>` | Kea open | This hook library adds BOOTP support, as defined in |
+ | | source | RFC 1497. It recognizes received BOOTP requests: |
+ | | | they are translated into DHCPREQUEST packets, put into the |
+ | | | BOOTP client class, and receive infinite lifetime leases. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`Class Commands <hooks-class-cmds>` | ISC support | This hook library allows configured DHCP client classes to |
+ | | customers | be added, updated, deleted, and fetched without |
+ | | | needing to restart the DHCP server. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`Configuration Backend Commands <hooks-cb-cmds>` | ISC support | This hook |
+ | | customers | library implements a collection of commands to manage |
+ | | | Kea configuration information in a |
+ | | | database. This library may only be used in conjunction with |
+ | | | one of the supported Configuration Backend implementations. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`DDNS Tuning <hooks-ddns-tuning>` | ISC premium | This hook library adds custom behaviors related to Dynamic |
+ | | library | DNS updates on a per-client basis. Its primary feature is to |
+ | | | allow the host name used for DNS to be |
+ | | | calculated using an expression. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`Flexible Identifier <hooks-flex-id>` | ISC premium | Kea software provides a way to handle host reservations that |
+ | | library | include addresses, prefixes, options, client classes and |
+ | | | other features. The reservation can be based on hardware |
+ | | | address, DUID, circuit-id, or client-id in DHCPv4 and on |
+ | | | hardware address or DUID in DHCPv6. However, there are |
+ | | | sometimes scenarios where the reservation is more complex, |
+ | | | e.g. uses other options than mentioned above, uses parts of |
+ | | | specific options, or perhaps uses a combination of several |
+ | | | options and fields to uniquely identify a client. Those |
+ | | | scenarios are addressed by the Flexible Identifier hook |
+ | | | application. It allows defining an expression, similar to |
+ | | | the one used in client classification, |
+ | | | e.g. ``substring(relay6[0].option[37],0,6)``. Each incoming |
+ | | | packet is evaluated against that expression and its value is |
+ | | | then searched in the reservations database. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`Flexible Option <hooks-flex-option>` | Kea open | This library provides hooks that compute option values |
+ | | source | instead of static configured values. An expression is |
+ | | | evaluated on the query packet. Defined add, supersede, and |
+ | | | remove actions are applied on the response packet before |
+ | | | it is sent using the evaluation result. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`Forensic Logging <hooks-legal-log>` | ISC premium | This library provides hooks that record a detailed log of |
+ | | library | lease assignments and renewals in a set of log files. 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 a different set of information to be saved, it can |
+ | | | be used as a template or example to create |
+ | | | custom logging hooks. In Kea 1.9.8, additional parameters |
+ | | | were added to give users more flexibility regarding |
+ | | | what information should be logged. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`GSS-TSIG <hooks-gss-tsig>` | ISC support | This hook library adds support to the Kea D2 server |
+ | | customers | (kea-dhcp-ddns) for using GSS-TSIG to sign DNS updates. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`High Availability <hooks-high-availability>` | Kea open | The risk of DHCP service unavailability can be minimized |
+ | | source | by setting up a pair of DHCP servers in a network. Two |
+ | | | modes of operation are supported. The first one is called |
+ | | | load-balancing, and is sometimes referred to as |
+ | | | "active-active." Each server can handle selected groups of |
+ | | | clients in this network, or all clients if it detects that |
+ | | | its partner has become unavailable. It is also possible to |
+ | | | designate one server to serve all DHCP clients, and leave |
+ | | | another server as standby. This mode is called "hot standby" |
+ | | | and is sometimes referred to as "active-passive." This |
+ | | | server activates its DHCP function only when it detects that |
+ | | | its partner is not available. Such cooperation between the |
+ | | | DHCP servers requires that these servers constantly |
+ | | | communicate with each other to send updates about allocated |
+ | | | leases, and to periodically test whether their partners are |
+ | | | still operational. The hook library also provides an ability |
+ | | | to send lease updates to external backup servers, making it |
+ | | | much easier to have a replacement that is up to date. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`Host Cache <hooks-host-cache>` | ISC support | Some database backends, such as RADIUS, |
+ | | customers | may take a long time to respond. Since |
+ | | | Kea in general is synchronous, backend performance |
+ | | | directly affects DHCP performance. To minimize |
+ | | | performance impact, this library |
+ | | | provides a way to cache responses from other hosts. This |
+ | | | includes negative caching, i.e. the ability to remember that |
+ | | | there is no client information in the database. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`Host Commands <hooks-host-cmds>` | ISC premium | Kea provides a way to store host reservations in a |
+ | | library | database. In many larger deployments it is useful to be able |
+ | | | to manage that information while the server is running. This |
+ | | | library provides management commands for adding, querying, |
+ | | | and deleting host reservations in a safe way without |
+ | | | restarting the server. In particular, it validates the |
+ | | | parameters, so an attempt to insert incorrect data, e.g. add |
+ | | | a host with conflicting identifier in the same subnet, is |
+ | | | rejected. Those commands are exposed via the command channel |
+ | | | (JSON over UNIX sockets) and the Control Agent (JSON over |
+ | | | RESTful interface). |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`Lease Commands <hooks-lease-cmds>` | Kea open | This hook library offers a number of commands used to |
+ | | source | manage leases. Kea can store lease information in various |
+ | | | backends: memfile, MySQL, PostgreSQL. This library provides |
+ | | | a unified interface to manipulate leases in an unified, safe |
+ | | | way. In particular, it allows manipulation of memfile leases |
+ | | | while Kea is running, sanity check changes, lease existence |
+ | | | checks, and removal of all leases belonging to a specific |
+ | | | subnet. It can also catch obscure errors, like the addition |
+ | | | of a lease with subnet-id that does not exist in the |
+ | | | configuration, or configuration of a lease to use an address |
+ | | | that is outside of the subnet to which it is supposed to |
+ | | | belong. This library allows easy management of user contexts |
+ | | | associated with leases. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`Leasequery <hooks-lease-query>` | ISC support | This library adds support for DHCPv4 |
+ | | customers | Leasequery as described in RFC 4388; and for DHCPv6 |
+ | | | Leasequery as described in RFC 5007. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`Limits <hooks-limits>` | ISC support | With this hook library, :iscman:`kea-dhcp4` and |
+ | | customers | :iscman:`kea-dhcp6` servers can apply a limit to the rate at |
+ | | | which packets receive a response. The limit can be applied |
+ | | | per-client class or per-subnet. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`MySQL Configuration Backend <hooks-cb-mysql>` | Kea open | This hook library is an implementation of the Kea |
+ | | source | Configuration Backend for MySQL. It uses a MySQL database as |
+ | | | a repository for the Kea configuration information. Kea |
+ | | | servers use this library to fetch their configurations. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`PerfMon <hooks-perfmon>` | Kea open | With this hook library, :iscman:`kea-dhcp4` server and |
+ | | source | :iscman:`kea-dhcp6` servers can track and report performance |
+ | | | data. CURRENTLY UNDER DEVELOPMENT |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`Ping Check <hooks-ping-check>` | ISC support | With this hook library, :iscman:`kea-dhcp4` server can |
+ | | customers | perform ping checks of candidate lease addresses before |
+ | | | offering them to clients. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`PostgreSQL Configuration Backend <hooks-cb-pgsql>` | Kea open | This hook library is an implementation of the Kea |
+ | | source | Configuration Backend for PostgreSQL. It uses a PostgreSQL |
+ | | | database as a repository for the Kea configuration |
+ | | | information. Kea servers use this library to fetch their |
+ | | | configurations. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`RADIUS <hooks-radius>` | ISC support | The RADIUS hook library allows Kea to interact with |
+ | | customers | RADIUS servers using access and accounting mechanisms. The |
+ | | | access mechanism may be used for access control, assigning |
+ | | | specific IPv4 or IPv6 addresses reserved by RADIUS, |
+ | | | dynamically assigning addresses from designated pools chosen |
+ | | | by RADIUS, or rejecting the client's messages altogether. |
+ | | | The accounting mechanism allows a RADIUS server to keep |
+ | | | track of device activity over time. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`RBAC <hooks-rbac>` | ISC support | This hook library adds support to the Kea Control Agent |
+ | | customers | (kea-ctrl-agent) for Role-Based Access Control filtering |
+ | | | of commands. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`Run Script <hooks-run-script>` | Kea open | This hook library adds support to run external |
+ | | source | scripts for specific packet-processing hook points. There |
+ | | | are several exported environment variables available for |
+ | | | the script. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`Statistics Commands <hooks-stat-cmds>` | Kea open | This library provides additional |
+ | | source | commands for retrieving accurate DHCP lease statistics, for |
+ | | | Kea DHCP servers that share the same lease database. This |
+ | | | setup is common in deployments where DHCP service redundancy |
+ | | | is required and a shared lease database is used to avoid |
+ | | | lease-data replication between the DHCP servers. |
+ | | | This hook library returns lease statistics |
+ | | | for each subnet. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`Subnet Commands <hooks-subnet-cmds>` | ISC support | In deployments in which subnet configuration needs to be |
+ | | customers | frequently updated, it is a hard requirement that such |
+ | | | updates be performed without the need for a full DHCP server |
+ | | | reconfiguration or restart. This hook library allows for |
+ | | | incremental changes to the subnet configuration such as |
+ | | | adding or removing a subnet. It also allows for |
+ | | | listing all available subnets and fetching detailed |
+ | | | information about a selected subnet. The commands exposed by |
+ | | | this library do not affect other subnets or configuration |
+ | | | parameters currently used by the server. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+ | :ref:`User Check <hooks-user-chk>` | Kea open | Reads known users list from a file. Unknown users will be |
+ | | source | assigned a lease from the last subnet defined in the |
+ | | | configuration file, e.g. to redirect them to a captive |
+ | | | portal. This demonstrates how an external source of |
+ | | | information can be used to influence the Kea allocation |
+ | | | engine. This hook is part of the Kea source code and is |
+ | | | available in the ``src/hooks/dhcp/user_chk`` directory. |
+ +-----------------------------------------------------------+--------------+--------------------------------------------------------------+
+
+ISC hopes to see more hook libraries become available as time
+progresses, developed both internally and externally. Since this list
+may evolve dynamically, it is maintained on a wiki page, available
+at https://gitlab.isc.org/isc-projects/kea/wikis/Hooks-available.
+Developers or others who are aware of any hook libraries not listed there
+are asked to please send a note to the kea-users or kea-dev mailing lists for
+updating. (Information on all of ISC's public mailing lists can be found
+at https://www.isc.org/mailinglists/.)
+
+The libraries developed by ISC are described in detail in the following
+sections.
+
+.. include:: hooks-bootp.rst
+.. include:: hooks-cb-cmds.rst
+.. include:: hooks-class-cmds.rst
+.. include:: hooks-ddns-tuning.rst
+.. include:: hooks-flex-id.rst
+.. include:: hooks-flex-option.rst
+.. include:: hooks-gss-tsig.rst
+.. include:: hooks-ha.rst
+.. include:: hooks-host-cache.rst
+.. include:: hooks-host-cmds.rst
+.. include:: hooks-lease-cmds.rst
+.. include:: hooks-lease-query.rst
+.. include:: hooks-legal-log.rst
+.. include:: hooks-limits.rst
+.. include:: hooks-cb-mysql.rst
+.. include:: hooks-perfmon.rst
+.. include:: hooks-ping-check.rst
+.. include:: hooks-cb-pgsql.rst
+.. include:: hooks-radius.rst
+.. include:: hooks-rbac.rst
+.. include:: hooks-run-script.rst
+.. include:: hooks-stat-cmds.rst
+.. include:: hooks-subnet-cmds.rst
+.. include:: hooks-user-chk.rst
diff --git a/doc/sphinx/arm/install.rst b/doc/sphinx/arm/install.rst
new file mode 100644
index 0000000..c9b24ea
--- /dev/null
+++ b/doc/sphinx/arm/install.rst
@@ -0,0 +1,633 @@
+.. _installation:
+
+************
+Installation
+************
+
+Packages
+========
+
+ISC publishes native RPM, deb, and APK packages, along with the tarballs
+with the source code. The packages are available on
+`Cloudsmith <https://cloudsmith.io/~isc/repos/>`_ at
+https://cloudsmith.io/~isc/repos. The native packages can be downloaded
+and installed using the system available in a specific distribution (such
+as dpkg or rpm). The Kea repository can also be added to the system,
+making it easier to install updates. For details, please
+go to https://cloudsmith.io/~isc/repos, choose the repository of
+interest, and then click the ``Set Me Up`` button. For detailed
+instructions or refer to ISC `KB article <https://kb.isc.org/docs/isc-kea-packages>`_.
+
+Installation From Cloudsmith Packages
+-------------------------------------
+ISC provides Kea packages for Alpine, Debian, Fedora, RHEL, and Ubuntu.
+The recommended method for installing Kea on any of these systems, from the
+Cloudsmith repository for Kea release 2.3.1 or later, is to install the ``isc-kea``
+metapackage. This metapackage is included on all supported distros and
+installs all of the services offered by the Kea software suite.
+
+Specific Kea components
+can be installed individually, with any of the following packages:
+
+- ``isc-kea-dhcp4`` — Kea DHCPv4 server package
+
+- ``isc-kea-dhcp6`` — Kea DHCPv6 server package
+
+- ``isc-kea-dhcp-ddns`` — Kea DHCP DDNS server
+
+- ``isc-kea-ctrl-agent`` — Kea Control Agent for remote configuration
+
+- ``isc-kea-admin`` — Kea database administration tools
+
+- ``isc-kea-hooks`` — Kea open source DHCP hooks
+
+Kea Premium hook packages are not included in the ``isc-kea-hooks`` package.
+For ISC customers with access to the premium hooks, those packages have the
+``isc-kea-premium-`` prefix.
+
+Once installed, the services can be managed through the distribution's
+service manager. The services are named: :iscman:`kea-dhcp4`, :iscman:`kea-dhcp6`,
+:iscman:`kea-dhcp-ddns`, and :iscman:`kea-ctrl-agent`.
+
+.. note::
+ The real service names on Debian and Ubuntu follow the names of the older
+ packages, to maintain compatibility with pre-existing scripts. A
+ systemd service alias is used to allow users to refer to them with shorter
+ names. Calling ``systemctl enable`` on these services requires
+ the real service names, which are: ``isc-kea-dhcp4-server``,
+ ``isc-kea-dhcp6-server``, ``isc-kea-dhcp-ddns-server``, and
+ ``isc-kea-ctrl-agent``.
+
+Caveats When Upgrading Kea Packages
+-----------------------------------
+
+To upgrade to Kea 2.3.2 or later from an earlier version of Kea on Debian
+and Ubuntu systems, run ``apt dist-upgrade`` instead of the usual ``apt upgrade``.
+Once this upgrade has been completed, it is possible to upgrade to later versions
+normally using ``apt upgrade`` on Debian and Ubuntu systems.
+
+Users may notice differences in the packages distributed in Kea versions prior to
+2.3.2 and those distributed with 2.3.2 and later. As a result of an overhaul of our
+package design with that release, some packages were renamed or removed.
+To ensure that upgrades go as smoothly as possible, pay attention to
+which packages are being removed and installed by the upgrade transaction,
+and ensure that all required packages are reinstalled.
+
+Specifically, there is a possibility for the following packages to be removed
+during the upgrade, depending on which packages were originally installed:
+
+- ``isc-kea-dhcp4``
+
+- ``isc-kea-dhcp6``
+
+- ``isc-kea-dhcp-ddns``
+
+- ``isc-kea-hooks``
+
+To install the entire Kea software suite, simply run
+``apt install isc-kea`` after upgrading, which
+will install all of the relevant subpackages that make up Kea.
+
+This upgrade path issue does not apply to RPM and Alpine systems; however,
+customers with ISC support contracts who experience difficulties with upgrading
+past 2.3.1 are invited to open a ticket in their support queue. Other users
+are encouraged to describe their situation on the kea-users mailing list for
+best-effort support from other list members.
+
+.. _install-hierarchy:
+
+Installation Hierarchy
+======================
+
+The following is the directory layout of the complete Kea installation.
+(All directory paths are relative to the installation directory.)
+
+- ``etc/kea/`` — configuration files.
+
+- ``include/`` — C++ development header files.
+
+- ``lib/`` — libraries.
+
+- ``lib/kea/hooks`` — additional hook libraries.
+
+- ``sbin/`` — server software and commands used by the system administrator.
+
+- ``share/doc/kea/`` — this guide, other supplementary documentation, and examples.
+
+- ``share/kea/`` — API command examples and database schema scripts.
+
+- ``share/man/`` — manual pages (online documentation).
+
+- ``var/lib/kea/`` — server identification and lease database files.
+
+- ``var/log/`` - log files.
+
+- ``var/run/kea`` - PID file and logger lock file.
+
+.. _build-requirements:
+
+Build Requirements
+==================
+
+In addition to the runtime requirements (listed in
+:ref:`required-software`), building Kea from source code requires
+various development include headers and program development tools.
+
+.. note::
+
+ Some operating systems have split their distribution packages into a
+ runtime and a development package. The
+ development package versions, which include header files and
+ libraries, must be installed to build Kea from the source code.
+
+Building from source code requires the following software installed on
+the system:
+
+- Boost C++ libraries (https://www.boost.org/). The oldest Boost version
+ used for testing is 1.57 (although Kea may also work with older
+ versions). The Boost system library must also be installed.
+ Installing a header-only version of Boost is not recommended.
+
+- OpenSSL (at least version 1.0.2) or Botan (at least version 2).
+ OpenSSL version 1.1.1 or later is strongly recommended.
+
+- log4cplus (at least version 1.0.3) development include headers.
+
+- A C++ compiler (with C++14 support) and standard development headers.
+
+- The development tools automake, libtool, and pkg-config.
+
+- The MySQL client and the client development libraries, when using the
+ ``--with-mysql`` configuration flag to build the Kea MySQL database
+ backend. In this case, an instance of the MySQL server running locally
+ or on a machine reachable over a network is required. Note that running
+ the unit tests requires a local MySQL server.
+
+- The PostgreSQL client and the client development libraries, when using the
+ ``--with-pgsql`` configuration flag to build the Kea PostgreSQL database
+ backend. In this case an instance of the PostgreSQL server running locally
+ or on a machine reachable over a network is required. Note that running
+ the unit tests requires a local PostgreSQL server.
+
+- Sysrepo v1.4.140 and libyang v1.0.240 are needed to connect to a Sysrepo
+ datastore. Earlier versions are no longer supported. When compiling from
+ sources, the configure switches that can be used are ``--with-libyang`` and
+ ``--with-sysrepo`` without any parameters. If these dependencies were
+ installed in custom paths, point the switches to them.
+
+- The MIT Kerberos 5 or Heimdal libraries are needed by Kea DDNS server to sign
+ and verify DNS updates using GSS-TSIG. The configuration switch which enables
+ this functionality is ``--with-gssapi`` without any parameters. If these
+ dependencies were installed in custom paths, point the switch to them.
+
+- googletest (version 1.8 or later) is required when using the ``--with-gtest``
+ configuration option to build the unit tests.
+
+- The documentation generation tools `Sphinx <https://www.sphinx-doc.org/>`_,
+ texlive with its extensions, and Doxygen, if using the
+ ``--enable-generate-docs`` configuration option to create the documentation.
+ Specifically, with Fedora, ``python3-sphinx``, ``python3-sphinx_rtd_theme``,
+ ``texlive``, and ``texlive-collection-latexextra`` are necessary.
+ With Ubuntu, ``python3-sphinx``, ``python3-sphinx-rtd-theme``,
+ ``texlive``, and ``texlive-latex-extra`` are needed.
+ If LaTeX packages are missing, Kea skips PDF generation and produces only
+ HTML documents.
+
+Visit ISC's Knowledgebase at https://kb.isc.org/docs/installing-kea for
+system-specific installation tips.
+
+.. _install:
+
+Installation From Source
+========================
+
+Although Kea may be available in pre-compiled, ready-to-use packages
+from operating system vendors, it is open source software written in
+C++. As such, it is freely available in source code form from ISC as a
+downloadable tar file. The source code can also be obtained from the Kea
+GitLab repository at https://gitlab.isc.org/isc-projects/kea. This
+section describes how to build Kea from the source code.
+
+Download Tar File
+-----------------
+
+The Kea release tarballs may be downloaded from:
+https://downloads.isc.org/isc/kea/.
+
+Retrieve From Git
+-----------------
+
+The latest development code is available on GitLab (see
+https://gitlab.isc.org/isc-projects/kea). The Kea source is public and
+development is done in the “master” branch.
+
+Downloading this "bleeding edge" code is recommended only for developers
+or advanced users. Using development code in a production environment is
+not recommended.
+
+.. note::
+
+ When building from source code retrieved via git, additional software
+ is required: automake (v1.11 or later), libtoolize, and autoconf
+ (v2.69 or later). These may need to be installed.
+
+The code can be checked out from
+``https://gitlab.isc.org/isc-projects/kea.git``:
+
+.. code-block:: console
+
+ $ git clone https://gitlab.isc.org/isc-projects/kea.git
+
+The code checked out from the git repository does not include the
+generated configure script or the Makefile.in files, nor their related build
+files. They can be created by running ``autoreconf`` with the
+``--install`` switch. This will run ``autoconf``, ``aclocal``,
+``libtoolize``, ``autoheader``, ``automake``, and related commands.
+
+Write access to the Kea repository is only granted to ISC staff.
+Developers planning to contribute to Kea should check our
+`Contributor's
+Guide <https://gitlab.isc.org/isc-projects/kea/blob/master/contributors-guide.md>`__.
+The `Kea Developer's
+Guide <https://reports.kea.isc.org/dev_guide/>`__ contains more
+information about the process, and describes the requirements for
+contributed code to be accepted by ISC.
+
+.. _configure:
+
+Configure Before the Build
+--------------------------
+
+Kea uses the GNU Build System to discover build environment details. To
+generate the makefiles using the defaults, simply run:
+
+.. code-block:: console
+
+ $ ./configure
+
+Run ``./configure`` with the ``--help`` switch to view the different
+options. Some commonly used options are:
+
+ - ``--prefix``
+ Define the installation location (the default is ``/usr/local``).
+
+ - ``--with-mysql``
+ Build Kea with code to allow it to store leases and host reservations
+ in a MySQL database.
+
+ - ``--with-pgsql``
+ Build Kea with code to allow it to store leases and host reservations
+ in a PostgreSQL database.
+
+ - ``--with-log4cplus``
+ Define the path to find the Log4cplus headers and libraries. Normally
+ this is not necessary.
+
+ - ``--with-boost-include``
+ Define the path to find the Boost headers. Normally this is not
+ necessary.
+
+ - ``--with-botan-config``
+ Specify the path to the botan-config script to build with Botan for
+ cryptographic functions. It is preferable to use OpenSSL (see below).
+
+ - ``--with-openssl``
+ Use the OpenSSL cryptographic library instead of Botan. By default
+ ``configure`` searches for a valid Botan installation; if one is not
+ found, Kea searches for OpenSSL. Normally this is not necessary.
+
+ - ``--enable-shell``
+ Build the optional :iscman:`kea-shell` tool (more in :ref:`kea-shell`).
+ The default is to not build it.
+
+ - ``--with-site-packages``
+ Only useful when :iscman:`kea-shell` is enabled, this switch causes the kea-shell
+ Python packages to be installed in the specified directory. This is
+ mostly useful for Debian-related distributions. While most systems store
+ Python packages in ``${prefix}/usr/lib/pythonX/site-packages``, Debian
+ introduced a separate directory for packages installed from DEB. Such
+ Python packages are expected to be installed in
+ ``/usr/lib/python3/dist-packages``.
+
+ - ``--enable-perfdhcp``
+ Build the optional :iscman:`perfdhcp` DHCP benchmarking tool. The default
+ is to not build it.
+
+.. note::
+
+ For instructions concerning the installation and configuration of
+ database backends for Kea, see :ref:`dhcp-install-configure`.
+
+There are many options that are typically not necessary for
+regular users. However, they may be useful for package maintainers,
+developers, or people who want to extend Kea code or send patches:
+
+ - ``--with-gtest``, ``--with-gtest-source``
+ Enable the building of C++ unit tests using the Google Test
+ framework. This option specifies the path to the gtest source. (If
+ the framework is not installed on the system, it can be downloaded
+ from https://github.com/google/googletest.)
+
+ - ``--enable-generate-docs``
+ Enable the rebuilding of Kea documentation. ISC publishes Kea
+ documentation for each release; however, in some cases it may be
+ desirable to rebuild it: for example, to change something in the
+ docs, or to generate new ones from git sources that are not yet
+ released.
+
+ - ``--enable-generate-parser``
+ Enable the generation of parsers using flex or bison. Kea sources include
+ .cc and .h parser files, pre-generated for users' convenience. By
+ default Kea does not use flex or bison, to avoid
+ requiring installation of unnecessary dependencies for users.
+ However, if anything in the parsers is changed (such as adding a new
+ parameter), flex and bison are required to regenerate
+ parsers. This option permits that.
+
+ - ``--enable-generate-messages``
+ Enable the regeneration of messages files from their messages source
+ files, e.g. regenerate xxx_messages.h and xxx_messages.cc from
+ xxx_messages.mes using the Kea message compiler. By default Kea is
+ built using these .h and .cc files from the distribution. However, if
+ anything in a .mes file is changed (such as adding a new message),
+ the Kea message compiler needs to be built and used. This option
+ permits that.
+
+As an example, the following command configures Kea to find the Boost
+headers in /usr/pkg/include, specifies that PostgreSQL support should be
+enabled, and sets the installation location to /opt/kea:
+
+.. code-block:: console
+
+ $ ./configure \
+ --with-boost-include=/usr/pkg/include \
+ --with-pgsql=/usr/local/bin/pg_config \
+ --prefix=/opt/kea
+
+Users who have any problems with building Kea using the header-only Boost
+code, or who would like to use the Boost system library (assumed for the
+sake of this example to be located in /usr/pkg/lib), should issue these
+commands:
+
+.. code-block:: console
+
+ $ ./configure \
+ --with-boost-libs=-lboost_system \
+ --with-boost-lib-dir=/usr/pkg/lib
+
+If ``configure`` fails, it may be due to missing or old dependencies.
+
+When ``configure`` succeeds, it displays a report with the parameters used
+to build the code. This report is saved into the file ``config.report``
+and is also embedded into the executable binaries, e.g., :iscman:`kea-dhcp4`.
+
+Build
+-----
+
+After the configure step is complete, build the executables from the C++
+code and prepare the Python scripts by running the command:
+
+.. code-block:: console
+
+ $ make
+
+Install
+-------
+
+To install the Kea executables, support files, and documentation, issue
+the command:
+
+.. code-block:: console
+
+ $ make install
+
+Do not use any form of parallel or job server options (such as GNU
+make's ``-j`` option) when performing this step; doing so may cause
+errors.
+
+.. note::
+
+ The install step may require superuser privileges.
+
+If required, run ``ldconfig`` as root with ``/usr/local/lib`` (or with
+prefix/lib if configured with ``--prefix``) in ``/etc/ld.so.conf`` (or the
+relevant linker cache configuration file for the OS):
+
+.. code-block:: console
+
+ $ ldconfig
+
+.. note::
+
+ If ``ldconfig`` is not run where required, users may see
+ errors like the following:
+
+ ::
+
+ program: error while loading shared libraries: libkea-something.so.1:
+ cannot open shared object file: No such file or directory
+
+
+Cross-Building
+--------------
+
+It is possible to cross-build Kea, i.e. to create binaries in a separate
+system (the ``build`` system) from the one where Kea runs
+(the ``host`` system).
+
+It is outside of the scope of common administrator operations and requires
+some developer skills, but the Developer Guide explains how to do that
+using an x86_64 Linux system to build Kea for a Raspberry Pi box running
+Raspbian: `Kea Cross-Compiling Example
+<https://reports.kea.isc.org/dev_guide/de/d9a/crossCompile.html>`__.
+
+.. _dhcp-install-configure:
+
+DHCP Database Installation and Configuration
+============================================
+
+Kea stores its leases in a lease database. The software has been written
+in a way that makes it possible to choose which database product should
+be used to store the lease information. Kea supports three
+database backends: MySQL, PostgreSQL and memfile. To limit external
+dependencies, MySQL and PostgreSQL support are disabled by default and only
+memfile is available. Support for the optional external database backend must
+be explicitly included when Kea is built.
+This section covers the building of Kea with one of the optional backends and
+the creation of the lease database.
+
+.. note::
+
+ When unit tests are built with Kea (i.e. the ``--with-gtest`` configuration
+ option is specified), the databases must be manually pre-configured
+ for the unit tests to run. The details of this configuration can be
+ found in the `Kea Developer's
+ Guide <https://reports.kea.isc.org/dev_guide/>`__.
+
+Building with MySQL Support
+---------------------------
+
+Install MySQL according to the instructions for the system. The client
+development libraries must be installed.
+
+Build and install Kea as described in :ref:`installation`,
+with the following modification. To enable the MySQL database code, at the
+"configure" step (see :ref:`configure`), the ``--with-mysql`` switch should be
+specified:
+
+.. code-block:: console
+
+ $ ./configure [other-options] --with-mysql
+
+If MySQL was not installed in the default location, the location of the
+MySQL configuration program "mysql_config" should be included with the
+switch:
+
+.. code-block:: console
+
+ $ ./configure [other-options] --with-mysql=path-to-mysql_config
+
+See :ref:`mysql-database-create` for details regarding MySQL
+database configuration.
+
+Building with PostgreSQL support
+--------------------------------
+
+Install PostgreSQL according to the instructions for the system. The
+client development libraries must be installed. Client development
+libraries are often packaged as "libpq".
+
+Build and install Kea as described in :ref:`installation`,
+with the following modification. To enable the PostgreSQL database code, at the
+"configure" step (see :ref:`configure`), the ``--with-pgsql`` switch should be
+specified:
+
+.. code-block:: console
+
+ $ ./configure [other-options] --with-pgsql
+
+If PostgreSQL was not installed in the default location, the location of
+the PostgreSQL configuration program "pg_config" should be included with
+the switch:
+
+.. code-block:: console
+
+ $ ./configure [other-options] --with-pgsql=path-to-pg_config
+
+See :ref:`pgsql-database-create` for details regarding PostgreSQL
+database configuration.
+
+
+
+.. include:: hammer.rst
+
+.. _non-root:
+
+Running Kea From a Non-root Account on Linux
+============================================
+
+Both Kea DHCPv4 and DHCPv6 servers perform operations that in general require root access
+privileges. In particular, DHCPv4 opens raw sockets and both DHCPv4 and DHCPv6 open UDP sockets on
+privileged ports. However, with some extra system configuration, it is possible to run Kea from
+non-root accounts.
+
+First, a regular user account must be created:
+
+.. code-block:: console
+
+ useradd admin
+
+Then, change the binaries' ownership and group to the new user. Note that
+the specific path may be different. Please refer to the ``--prefix``
+parameter passed to the configure script:
+
+.. code-block:: console
+
+ chown -R admin /opt/kea
+ chgrp -R admin /opt/kea
+ chown -R admin /var/log/kea-dhcp4.log
+ chgrp -R admin /var/log/kea-dhcp4.log
+ chown -R admin /var/log/kea-dhcp6.log
+ chgrp -R admin /var/log/kea-dhcp6.log
+
+If using systemd, modify its service file
+(e.g. /etc/systemd/system/kea-dhcp6.service):
+
+.. code-block:: console
+
+ User=admin
+ Group=admin
+
+The most important step is to set the capabilities of the binaries. Refer to `man capabilities` to get
+more information.
+
+.. code-block:: console
+
+ setcap 'cap_net_bind_service,cap_net_raw=+ep' /opt/kea/sbin/kea-dhcp4
+ setcap 'cap_net_bind_service=+ep' /opt/kea/sbin/kea-dhcp6
+
+If using systemd, also add this to the service file
+(e.g. /etc/systemd/system/kea-dhcp6.service):
+
+.. code-block:: console
+
+ ExecStartPre=setcap 'cap_net_bind_service=+ep' /opt/kea/sbin/kea-dhcp6
+
+After this step is complete, the admin user should be able to run Kea. Note that the DHCPv4 server by
+default opens raw sockets. If the network is only using relayed traffic, Kea can be instructed to
+use regular UDP sockets (refer to ``dhcp-socket-type`` parameter in the
+:ref:`dhcp4-interface-configuration` section) and the ``cap_net_raw`` capability can be skipped.
+
+.. note::
+
+ It is possible to avoid running Kea with root privileges by instructing Kea to
+ use non-privileged (greater than 1024) ports and redirecting traffic. This, however, only works
+ for relayed traffic. This approach in general is considered experimental and has not been tested
+ for deployment in production environments. Use with caution!
+
+ To use this approach, configure the server to listen on other non-privileged ports (e.g. 1547
+ and 1548) by running the process with the ``-p`` option in ``/etc/systemd/system/kea-dhcp4.service``:
+
+.. code-block:: console
+
+ ExecStart=/opt/kea/sbin/kea-dhcp4 -d -c /etc/kea/kea-dhcp4.conf -p 2067
+
+and ``/etc/systemd/system/kea-dhcp4.service``:
+
+.. code-block:: console
+
+ ExecStart=/opt/kea/sbin/kea-dhcp6 -d -c /etc/kea/kea-dhcp6.conf -p 1547
+
+Then configure port redirection with iptables and ip6tables for new ports (e.g. 1547
+and 1548). Be sure to replace ``ens4`` with the specific interface name.
+
+.. code-block:: console
+
+ iptables -t nat -A PREROUTING -i ens4 -p udp --dport 67 -j REDIRECT --to-port 2067
+ iptables -t nat -A PREROUTING -i ens4 -p udp --dport 2068 -j REDIRECT --to-port 68
+ ip6tables -t nat -A PREROUTING -i ens4 -p udp --dport 547 -j REDIRECT --to-port 1547
+ ip6tables -t nat -A PREROUTING -i ens4 -p udp --dport 1548 -j REDIRECT --to-port 548
+
+.. _deprecated:
+
+Deprecated Features
+===================
+
+This section lists significant features that have been or will be removed. We try to
+deprecate features before removing them to signal
+to current users to plan a migration. New users should not rely on deprecated features.
+
+Sysrepo 0.x or 1.x
+------------------
+
+Kea 2.3.2 introduced support for Sysrepo 2.x. Unfortunately,
+Sysrepo continues to undergo major changes that are backward-incompatible,
+and Kea versions 2.3.2 do not support Sysrepo earlier than versions 2.x.
+
+:isccmd:`libreload` command
+----------------------------------------
+
+The :isccmd:`libreload` command was deprecated in Kea 2.3.4. The code to handle this command is
+still there, but there are reports of it being buggy and not really usable.
+Kea 2.3 and 2.4 versions will produce a warning when this command
+is used, and it will be removed entirely sometime in the 2.5 branch.
diff --git a/doc/sphinx/arm/integrations.rst b/doc/sphinx/arm/integrations.rst
new file mode 100644
index 0000000..57f5734
--- /dev/null
+++ b/doc/sphinx/arm/integrations.rst
@@ -0,0 +1,11 @@
+*********************************
+Integration With External Systems
+*********************************
+
+Kea provides optional support for a variety of external systems, such as RADIUS, NETCONF,
+YANG, and GSS-TSIG. The following sections describe how to compile Kea with those additional
+capabilities and how to configure them.
+
+.. include:: ext-netconf.rst
+.. include:: ext-gss-tsig.rst
+.. include:: ext-radius.rst
diff --git a/doc/sphinx/arm/intro.rst b/doc/sphinx/arm/intro.rst
new file mode 100644
index 0000000..f5c82f5
--- /dev/null
+++ b/doc/sphinx/arm/intro.rst
@@ -0,0 +1,64 @@
+.. _intro:
+
+************
+Introduction
+************
+
+Kea is the next generation of DHCP software, developed by Internet Systems Consortium (ISC). It
+supports both the DHCPv4 and DHCPv6 protocols along with their extensions,
+e.g. prefix delegation and dynamic updates to DNS.
+
+This guide covers Kea version |release|.
+
+For information about supported platforms see :ref:`platforms`.
+
+.. include:: platforms.rst
+
+.. _kea_software:
+
+Kea Software
+============
+
+Kea is a modular DHCP server solution. This modularity is accomplished using multiple
+cooperating processes which, together, provide the server functionality.
+The following software is included with Kea:
+
+- :iscman:`keactrl` — This tool starts, stops, reconfigures, and reports the status of
+ the Kea servers.
+
+- :iscman:`kea-dhcp4` — The DHCPv4 server process. This process responds to
+ DHCPv4 queries from clients.
+
+- :iscman:`kea-dhcp6` — The DHCPv6 server process. This process responds to
+ DHCPv6 queries from clients.
+
+- :iscman:`kea-dhcp-ddns` — The DHCP Dynamic DNS process. This process acts
+ as an intermediary between the DHCP servers and external DNS servers. It
+ receives name update requests from the DHCP servers and sends DNS
+ update messages to the DNS servers.
+
+- :iscman:`kea-admin` — This is a useful tool for database backend maintenance
+ (creating a new database, checking versions, upgrading, etc.).
+
+- :iscman:`kea-lfc` — This process removes redundant information from the
+ files used to provide persistent storage for the memfile database
+ backend. While it can be run standalone, it is normally run as and
+ when required by the Kea DHCP servers.
+
+- :iscman:`kea-ctrl-agent` — The Kea Control Agent (CA) is a daemon that exposes
+ a RESTful control interface for managing Kea servers.
+
+- :iscman:`kea-netconf` - kea-netconf is an agent that provides a
+ YANG/NETCONF interface for configuring Kea.
+
+- :iscman:`kea-shell` — This simple text client uses the REST interface to
+ connect to the Kea Control Agent.
+
+- :iscman:`perfdhcp` — This is a DHCP benchmarking tool which simulates multiple
+ clients to test both DHCPv4 and DHCPv6 server performance.
+
+The tools and modules are covered in full detail in this guide. In
+addition, manual pages are also provided in the default installation.
+
+Kea also provides C++ libraries and programmer interfaces for DHCP.
+These include detailed developer documentation and code examples.
diff --git a/doc/sphinx/arm/keactrl.rst b/doc/sphinx/arm/keactrl.rst
new file mode 100644
index 0000000..93768c3
--- /dev/null
+++ b/doc/sphinx/arm/keactrl.rst
@@ -0,0 +1,364 @@
+.. _keactrl:
+
+***********************************
+Managing Kea with :iscman:`keactrl`
+***********************************
+
+.. _keactrl-overview:
+
+Overview
+========
+
+:iscman:`keactrl` is a shell script which controls the startup, shutdown, and
+reconfiguration of the Kea servers (:iscman:`kea-dhcp4`, :iscman:`kea-dhcp6`,
+:iscman:`kea-dhcp-ddns`, :iscman:`kea-ctrl-agent`, and :iscman:`kea-netconf`). It also
+provides the means for checking the current status of the servers and
+determining the configuration files in use.
+
+:iscman:`keactrl` is available only when Kea is built from sources. When installing
+Kea using native packages, the native ``systemd`` scripts are provided. See
+:ref:`systemd` Section for details.
+
+.. _keactrl-usage:
+
+Command Line Options
+====================
+
+:iscman:`keactrl` is run as follows:
+
+.. code-block:: console
+
+ # keactrl <command> [-c keactrl-config-file] [-s server[,server,...]]
+
+``<command>`` is one of the commands described in :ref:`keactrl-commands`.
+
+The optional ``-c keactrl-config-file`` switch allows specification of
+an alternate :iscman:`keactrl` configuration file. (``--ctrl-config`` is a
+synonym for ``-c``.) In the absence of ``-c``, :iscman:`keactrl` uses the
+default configuration file ``[kea-install-dir]/etc/kea/keactrl.conf``.
+
+The optional ``-s server[,server,...]`` switch selects the servers to
+which the command is issued. (``--server`` is a synonym for ``-s``.) If
+absent, the command is sent to all servers enabled in the :iscman:`keactrl`
+configuration file. If multiple servers are specified, they should be
+separated by commas with no intervening spaces.
+
+.. _keactrl-config-file:
+
+The :iscman:`keactrl` Configuration File
+========================================
+
+Depending on the administrator's requirements, it may not be
+necessary to run all of the available servers.
+The :iscman:`keactrl` configuration file sets which servers are enabled and
+which are disabled. The default configuration file is
+``[kea-install-dir]/etc/kea/keactrl.conf``, but this can be overridden
+on a per-command basis using the ``-c`` switch.
+
+The contents of ``keactrl.conf`` are:
+
+.. code-block:: bash
+
+ # This is a configuration file for keactrl script which controls
+ # the startup, shutdown, reconfiguration and gathering the status
+ # of the Kea processes.
+
+ # prefix holds the location where the Kea is installed.
+ prefix=@prefix@
+
+ # Location of Kea configuration file.
+ kea_dhcp4_config_file=@sysconfdir@/@PACKAGE@/kea-dhcp4.conf
+ kea_dhcp6_config_file=@sysconfdir@/@PACKAGE@/kea-dhcp6.conf
+ kea_dhcp_ddns_config_file=@sysconfdir@/@PACKAGE@/kea-dhcp-ddns.conf
+ kea_ctrl_agent_config_file=@sysconfdir@/@PACKAGE@/kea-ctrl-agent.conf
+ kea_netconf_config_file=@sysconfdir@/@PACKAGE@/kea-netconf.conf
+
+ # Location of Kea binaries.
+ exec_prefix=@exec_prefix@
+ dhcp4_srv=@sbindir@/kea-dhcp4
+ dhcp6_srv=@sbindir@/kea-dhcp6
+ dhcp_ddns_srv=@sbindir@/kea-dhcp-ddns
+ ctrl_agent_srv=@sbindir@/kea-ctrl-agent
+ netconf_srv=@sbindir@/kea-netconf
+
+ # Start DHCPv4 server?
+ dhcp4=yes
+
+ # Start DHCPv6 server?
+ dhcp6=yes
+
+ # Start DHCP DDNS server?
+ dhcp_ddns=no
+
+ # Start Control Agent?
+ ctrl_agent=yes
+
+ # Start Netconf?
+ netconf=no
+
+ # Be verbose?
+ kea_verbose=no
+
+.. note::
+
+ In the example above, strings of the form @something@ are replaced by
+ the appropriate values when Kea is installed.
+
+Setting the ``dhcp4``, ``dhcp6``, ``dhcp_ddns``, ``ctrl_agent``, and ``netconf``
+parameters set to "yes" configures :iscman:`keactrl` to manage (start,
+reconfigure) all servers, i.e. :iscman:`kea-dhcp4`, :iscman:`kea-dhcp6`,
+:iscman:`kea-dhcp-ddns`, :iscman:`kea-ctrl-agent`, and :iscman:`kea-netconf`. When any of
+these parameters is set to "no", :iscman:`keactrl` ignores the
+corresponding server when starting or reconfiguring Kea. Some daemons
+(dhcp_ddns and netconf) are disabled by default.
+
+By default, Kea servers managed by :iscman:`keactrl` are located in
+``[kea-install-dir]/sbin``. This should work for most installations. If
+the default location needs to be altered, the paths
+specified with the ``dhcp4_srv``, ``dhcp6_srv``, ``dhcp_ddns_srv``,
+``ctrl_agent_srv``, and ``netconf_srv`` parameters should be modified.
+
+The ``kea_verbose`` parameter specifies the verbosity of the servers
+being started. When ``kea_verbose`` is set to ``yes``, the logging level of
+the server is set to DEBUG. Modification of the logging severity in a
+configuration file, as described in :ref:`logging`, will have no
+effect as long as ``kea_verbose`` is set to "yes." Setting it to
+"no" causes the server to use the logging levels specified in the
+Kea configuration file. If no logging configuration is specified, the
+default settings are used.
+
+.. note::
+
+ The verbosity for the server is set when it is started. Once started,
+ the verbosity can only be changed by stopping the server and starting
+ it again with the new value of the ``kea_verbose`` parameter.
+
+.. _keactrl-commands:
+
+Commands
+========
+
+The following commands are supported by :iscman:`keactrl`:
+
+- ``start`` - starts the selected servers.
+
+- ``stop`` - stops all running servers.
+
+- ``reload`` - triggers reconfiguration of the selected servers by
+ sending the SIGHUP signal to them.
+
+- ``status`` - returns the status of the servers (active or inactive)
+ and the names of the configuration files in use.
+
+- ``version`` - prints out the version of the :iscman:`keactrl` tool itself,
+ together with the versions of the Kea daemons.
+
+Typical output from :iscman:`keactrl` when starting the servers looks similar
+to the following:
+
+.. code-block:: console
+
+ $ keactrl start
+ INFO/keactrl: Starting kea-dhcp4 -c /usr/local/etc/kea/kea-dhcp4.conf -d
+ INFO/keactrl: Starting kea-dhcp6 -c /usr/local/etc/kea/kea-dhcp6.conf -d
+ INFO/keactrl: Starting kea-dhcp-ddns -c /usr/local/etc/kea/kea-dhcp-ddns.conf -d
+ INFO/keactrl: Starting kea-ctrl-agent -c /usr/local/etc/kea/kea-ctrl-agent.conf -d
+ INFO/keactrl: Starting kea-netconf -c /usr/local/etc/kea/kea-netconf.conf -d
+
+Kea's servers create PID files upon startup. These files are used by
+:iscman:`keactrl` to determine whether a given server is running. If one or more
+servers are running when the start command is issued, the output
+looks similar to the following:
+
+.. code-block:: console
+
+ $ keactrl start
+ INFO/keactrl: kea-dhcp4 appears to be running, see: PID 10918, PID file: /usr/local/var/run/kea/kea.kea-dhcp4.pid.
+ INFO/keactrl: kea-dhcp6 appears to be running, see: PID 10924, PID file: /usr/local/var/run/kea/kea.kea-dhcp6.pid.
+ INFO/keactrl: kea-dhcp-ddns appears to be running, see: PID 10930, PID file: /usr/local/var/run/kea/kea.kea-dhcp-ddns.pid.
+ INFO/keactrl: kea-ctrl-agent appears to be running, see: PID 10931, PID file: /usr/local/var/run/kea/kea.kea-ctrl-agent.pid.
+ INFO/keactrl: kea-netconf appears to be running, see: PID 10123, PID file: /usr/local/var/run/kea/kea.kea-netconf.pid.
+
+During normal shutdowns, these PID files are deleted; they may, however,
+be left over as remnants following a system crash. It is possible,
+though highly unlikely, that upon system restart the PIDs they contain
+may actually refer to processes unrelated to Kea. This condition will
+cause :iscman:`keactrl` to decide that the servers are running, when in fact they
+are not. In such a case the PID files listed in the :iscman:`keactrl` output
+must be manually deleted.
+
+The following command stops all servers:
+
+.. code-block:: console
+
+ $ keactrl stop
+ INFO/keactrl: Stopping kea-dhcp4...
+ INFO/keactrl: Stopping kea-dhcp6...
+ INFO/keactrl: Stopping kea-dhcp-ddns...
+ INFO/keactrl: Stopping kea-ctrl-agent...
+ INFO/keactrl: Stopping kea-netconf...
+
+Note that the ``stop`` command attempts to stop all servers
+regardless of whether they are "enabled" in ``keactrl.conf``. If any
+of the servers are not running, an informational message is displayed as
+in the ``stop`` command output below.
+
+.. code-block:: console
+
+ $ keactrl stop
+ INFO/keactrl: kea-dhcp4 isn't running.
+ INFO/keactrl: kea-dhcp6 isn't running.
+ INFO/keactrl: kea-dhcp-ddns isn't running.
+ INFO/keactrl: kea-ctrl-agent isn't running.
+ INFO/keactrl: kea-netconf isn't running.
+
+As already mentioned, the reconfiguration of each Kea server is
+triggered by the SIGHUP signal. The ``reload`` command sends the SIGHUP
+signal to any servers that are enabled in the :iscman:`keactrl` configuration
+file and that are currently running. When a server receives the SIGHUP signal
+it rereads its configuration file and, if the new configuration is
+valid, uses the new configuration.
+If the new configuration proves to be invalid, the server retains its
+current configuration; however, in some cases a fatal error message is logged
+indicating that the server is no longer providing any service: a working
+configuration must be loaded as soon as possible.
+
+A reload is executed as follows:
+
+.. code-block:: console
+
+ $ keactrl reload
+ INFO/keactrl: Reloading kea-dhcp4...
+ INFO/keactrl: Reloading kea-dhcp6...
+ INFO/keactrl: Reloading kea-dhcp-ddns...
+ INFO/keactrl: Reloading kea-ctrl-agent...
+
+If any of the servers are not running, an informational message is
+displayed as in the ``reload`` command output below.
+:iscman:`kea-netconf` does not support the SIGHUP signal. If its
+configuration has changed, please stop and restart it for the change to
+take effect.
+
+.. code-block:: console
+
+ $ keactrl stop
+ INFO/keactrl: kea-dhcp4 isn't running.
+ INFO/keactrl: kea-dhcp6 isn't running.
+ INFO/keactrl: kea-dhcp-ddns isn't running.
+ INFO/keactrl: kea-ctrl-agent isn't running.
+ INFO/keactrl: kea-netconf isn't running.
+
+.. note::
+
+ NETCONF is an optional feature that is disabled by default and can be
+ enabled during compilation. If Kea was compiled without NETCONF
+ support, :iscman:`keactrl` does not provide
+ information about it. The NETCONF entries are still present in
+ the ``keactrl.conf`` file, but NETCONF status is not shown and other
+ commands ignore it.
+
+.. note::
+
+ Currently :iscman:`keactrl` does not report configuration failures when the
+ server is started or reconfigured. To check if the server's
+ configuration succeeded, the Kea log must be examined for errors. By
+ default, the log is written to the `syslog` file.
+
+Sometimes it is useful to check which servers are running. The
+``status`` command reports this, with typical output that looks like:
+
+.. code-block:: console
+
+ $ keactrl status
+ DHCPv4 server: active
+ DHCPv6 server: inactive
+ DHCP DDNS: active
+ Control Agent: active
+ Netconf agent: inactive
+ Kea configuration file: /usr/local/etc/kea/kea.conf
+ Kea DHCPv4 configuration file: /usr/local/etc/kea/kea-dhcp4.conf
+ Kea DHCPv6 configuration file: /usr/local/etc/kea/kea-dhcp6.conf
+ Kea DHCP DDNS configuration file: /usr/local/etc/kea/kea-dhcp-ddns.conf
+ Kea Control Agent configuration file: /usr/local/etc/kea/kea-ctrl-agent.conf
+ Kea Netconf configuration file: /usr/local/etc/kea/kea-netconf.conf
+ keactrl configuration file: /usr/local/etc/kea/keactrl.conf
+
+``keactrl status`` offers basic reporting capabilities. For more extensive insight
+into Kea's health and status, consider deploying Stork. For details, see :ref:`stork`.
+
+.. _keactrl-overriding-servers:
+
+Overriding the Server Selection
+===============================
+
+The optional ``-s`` switch allows the selection of the server(s) to which
+the :iscman:`keactrl` command is issued. For example, the following instructs
+:iscman:`keactrl` to stop the :iscman:`kea-dhcp4` and :iscman:`kea-dhcp6` servers and
+leave the :iscman:`kea-dhcp-ddns` and :iscman:`kea-ctrl-agent` running:
+
+.. code-block:: console
+
+ $ keactrl stop -s dhcp4,dhcp6
+
+Similarly, the following starts only the :iscman:`kea-dhcp4` and
+:iscman:`kea-dhcp-ddns` servers, but not :iscman:`kea-dhcp6` or :iscman:`kea-ctrl-agent`.
+
+.. code-block:: console
+
+ $ keactrl start -s dhcp4,dhcp_ddns
+
+Note that the behavior of the ``-s`` switch with the ``start`` and
+``reload`` commands is different from its behavior with the ``stop``
+command. On ``start`` and ``reload``, :iscman:`keactrl` checks whether the
+servers given as parameters to the ``-s`` switch are enabled in the
+:iscman:`keactrl` configuration file; if not, the server is ignored. For
+``stop``, however, this check is not made; the command is applied to all
+listed servers, regardless of whether they have been enabled in the
+file.
+
+The following keywords can be used with the ``-s`` command-line option:
+
+- ``dhcp4`` for :iscman:`kea-dhcp4`.
+
+- ``dhcp6`` for :iscman:`kea-dhcp6`.
+
+- ``dhcp_ddns`` for :iscman:`kea-dhcp-ddns`.
+
+- ``ctrl_agent`` for :iscman:`kea-ctrl-agent`.
+
+- ``netconf`` for :iscman:`kea-netconf`.
+
+- ``all`` for all servers (default).
+
+.. _systemd:
+
+Native Packages and ``systemd``
+===============================
+
+:iscman:`keactrl` is a script that was developed to assist in managing Kea processes.
+However, all modern operating systems have their own process-management scripts,
+such as ``systemd``. In general, these native scripts should be used,
+as they have several advantages. ``systemd`` scripts handle processes in a uniform
+way, so Kea is handled in a similar fashion to HTTP or a mail
+server. Second and more importantly, ``systemd`` allows dependencies to be defined
+between services. For example, it is easy to specify that the Kea server should not start
+until the network interfaces are operational. Using native scripts also has other benefits, such as
+the ability to enable or disable services using commands, and the ability to temporarily start a disabled
+service.
+
+Thus, it is recommended to use ``systemctl`` commands if they are available. Native
+Kea packages do not provide :iscman:`keactrl`; ``systemctl`` service definitions are
+provided instead. Consult the system documentation for details.
+
+Briefly, here are example commands to check status, start, stop, and restart various Kea daemons:
+
+.. code-block:: console
+
+ # systemctl status kea-ctrl-agent
+ # systemctl start kea-dhcp4
+ # systemctl stop kea-dhcp6
+ # systemctl restart kea-dhcp-ddns
+
+Note that the service names may be slightly different between Linux distributions; in general,
+we have followed the naming conventions in third-party packages. In particular,
+some systems may not have the `isc-` prefix.
diff --git a/doc/sphinx/arm/lease-expiration.rst b/doc/sphinx/arm/lease-expiration.rst
new file mode 100644
index 0000000..a6ebd51
--- /dev/null
+++ b/doc/sphinx/arm/lease-expiration.rst
@@ -0,0 +1,328 @@
+.. _lease-expiration:
+
+****************
+Lease Expiration
+****************
+
+The primary role of the DHCP server is to assign addresses and/or
+delegate prefixes to DHCP clients. These addresses and prefixes are
+often referred to as "leases." Leases are typically assigned to clients
+for a finite amount of time, known as the "valid lifetime." DHCP clients
+who wish to continue using their assigned leases periodically renew
+them by sending the appropriate message to the DHCP server. The DHCP
+server records the time when these leases are renewed and calculates new
+expiration times for them.
+
+If the client does not renew a lease before its valid lifetime elapses,
+the lease is considered expired. There are many situations when the
+client may cease lease renewals; common scenarios include when the machine
+running the client shuts down for an extended period of time, or when a
+mobile device leaves the vicinity of a network.
+
+The process through which the DHCP server makes expired leases available
+for reassignment is referred to as "lease reclamation," and expired
+leases returned to availability through this process are referred to as
+"reclaimed." The DHCP server attempts to reclaim an expired lease as
+soon as it detects that it has expired. The server has several possible
+ways to detect expiration: it may attempt to allocate a lease to a
+client but find this lease already present in the database and expired;
+or it can periodically query the lease database for expired leases.
+Regardless of how an expired lease is detected, it must be reclaimed
+before it can be assigned to a client.
+
+This chapter explains how to configure the server to periodically query
+for the expired leases, and how to minimize the impact of the periodic
+lease-reclamation process on the server's responsiveness. Finally, it
+explains "lease affinity," which provides the means to assign the same
+lease to a returning client after its lease has expired.
+
+Although all configuration examples in this section are provided for the
+DHCPv4 server, the same parameters may be used for DHCPv6 server
+configuration.
+
+.. _lease-reclamation:
+
+Lease Reclamation
+=================
+
+Lease reclamation is the process through which an expired lease becomes
+available for assignment to the same or a different client. This process
+involves the following steps for each reclaimed lease:
+
+- Invoke callouts for the ``lease4_expire`` or ``lease6_expire`` hook
+ points, if hook libraries supporting those callouts are currently
+ loaded.
+
+- Update the DNS, i.e. remove any DNS entries associated with the
+ expired lease.
+
+- Update lease information in the lease database to indicate that the
+ lease is now available for reassignment.
+
+- Update counters on the server, a process that includes increasing the
+ number of reclaimed leases and decreasing the number of assigned
+ addresses or delegated prefixes.
+
+Please refer to :ref:`dhcp-ddns-server` to see how to configure DNS
+updates in Kea, and to :ref:`hooks-libraries` for information about
+using hook libraries.
+
+.. _lease-reclamation-defaults:
+
+Lease Reclamation Configuration Parameters
+==========================================
+
+The following list presents all the configuration parameters pertaining to
+processing expired leases, with their default values:
+
+- ``reclaim-timer-wait-time`` - this parameter governs intervals
+ between the completion of the previous reclamation cycle and the start of the
+ next one. Specified in seconds; the default value is 10.
+
+- ``flush-reclaimed-timer-wait-time`` - this parameter controls how
+ often the server initiates the lease reclamation procedure. Expressed in
+ seconds; the default value is 25. If both ``flush-reclaimed-timer-wait-time``
+ and ``hold-reclaimed-time`` are not 0, when the client sends a release
+ message the lease is expired instead of being deleted from lease storage.
+
+- ``hold-reclaimed-time`` - this parameter governs how long the lease
+ should be kept after it is reclaimed. This enables lease affinity
+ when set to a non-zero value. Expressed in seconds; the default value
+ is 3600. If both ``flush-reclaimed-timer-wait-time`` and
+ ``hold-reclaimed-time`` are not 0, when the client sends a release message
+ the lease is expired instead of being deleted from lease storage.
+
+- ``max-reclaim-leases`` - this parameter specifies the maximum number
+ of reclaimed leases that can be processed at one time. Zero means
+ unlimited (i.e. process all reclaimed leases). The default value is
+ 100.
+
+- ``max-reclaim-time`` - this parameter specifies an upper limit to the
+ length of time a lease reclamation procedure can take. Zero means no time
+ limit. Expressed in milliseconds; the default value is 250.
+
+- ``unwarned-reclaim-cycles`` - if lease reclamation limits are
+ specified (``max-reclaim-leases`` and/or ``max-reclaim-time``), then
+ under certain circumstances the server may not be able to deal with
+ the leases to be reclaimed fast enough. This parameter specifies how many
+ consecutive clean-up cycles must end with remaining leases to be
+ processed before a warning is printed. The default is 5 cycles.
+
+The parameters are explained in more detail in the rest of this chapter.
+
+The default value for any parameter is used when the parameter is not
+explicitly specified in the configuration. If the
+``expired-leases-processing`` map is omitted entirely in the
+configuration, the default values are used for all
+parameters listed above.
+
+.. _lease-reclaim-config:
+
+Configuring Lease Reclamation
+=============================
+
+Kea can be configured to periodically detect and reclaim expired leases.
+During this process the lease entries in the database are modified or
+removed. While this is happening the server does not process incoming
+DHCP messages, to avoid issues with concurrent access to database
+information. As a result, the server is unresponsive while lease
+reclamation is performed and DHCP queries will accumulate; responses
+will be sent once the lease-reclamation cycle is complete.
+
+In deployments where response time is critical, administrators may wish
+to minimize the interruptions in service caused by lease reclamation.
+To this end, Kea provides configuration parameters to control the
+frequency of lease reclamation cycles, the maximum number of leases
+processed in a single reclamation cycle, and the maximum amount of time
+a single reclamation cycle is allowed to run before being interrupted.
+The following examples demonstrate how these parameters can be used:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "expired-leases-processing": {
+ "reclaim-timer-wait-time": 5,
+ "max-reclaim-leases": 0,
+ "max-reclaim-time": 0
+ }
+ }
+ }
+
+The first parameter is expressed in seconds and specifies an interval
+between the two consecutive lease reclamation cycles. This is explained
+by the following diagram:
+
+::
+
+
+ | c1 | | c2 | |c3| | c4 |
+ |<---->|<---------->|<-->|<---------->|<>|<---------->|<-->|<--
+ ------------------------------------------------------------------>
+ | | 5s | | 5s | | 5s | | time
+
+This diagram shows four lease-reclamation cycles (c1 through c4) of
+variable duration. The duration of the reclamation cycle
+depends on the number of expired leases detected and processed in a
+particular cycle. This duration is usually significantly shorter than
+the interval between the cycles.
+
+According to the ``reclaim-timer-wait-time``, the server keeps fixed
+intervals of five seconds between the end of one cycle and the start of
+the next cycle. This guarantees the presence of 5-second-long periods during
+which the server remains responsive to DHCP queries and does not perform
+lease reclamation. The ``max-reclaim-leases`` and ``max-reclaim-time``
+are set to 0, which sets no restriction on the maximum number of leases
+reclaimed in the particular cycle, or on the maximum duration of each
+cycle.
+
+In deployments with high lease-pool utilization, relatively short valid
+lifetimes, and frequently disconnecting clients which allow leases to
+expire, the number of expired leases requiring reclamation at any given
+time may rise significantly. In this case, it is often desirable to
+apply restrictions to the maximum duration of a reclamation cycle or the
+maximum number of leases reclaimed in a cycle. The following
+configuration demonstrates how this can be done:
+
+.. code-block:: json
+
+ {
+ "Dhcp4": {
+ "expired-leases-processing": {
+ "reclaim-timer-wait-time": 3,
+ "max-reclaim-leases": 100,
+ "max-reclaim-time": 50,
+ "unwarned-reclaim-cycles": 10
+ }
+ }
+ }
+
+In this example, the ``max-reclaim-leases`` parameter limits the number of leases
+reclaimed in a single cycle to 100, and the ``max-reclaim-time`` limits the
+maximum duration of each cycle to 50ms. The lease-reclamation cycle will
+be interrupted if either of these limitations is reached. The
+reclamation of any unreclaimed leases will be attempted in subsequent
+cycles.
+
+The following diagram illustrates the behavior of the system in the
+presence of many expired leases, when the limits are applied for the
+reclamation cycles:
+
+::
+
+
+ | c1 | | c2 | | c3 | | c4 |
+ |<-->|<-------------->|<-->|<-------------->|<-->|<-------------->|<-->|<--
+ ------------------------------------------------------------------------------>
+ |50ms| 3s |50ms| 3s |50ms| 3s |50ms| time
+
+In this case, if any reclamation cycle takes
+more than 50ms, it is interrupted according to the value of the
+``max-reclaim-time``. This results in equal durations of all reclamation
+cycles over time. In this example, the limitation of the
+maximum 100 leases is not reached. This may be the case when database
+transactions or callouts in the hook libraries attached to the
+server are slow. Regardless, the chosen values for either the maximum
+number of leases or a maximum cycle time strongly depend on the
+particular deployment, the lease database backend being used, any
+hook libraries, etc. Administrators may need to experiment to tune the
+system to suit the dynamics of their deployment.
+
+It is important to realize that with the use of these limits, there is a
+risk that expired leases will accumulate faster than the server can
+reclaim them. This should not be a problem if the server is dealing with
+a temporary burst of expirations, because it should be able to
+eventually deal with them over time. However, if leases expire at a high
+rate for a long period of time, the unreclaimed leases will pile up in
+the database. To notify the administrator that the current configuration
+does not satisfy the needs for reclamation of expired leases, the server
+issues a warning message in the log if it is unable to reclaim all
+leases within several reclamation cycles. The number of cycles after
+which such a warning is issued is specified with the
+``unwarned-reclaim-cycles`` configuration parameter.
+
+Setting the ``reclaim-timer-wait-time`` to 0 disables periodic
+reclamation of the expired leases.
+
+.. _lease-affinity:
+
+Configuring Lease Affinity
+==========================
+
+Suppose that a laptop goes into sleep mode after a period of user
+inactivity. While the laptop is in sleep mode, its DHCP client does not
+renew leases obtained from the server and these leases will eventually
+expire. When the laptop wakes up, it is often desirable for it to
+continue using its previous assigned IP addresses. To facilitate this,
+the server needs to correlate returning clients with their expired
+leases. When the client returns, the server first checks for those
+leases and reassigns them if they have not been assigned to another
+client. The ability of the server to reassign the same lease to a
+returning client is referred to as "lease affinity."
+
+When lease affinity is enabled (i.e. when ``hold-reclaimed-time`` is configured
+to a value greater than zero), the server still reclaims leases according to the
+parameters described in :ref:`lease-reclaim-config`, but the reclaimed leases
+are held in the database for a specified amount of time rather than removed.
+If both ``flush-reclaimed-timer-wait-time`` and ``hold-reclaimed-time`` are
+greater than zero, the lease is expired immediately when the client sends a
+release message, instead of being deleted from lease storage. When the client
+returns, the server first verifies whether there are any reclaimed leases
+associated with this client and then reassigns them if possible. However, it is
+important to note that any reclaimed lease may be assigned to another client if
+that client specifically asks for it. Therefore, lease affinity does not
+guarantee that the reclaimed lease will be available for the client who used it
+before; it merely increases the chances of the client being assigned the same
+lease. If the lease pool is small - namely, in DHCPv4, for which address space
+is limited - there is an increased likelihood that the expired lease will be
+assigned to another client.
+
+Consider the following configuration:
+
+::
+
+ "Dhcp4": {
+ "expired-leases-processing": {
+ "reclaim-timer-wait-time": 3,
+ "hold-reclaimed-time": 1800,
+ "flush-reclaimed-timer-wait-time": 5
+ },
+ ...
+ }
+
+The ``hold-reclaim-time`` specifies how many seconds after an expiration
+a reclaimed lease should be held in the database for reassignment to
+the same client. In the example given above, reclaimed leases are
+held for 30 minutes (1800 seconds) after their expiration. During this time,
+the server will likely be able to reassign the same lease to the
+returning client, unless another client specifically requests this lease and the
+server assigns it.
+
+The server must periodically remove reclaimed leases for which the time
+indicated by ``hold-reclaim-time`` has elapsed. The
+``flush-reclaimed-timer-wait-time`` parameter controls how often the
+server removes such leases. In the example provided above, the server
+initiates removal of such leases five seconds after the previous
+removal attempt was completed. Setting this value to 0 disables lease
+affinity, meaning leases are removed from the lease database
+when they are reclaimed. If lease affinity is enabled, it is recommended
+that the ``hold-reclaim-time`` be set to a value significantly higher than
+the ``reclaim-timer-wait-time``, as timely removal of expired-reclaimed
+leases is less critical than the removal process, which may impact
+server responsiveness.
+
+There is no guarantee that lease affinity will work every time; if a
+server is running out of addresses, it will reassign expired addresses
+to new clients. Also, clients can request specific addresses and the
+server tries to honor such requests if possible. Administrators who want to
+ensure a client keeps its address, even after periods of inactivity,
+should consider using host reservations or leases with very long lifetimes.
+
+.. _leases-reclamation-using-command:
+
+Reclaiming Expired Leases via Command
+=====================================
+
+The :isccmd:`leases-reclaim` command can be used to trigger lease reclamation at
+any time. Please consult the :ref:`command-leases-reclaim` section
+for details about using this command.
diff --git a/doc/sphinx/arm/lfc.rst b/doc/sphinx/arm/lfc.rst
new file mode 100644
index 0000000..f28756a
--- /dev/null
+++ b/doc/sphinx/arm/lfc.rst
@@ -0,0 +1,73 @@
+.. _kea-lfc:
+
+***************
+The LFC Process
+***************
+
+.. _kea-lfc-overview:
+
+Overview
+========
+
+:iscman:`kea-lfc` is a service process that removes redundant information from
+the files used to provide persistent storage for the memfile database
+backend. This service is written to run as a standalone process.
+
+While :iscman:`kea-lfc` can be started externally, there is usually no need to
+do so. :iscman:`kea-lfc` is run on a periodic basis by the Kea DHCP servers.
+
+The process operates on a set of files, using them to receive input and
+output of the lease entries and to indicate what stage the process is
+in, in the event of an interruption. Currently the caller must supply
+names for all of the files.
+
+.. _kea-lfc-usage:
+
+Command-Line Options
+====================
+
+:iscman:`kea-lfc` is run as follows:
+
+::
+
+ kea-lfc [-4 | -6] -c config-file -p pid-file -x previous-file -i copy-file -o output-file -f finish-file
+
+The argument ``-4`` or ``-6`` selects the protocol version of the lease
+files.
+
+The ``-c`` argument specifies the configuration file. This is required,
+but is not currently used by the process.
+
+The ``-p`` argument specifies the PID file. When the :iscman:`kea-lfc` process
+starts, it attempts to determine whether another instance of the process
+is already running by examining the PID file. If one is already running,
+the new process is terminated; if one is not running, Kea writes its PID
+into the PID file.
+
+The other filenames specify where the :iscman:`kea-lfc` process should look
+for input, write its output, and perform its bookkeeping:
+
+- ``previous`` — when :iscman:`kea-lfc` starts, this is the result of any
+ previous run of :iscman:`kea-lfc`. When :iscman:`kea-lfc` finishes, it is the
+ result of this run. If :iscman:`kea-lfc` is interrupted before completing,
+ this file may not exist.
+
+- ``input`` — before the DHCP server invokes :iscman:`kea-lfc`, it moves
+ the current lease file here and then calls :iscman:`kea-lfc` with this file.
+
+- ``output`` — this is the temporary file where :iscman:`kea-lfc` writes the
+ leases. Once the file has finished writing, it is moved to the
+ ``finish`` file (see below).
+
+- ``finish`` — this is another temporary file :iscman:`kea-lfc` uses for
+ bookkeeping. When :iscman:`kea-lfc` completes writing the ``output`` file, it
+ moves the contents to the file of this name. After :iscman:`kea-lfc` finishes deleting the
+ other files (``previous`` and ``input``), it moves this file to the ``previous``
+ lease file. By moving the files in this fashion, :iscman:`kea-lfc` and
+ the DHCP server processes can determine the correct file to use even
+ if one of the processes is interrupted before completing its task.
+
+There are several additional arguments, mostly for debugging purposes.
+``-d`` sets the logging level to debug. ``-v`` and ``-V`` print out
+version stamps, with ``-V`` providing a longer form. ``-h`` prints out
+the usage string.
diff --git a/doc/sphinx/arm/logging.rst b/doc/sphinx/arm/logging.rst
new file mode 100644
index 0000000..897cf9e
--- /dev/null
+++ b/doc/sphinx/arm/logging.rst
@@ -0,0 +1,1077 @@
+.. _logging:
+
+*******
+Logging
+*******
+
+Logging Configuration
+=====================
+
+During its operation Kea may produce many log messages. They differ in
+severity (some are more important than others) and source (different
+components, like hooks, produce different messages). It is useful to
+understand which log messages are critical and which are not, and to
+configure logging appropriately. For example, debug-level messages
+can be safely ignored in a typical deployment. They are, however, very
+useful when debugging a problem.
+
+The logging system in Kea is configured through the ``loggers`` entry in the
+server section of the configuration file.
+
+Loggers
+-------
+
+Within Kea, a message is logged through an entity called a "logger."
+Different components log messages through different loggers, and each
+logger can be configured independently of the others. Some components,
+in particular the DHCP server processes, may use multiple loggers to log
+messages pertaining to different logical functions of the component. For
+example, the DHCPv4 server uses one logger for messages about packet
+reception and transmission, another logger for messages related to lease
+allocation, and so on. Some of the libraries used by the Kea server,
+such as libdhcpsrv, use their own loggers.
+
+Users implementing hook libraries (code attached to the server at
+runtime) are responsible for creating the loggers used by those
+libraries. Such loggers should have unique names, different from the
+logger names used by Kea. That way, the messages produced by the hook
+library can be distinguished from messages issued by the core Kea code.
+Unique names also allow the hook loggers to be configured independently of
+loggers used by Kea. Whenever it makes sense, a hook library can use
+multiple loggers to log messages pertaining to different logical parts
+of the library.
+
+In the server section of a configuration file, the
+configuration for zero or more loggers (including loggers used by the
+proprietary hook libraries) can be specified. If there are no loggers specified, the
+code uses default values; these cause Kea to log messages of INFO
+severity or greater to standard output. There is a small time window
+after Kea has been started but before it has read its configuration;
+logging in this short period can be controlled using environment
+variables. For details, see :ref:`logging-during-startup`.
+
+The three main elements of a logger configuration are: ``name`` (the
+component that is generating the messages), ``severity`` (what to log),
+and ``output_commands`` (where to log). There is also a ``debuglevel``
+element, which is only relevant if debug-level logging has been
+selected.
+
+The ``name`` (string) Logger
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Each logger in the system has a name: that of the component binary file
+using it to log messages. For instance, to configure logging
+for the DHCPv4 server, add an entry for a logger named “kea-dhcp4”.
+This configuration will then be used by the loggers in the DHCPv4
+server and all the libraries used by it, unless a library defines its
+own logger and there is a specific logger configuration that applies to
+that logger.
+
+When tracking down an issue with the server's operation, use of DEBUG
+logging is required to obtain the verbose output needed for problem
+diagnosis. However, the high verbosity is likely to overwhelm the
+logging system in cases where the server is processing high-volume
+traffic. To mitigate this problem, Kea can use multiple loggers, for
+different functional parts of the server, that can each be configured
+independently. If the user is reasonably confident that a problem
+originates in a specific function of the server, or that the problem is
+related to a specific type of operation, they may enable high verbosity
+only for the relevant logger, thereby limiting the DEBUG messages to the
+required minimum.
+
+The loggers are associated with a particular library or binary of Kea.
+However, each library or binary may (and usually does) include multiple
+loggers. For example, the DHCPv4 server binary contains separate loggers
+for packet parsing, dropped packets, callouts, etc.
+
+The loggers form a hierarchy. For each program in Kea, there is a "root"
+logger, named after the program (e.g. the root logger for :iscman:`kea-dhcp4`, the
+DHCPv4 server, is named :iscman:`kea-dhcp4`). All other loggers are children of
+this logger and are named accordingly, e.g. the allocation engine in the
+DHCPv4 server logs messages using a logger called
+``kea-dhcp4.alloc-engine``.
+
+This relationship is important, as each child logger derives its default
+configuration from its parent root logger. In the typical case, the root
+logger configuration is the only logging configuration specified in the
+configuration file and so applies to all loggers. If an entry is made
+for a given logger, any attributes specified override those of the root
+logger, whereas any not specified are inherited from it.
+
+To illustrate this, suppose we are using the DHCPv4 server with the
+root logger :iscman:`kea-dhcp4` logging at the INFO level. In order to enable
+DEBUG verbosity for DHCPv4 packet drops, we must create a configuration
+entry for the logger with ``"name": "kea-dhcp4.bad-packets”``,
+``"severity": "DEBUG"``, and an explicit debug level. All other configuration
+parameters may be omitted for this logger if it should use the default values
+specified in the root logger's configuration.
+
+``debuglevel`` is inherited only if ``severity`` is missing as well. For
+predictable results, if ``severity`` is ``"DEBUG"``, these two attributes
+should always be explicitly specified or omitted together. An entry with an
+explicit ``"DEBUG"`` severity does not inherit ``debuglevel`` from the root
+logger and defaults to ``0`` if missing, resulting in no debug messages
+being logged. This is a consequence of relying on the log4cplus inheritance
+mechanism.
+
+If there are multiple logger specifications in the configuration that
+might match a particular logger, the specification with the more
+specific logger name takes precedence. For example, if there are entries
+for both :iscman:`kea-dhcp4` and ``kea-dhcp4.dhcpsrv``, the main DHCPv4 server
+program — and all libraries it uses other than the ``dhcpsrv`` library
+(libdhcpsrv) — logs messages according to the configuration in the
+first entry (:iscman:`kea-dhcp4`). Messages generated by the ``dhcpsrv`` library
+are logged according to the configuration set by the second entry.
+
+Currently defined loggers are listed in the following table. The
+"Software Package" column of this table specifies whether the particular
+loggers belong to the core Kea code (open source Kea binaries and
+libraries), or hook libraries (open source or premium).
+
+.. tabularcolumns:: |p{0.2\linewidth}|p{0.2\linewidth}|p{0.6\linewidth}|
+
+.. table:: List of loggers supported by Kea servers and hook libraries shipped with Kea/premium packages
+ :class: longtable
+ :widths: 20 20 60
+
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | Logger Name | Software Package | Description |
+ +==================================+=======================================+================================+
+ | ``kea-ctrl-agent`` | core | The root logger for |
+ | | | the Control Agent |
+ | | | exposing the RESTful |
+ | | | control API. All |
+ | | | components used by |
+ | | | the Control Agent |
+ | | | inherit the settings |
+ | | | from this logger. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-ctrl-agent.auth`` | core | A logger which covers |
+ | | | access control details, such as|
+ | | | a result of the basic HTTP |
+ | | | authentication. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-ctrl-agent.ctrl-agent`` | core | Used to log results of |
+ | | | configuration checks, |
+ | | | information about services |
+ | | | starting or failing to start, |
+ | | | command receival and |
+ | | | forwarding. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-ctrl-agent.http`` | core | A logger which |
+ | | | outputs log messages |
+ | | | related to receiving, |
+ | | | parsing, and sending |
+ | | | HTTP messages. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-ctrl-agent.rbac-hooks`` | :ischooklib:`libca_rbac.so` | Used to log messages related |
+ | | enterprise hook library | to the operation of the RBAC |
+ | | | hook library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4`` | core | The root logger for |
+ | | | the DHCPv4 server. |
+ | | | All components used |
+ | | | by the DHCPv4 server |
+ | | | inherit the settings |
+ | | | from this logger. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp6`` | core | The root logger for |
+ | | | the DHCPv6 server. |
+ | | | All components used |
+ | | | by the DHCPv6 server |
+ | | | inherit the settings |
+ | | | from this logger. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.alloc-engine``, | core | Used by the lease |
+ | ``kea-dhcp6.alloc-engine`` | | allocation engine, |
+ | | | which is responsible |
+ | | | for managing leases |
+ | | | in the lease |
+ | | | database, i.e. |
+ | | | creating, modifying, |
+ | | | and removing DHCP |
+ | | | leases as a result of |
+ | | | processing messages |
+ | | | from clients. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-ctrl-agent.auth``, | core | Used to log malformed HTTP |
+ | ``kea-dhcp4.auth``, | | packets when using basic |
+ | ``kea-dhcp6.auth`` | | authentication. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.bad-packets``, | core | Used by the DHCP |
+ | ``kea-dhcp6.bad-packets`` | | servers for logging |
+ | | | inbound client |
+ | | | packets that were |
+ | | | dropped or to which |
+ | | | the server responded |
+ | | | with a DHCPNAK. It |
+ | | | allows administrators |
+ | | | to configure a |
+ | | | separate log output |
+ | | | that contains only |
+ | | | packet drop and |
+ | | | reject entries. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.bootp-hooks`` | :ischooklib:`libdhcp_bootp.so` | This logger is used to log |
+ | | open-source hook library | messages related to the |
+ | | | operation of the BOOTP hook |
+ | | | library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-ctrl-agent.callouts``, | core | Used to log messages |
+ | ``kea-dhcp4.callouts``, | | pertaining to the |
+ | ``kea-dhcp6.callouts``, | | callouts registation and |
+ | ``kea-dhcp-ddns.callouts`` | | execution for a particular |
+ | | | hook point. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.cb-cmds-hooks``, | :ischooklib:`libdhcp_cb_cmds.so` | Used to log messages related |
+ | ``kea-dhcp6.cb-cmds-hooks`` | subscription hook library | to the operation of the |
+ | | | Config Backend Commands |
+ | | | hook library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.class-cmds-hooks``, | :ischooklib:`libdhcp_class_cmds.so` | Used to log messages related |
+ | ``kea-dhcp6.class-cmds-hooks`` | subscription hook library | to the operation of the |
+ | | | Class Commands |
+ | | | hook library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.commands``, | core | Used to log messages |
+ | ``kea-dhcp6.commands`` | | relating to the |
+ | | | handling of commands |
+ | | | received by the DHCP |
+ | | | server over the |
+ | | | command channel. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.database``, | core | Used to log messages |
+ | ``kea-dhcp6.database`` | | relating to general |
+ | | | operations on the |
+ | | | relational databases. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.ddns-tuning-hooks``, | :ischooklib:`libdhcp_ddns_tuning.so` | Used to log messages related |
+ | ``kea-dhcp6.ddns-tuning-hooks`` | premium hook library | to the operation of the |
+ | | | DDNS Tuning hook library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.ddns``, | core | Used by the DHCP |
+ | ``kea-dhcp6.ddns`` | | server to log |
+ | | | messages related to |
+ | | | Client FQDN and |
+ | | | Hostname option |
+ | | | processing. It also |
+ | | | includes log messages |
+ | | | related to the |
+ | | | relevant DNS updates. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.dhcp4``, | core | Used to log basic operations. |
+ | ``kea-dhcp6.dhcp6`` | | |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.dhcpsrv``, | core | The base loggers for the |
+ | ``kea-dhcp6.dhcpsrv`` | | ``libkea-dhcpsrv.so`` library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.eval``, | core | Used to log messages |
+ | ``kea-dhcp6.eval`` | | relating to the |
+ | | | client classification |
+ | | | expression evaluation |
+ | | | code. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.flex-id-hooks``, | :ischooklib:`libdhcp_flex_id.so` | Used |
+ | ``kea-dhcp6.flex-id-hooks`` | premium hook library | to log messages |
+ | | | related to the |
+ | | | operation of the |
+ | | | Flexible Identifier |
+ | | | hook library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.flex-option-hooks``, | :ischooklib:`libdhcp_flex_option.so` | Used to log messages related |
+ | ``kea-dhcp6.flex-option-hooks`` | open-source hook library | to the operaton of |
+ | | | the Flexible Option |
+ | | | hook library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.ha-hooks``, | :ischooklib:`libdhcp_ha.so` | Used |
+ | ``kea-dhcp6.ha-hooks`` | open-source hook library | to log messages |
+ | | | related to the |
+ | | | operation of the High |
+ | | | Availability hook |
+ | | | library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-ctrl-agent.hooks``, | core | Used to log messages related |
+ | ``kea-dhcp4.hooks``, | | to the maagemet of hook |
+ | ``kea-dhcp6.hooks``, | | libraries, e.g. |
+ | ``kea-dhcp-ddns.hooks`` | | registatin and |
+ | | | deregistration of the |
+ | | | libraries, and to the |
+ | | | initialization of the |
+ | | | callouts execution |
+ | | | for various hook |
+ | | | points within the |
+ | | | DHCP server. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.host-cache-hooks``, | :ischooklib:`libdhcp_host_cache.so` | Used |
+ | ``kea-dhcp6.host-cache-hooks`` | subscription hook library | to log messages |
+ | | | related to the |
+ | | | operation of the Host |
+ | | | Cache hook library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.host-cmds-hooks``, | :ischooklib:`libdhcp_host_cmds.so` | Used |
+ | ``kea-dhcp6.host-cmds-hooks`` | premium hook library | to log messages |
+ | | | related to the |
+ | | | operation of the Host |
+ | | | Commands hook |
+ | | | library. In general, |
+ | | | these pertain to |
+ | | | the loading and |
+ | | | unloading of the |
+ | | | library and the |
+ | | | execution of commands |
+ | | | by the library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.hosts``, | core | Used within |
+ | ``kea-dhcp6.hosts`` | | ``libdhcpsrv``, it logs |
+ | | | messages related to |
+ | | | the management of |
+ | | | DHCP host |
+ | | | reservations, i.e. |
+ | | | retrieving |
+ | | | reservations and |
+ | | | adding new |
+ | | | reservations. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.lease-cmds-hooks``, | :ischooklib:`libdhcp_lease_cmds.so` | Used |
+ | ``kea-dhcp6.lease-cmds-hooks`` | open-source hook library | to log messages |
+ | | | related to the |
+ | | | operation of the |
+ | | | Lease Commands hook |
+ | | | library. In general, |
+ | | | these pertain to |
+ | | | the loading and |
+ | | | unloading of the |
+ | | | library and the |
+ | | | execution of commands |
+ | | | by the library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.limits-hooks``, | :ischooklib:`libdhcp_limits.so` | Used to log messages related |
+ | ``kea-dhcp6.limits-hooks`` | subscription hook library | to the operation of the |
+ | | | Limits hook library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.lease-query-hooks``, | :ischooklib:`libdhcp_lease_query.so` | Used |
+ | ``kea-dhcp6.lease-query-hooks`` | premium hook library | to log messages |
+ | | | related to the |
+ | | | operation of the |
+ | | | Leasequery hook library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.leases``, | core | Used by the DHCP |
+ | ``kea-dhcp6.leases`` | | server to log |
+ | | | messages related to |
+ | | | lease allocation. The |
+ | | | messages include |
+ | | | detailed information |
+ | | | about the allocated |
+ | | | or offered leases, |
+ | | | errors during the |
+ | | | lease allocation, |
+ | | | etc. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.legal-log-hooks``, | :ischooklib:`libdhcp_legal_log.so` | Used |
+ | ``kea-dhcp6.legal-log-hooks`` | premium hook library | to log messages |
+ | | | related to the |
+ | | | operation of the |
+ | | | Forensic Logging |
+ | | | hook library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.mysql-cb-hooks``, | :ischooklib:`libdhcp_mysql_cb.so` | Used |
+ | ``kea-dhcp6.mysql-cb-hooks`` | open-source hook library | to log messages |
+ | | | related to the |
+ | | | operation of the |
+ | | | MySQL Configuration |
+ | | | Backend hook |
+ | | | library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.options``, | core | Used by the DHCP |
+ | ``kea-dhcp6.options`` | | server to log |
+ | | | messages related to |
+ | | | the processing of |
+ | | | options in the DHCP |
+ | | | messages, i.e. |
+ | | | parsing options, |
+ | | | encoding options into |
+ | | | on-wire format, and |
+ | | | packet classification |
+ | | | using options |
+ | | | contained in the |
+ | | | received packets. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.packets``, | core | Mostly |
+ | ``kea-dhcp6.packets`` | | used to log messages |
+ | | | related to |
+ | | | transmission of the |
+ | | | DHCP packets, i.e. |
+ | | | packet reception and |
+ | | | the sending of a |
+ | | | response. Such |
+ | | | messages include |
+ | | | information about the |
+ | | | source and |
+ | | | destination IP |
+ | | | addresses and |
+ | | | interfaces used to |
+ | | | transmit packets. The |
+ | | | logger is also used |
+ | | | to log messages |
+ | | | related to subnet |
+ | | | selection, as this |
+ | | | selection is usually |
+ | | | based on the IP |
+ | | | addresses, relay |
+ | | | addresses, and/or |
+ | | | interface names, |
+ | | | which can be |
+ | | | retrieved from the |
+ | | | received packet even |
+ | | | before the DHCP |
+ | | | message carried in |
+ | | | the packet is parsed. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.perfmon-hooks``, | :ischooklib:`libdhcp_perfmon.so` | Used to log messages related |
+ | ``kea-dhcp6.perfmon-hooks`` | open-source hook library | to performance monitoring. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.ping-check-hooks`` | :ischooklib:`libdhcp_ping_check.so` | Used |
+ | | subscription hook library | to log messages related to |
+ | | | carrying out pre-offer ping |
+ | | | checks of candidate leases. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.pgsql-cb-hooks``, | :ischooklib:`libdhcp_pgsql_cb.so` | Used |
+ | ``kea-dhcp6.pgsql-cb-hooks`` | open-source hook library | to log messages |
+ | | | related to the |
+ | | | operation of the |
+ | | | PostgreSQL Configuration |
+ | | | Backend hook |
+ | | | library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.run-script-hooks``, | :ischooklib:`libdhcp_run_script.so` | Used to log messages related |
+ | ``kea-dhcp6.run-script-hooks`` | open-source hook library | to the operation of the |
+ | | | Run Script hook library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.radius-hooks``, | :ischooklib:`libdhcp_radius.so` | Used |
+ | ``kea-dhcp6.radius-hooks`` | premium hook library | to log messages |
+ | | | related to the |
+ | | | operation of the |
+ | | | RADIUS hook library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.stat-cmds-hooks``, | :ischooklib:`libdhcp_stat_cmds.so` | Used |
+ | ``kea-dhcp6.stat-cmds-hooks`` | opens-source hook library | to log messages |
+ | | | related to the |
+ | | | operation of the |
+ | | | Statistics Commands |
+ | | | hook library. In |
+ | | | general, these |
+ | | | pertain to loading |
+ | | | and unloading the |
+ | | | library and the |
+ | | | execution of commands |
+ | | | by the library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.subnet-cmds-hooks``, | :ischooklib:`libdhcp_subnet_cmds.so` | Used |
+ | ``kea-dhcp6.subnet-cmds-hooks`` | premium hook library | to log messages |
+ | | | related to the |
+ | | | operation of the |
+ | | | Subnet Commands hook |
+ | | | library. In general, |
+ | | | these pertain to |
+ | | | loading and unloading |
+ | | | the library and the |
+ | | | execution of commands |
+ | | | by the library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.tcp``, | core | Used to log messages related |
+ | ``kea-dhcp6.tcp`` | | to TCP traffic. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp4.user_chk``, | :ischooklib:`libdhcp_user_chk.so` | Used to log messages related |
+ | ``kea-dhcp6.user_chk`` | hook library | to the operaton of the |
+ | | | User Check hook library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp-ddns`` | core | The root logger for |
+ | | | the :iscman:`kea-dhcp-ddns` |
+ | | | daemon. All |
+ | | | components used by |
+ | | | this daemon inherit |
+ | | | the settings from |
+ | | | this logger unless |
+ | | | there are |
+ | | | configurations for |
+ | | | more specialized |
+ | | | loggers. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-ctrl-agent.dctl``, | core | Used to log basic inofrmaton |
+ | ``kea-dhcp-ddns.dctl`` | | about the process, |
+ | | | received signals, and |
+ | | | triggered |
+ | | | reconfigurations. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp-ddns.asiodns``, | core | Used to log messages about |
+ | | | network events in DDNS |
+ | | | operations. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp-ddns.dhcpddns`` | core | Used by |
+ | | | the :iscman:`kea-dhcp-ddns` |
+ | | | daemon to log |
+ | | | events related to |
+ | | | DDNS operations. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp-ddns.dhcp-to-d2`` | core | Used by the |
+ | | | :iscman:`kea-dhcp-ddns` daemon |
+ | | | to log |
+ | | | information about |
+ | | | events dealing with |
+ | | | receiving messages |
+ | | | from the DHCP servers |
+ | | | and adding them to |
+ | | | the queue for |
+ | | | processing. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp-ddns.d2-to-dns`` | core | Used by the |
+ | | | :iscman:`kea-dhcp-ddns` daemon |
+ | | | to log |
+ | | | information about |
+ | | | events dealing with |
+ | | | sending and receiving |
+ | | | messages to and from |
+ | | | the DNS servers. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp-ddns.gss-tsig-hooks`` | :ischooklib:`libddns_gss_tsig.so` | Used to log messages related |
+ | | subscription hook library | to the operation of the |
+ | | | GSS-TSIG hook library. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-dhcp-ddns.libdhcp-ddns`` | core | Used to log events related to |
+ | | | DDNS operations. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+ | ``kea-netconf`` | core | The root logger for |
+ | | | the NETCONF agent. |
+ | | | All components used |
+ | | | by NETCONF inherit |
+ | | | the settings from |
+ | | | this logger if there |
+ | | | is no specialized |
+ | | | logger provided. |
+ +----------------------------------+---------------------------------------+--------------------------------+
+
+Note that user-defined hook libraries should not use any of the loggers
+mentioned above, but should instead define new loggers with names that
+correspond to the libraries using them. Suppose that a user created
+a library called “libdhcp-packet-capture” to dump packets received and
+transmitted by the server to a file. An appropriate name for the
+logger could be ``kea-dhcp4.packet-capture-hooks``. (Note that the hook
+library implementer only specifies the second part of this name, i.e.
+“packet-capture”. The first part is a root-logger name and is prepended
+by the Kea logging system.) It is also important to note that since this
+new logger is a child of a root logger, it inherits the configuration
+from the root logger, something that can be overridden by an entry in
+the configuration file.
+
+The easiest way to find a logger name is to configure all logging to go
+to a single destination and look there for specific logger names. See
+:ref:`logging-message-format` for details.
+
+The ``severity`` (string) Logger
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This specifies the category of messages logged. Each message is logged
+with an associated severity, which may be one of the following (in
+descending order of severity):
+
+- FATAL - associated with messages generated by a condition that is so
+ serious that the server cannot continue executing.
+
+- ERROR - associated with messages generated by an error condition. The
+ server continues executing, but the results may not be as
+ expected.
+
+- WARN - indicates an out-of-the-ordinary condition. However, the
+ server continues executing normally.
+
+- INFO - an informational message marking some event.
+
+- DEBUG - messages produced for debugging purposes.
+
+When the severity of a logger is set to one of these values, it
+only logs messages of that severity and above (e.g. setting the logging
+severity to INFO logs INFO, WARN, ERROR, and FATAL messages). The
+severity may also be set to NONE, in which case all messages from that
+logger are inhibited.
+
+.. note::
+
+ The :iscman:`keactrl` tool, described in :ref:`keactrl`, can be configured
+ to start the servers in verbose mode. If this is the case, the
+ settings of the logging severity in the configuration file have
+ no effect; the servers use a logging severity of DEBUG
+ regardless of the logging settings specified in the configuration
+ file. To control severity via the configuration file,
+ please make sure that the ``kea_verbose`` value is set to "no" within
+ the :iscman:`keactrl` configuration.
+
+.. _debuglevel:
+
+The ``debuglevel`` (integer) Logger
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When a logger's severity is set to DEBUG, this value specifies the
+level of debug messages to be printed. It ranges from 0 (least
+verbose) to 99 (most verbose). If severity for the logger is not DEBUG,
+this value is ignored.
+
+The ``output-options`` (list) Logger
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Each logger can have zero or more ``output-options``. These specify
+where log messages are sent and are explained in detail below.
+
+.. note::
+
+ As of Kea 2.5.1, alias ``output-options`` was added that can be used
+ interchangeably with previous ``output_options`` configuration key.
+ The reason behind this was to keep all configuration keys consistent i.e.
+ to use "dash" instead of "underscore" in the key name. For the time being
+ both configuration keys are considered correct and mean the same to Kea parsers.
+
+ As of Kea 2.5.2, the ``output-options`` becomes the default configuration key
+ and ``output_options`` can be used as an alias.
+
+The ``output`` (string) Option
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This value determines the type of output. There are several special
+values allowed here: ``stdout`` (messages are printed on standard
+output), ``stderr`` (messages are printed on stderr), ``syslog``
+(messages are logged to syslog using the default name), ``syslog:name``
+(messages are logged to syslog using a specified name). Any other value is
+interpreted as a filename to which messages should be written.
+
+The ``flush`` (boolean) Option
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This flushes the buffers after each log message. Doing this reduces performance
+but ensures that if the program terminates abnormally, all messages
+up to the point of termination are output. The default is ``true``.
+
+The ``maxsize`` (integer) Option
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This option is only relevant when the destination is a file; this is the maximum size
+in bytes that a log file may reach. When the maximum size is reached,
+the file is renamed and a new file created. Initially, a ".1" is
+appended to the name; if a ".1" file exists, it is renamed ".2", etc.
+This is referred to as rotation.
+
+The default value is 10240000 (10MB). The smallest value that can be
+specified without disabling rotation is 204800. Any value less than
+this, including 0, disables rotation. The greatest possible value is INT_MAX MB, which is
+approximately 2PB.
+
+.. note::
+
+ Due to a limitation of the underlying logging library (log4cplus),
+ rolling over the log files (from ".1" to ".2", etc.) may show odd
+ results; there can be multiple small files at the timing of rollover.
+ This can happen when multiple processes try to roll over the
+ files simultaneously. Version 1.1.0 of log4cplus solved this problem,
+ so if this version or later of log4cplus is used to build Kea, the
+ issue should not occur. Even with older versions, it is normally
+ expected to happen rarely unless the log messages are produced very
+ frequently by multiple different processes.
+
+The ``maxver`` (integer) Option
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This option is only relevant when the destination is a file and rotation is enabled
+(i.e. maxsize is large enough). This is the maximum number of rotated
+versions that will be kept. Once that number of files has been reached,
+the oldest file, "log-name.maxver", is discarded each time the log
+rotates. In other words, at most there will be the active log file plus
+maxver rotated files. The minimum and default value is 1.
+
+The ``pattern`` (string) Option
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This option can be used to specify the layout pattern of messages for
+a logger. Kea logging is implemented using the log4cplus library and its
+output formatting is based, conceptually, on the printf formatting from C;
+this is discussed in detail in the next section,
+:ref:`logging-message-format`.
+
+Each output type (``stdout``, file, or ``syslog``) has a default ``pattern`` which
+describes the content of its log messages. This parameter can be used to
+specify a desired pattern. The pattern for each logger is governed
+individually, so each configured logger can have its own pattern. Omitting
+the ``pattern`` parameter or setting it to an empty string, "", causes
+Kea to use the default pattern for that logger's output type.
+
+In addition to the log text itself, the default patterns used for ``stdout``
+and files contain information such as date and time, logger level, and
+process information. The default pattern for ``syslog`` is limited primarily
+to log level, source, and the log text. This avoids duplicating information
+which is usually supplied by syslog.
+
+.. warning::
+ Users are strongly encouraged to test their pattern(s) on a local,
+ non-production instance of Kea, running in the foreground and
+ logging to ``stdout``.
+
+.. _logging-message-format:
+
+Logging Message Format
+----------------------
+
+As mentioned above, Kea log message content is controlled via a scheme similar
+to the C language's printf formatting. The "pattern" used for each message is
+described by a string containing one or more format components as part of a
+text string. In addition to the components, the string may contain any other
+useful text for the administrator.
+
+The behavior of Kea's format strings is determined by log4cplus. The following
+time format options are possible enclosed in ``%D{}`` or ``%d{}``:
+
+.. table:: List of supported time format string components by Kea's logger
+ :class: longtable
+ :widths: 8 40
+
+ +-----------+-----------------------------------------------+
+ | Component | Value |
+ +===========+===============================================+
+ | ``%a`` | Abbreviated weekday name |
+ +-----------+-----------------------------------------------+
+ | ``%A`` | Full weekday name |
+ +-----------+-----------------------------------------------+
+ | ``%b`` | Abbreviated month name |
+ +-----------+-----------------------------------------------+
+ | ``%B`` | Full month name |
+ +-----------+-----------------------------------------------+
+ | ``%c`` | Standard date and time string |
+ +-----------+-----------------------------------------------+
+ | ``%d`` | Day of month as a decimal(1-31) |
+ +-----------+-----------------------------------------------+
+ | ``%H`` | Hour(0-23) |
+ +-----------+-----------------------------------------------+
+ | ``%I`` | Hour(1-12) |
+ +-----------+-----------------------------------------------+
+ | ``%j`` | Day of year as a decimal(1-366) |
+ +-----------+-----------------------------------------------+
+ | ``%m`` | Month as decimal(1-12) |
+ +-----------+-----------------------------------------------+
+ | ``%M`` | Minute as decimal(0-59) |
+ +-----------+-----------------------------------------------+
+ | ``%p`` | Locale's equivalent of AM or PM |
+ +-----------+-----------------------------------------------+
+ | ``%q`` | milliseconds as decimal(0-999) |
+ +-----------+-----------------------------------------------+
+ | ``%Q`` | microseconds as decimal(0-999.999) |
+ +-----------+-----------------------------------------------+
+ | ``%S`` | Second as decimal(0-59) |
+ +-----------+-----------------------------------------------+
+ | ``%U`` | Week of year, Sunday being first day(0-53) |
+ +-----------+-----------------------------------------------+
+ | ``%w`` | Weekday as a decimal(0-6, Sunday being 0) |
+ +-----------+-----------------------------------------------+
+ | ``%W`` | Week of year, Monday being first day(0-53) |
+ +-----------+-----------------------------------------------+
+ | ``%x`` | Standard date string |
+ +-----------+-----------------------------------------------+
+ | ``%X`` | Standard time string |
+ +-----------+-----------------------------------------------+
+ | ``%y`` | Year in decimal without century(0-99) |
+ +-----------+-----------------------------------------------+
+ | ``%Y`` | Year including century as decimal |
+ +-----------+-----------------------------------------------+
+ | ``%Z`` | Time zone name |
+ +-----------+-----------------------------------------------+
+ | ``%%`` | The percent sign |
+ +-----------+-----------------------------------------------+
+
+Refer to the documentation for the ``strftime()`` function found in the
+``<ctime>`` header or the ``strftime(3)`` UNIX manual page for more
+information.
+
+It is probably easiest to understand this by examining the default pattern
+for stdout and files; currently they are the same. That pattern is shown
+below:
+
+::
+
+ "%D{%Y-%m-%d %H:%M:%S.%q} %-5p [%c/%i.%t] %m\n"
+
+and a typical log produced by this pattern looks something like this:
+
+::
+
+ 2019-08-05 14:27:45.871 DEBUG [kea-dhcp4.dhcpsrv/8475.12345] DHCPSRV_TIMERMGR_START_TIMER starting timer: reclaim-expired-leases
+
+That breaks down to:
+
+ - ``%D{%Y-%m-%d %H:%M:%S.%q}``
+ "%D" is the local date and time when the log message is generated,
+ while everything between the curly braces, "{}", are date and time components.
+ From the example log above this produces:
+ ``2019-08-05 14:27:45.871``
+
+ - ``%-5p``
+ The severity of the message, output as a minimum of five characters,
+ using right-padding with spaces. In our example log: ``DEBUG``
+
+ - ``%c``
+ The log source. This includes two elements: the Kea process generating the
+ message, in this case, :iscman:`kea-dhcp4`; and the component within the program
+ from which the message originated, ``dhcpsrv`` (e.g. the name of the
+ library used by DHCP server implementations).
+
+ - ``%i``
+ The process ID. From the example log: ``8475``.
+
+ - ``%t``
+ The thread ID. From the example log: ``12345``.
+ The format of the thread ID is OS-dependent: e.g. on some systems
+ it is an address, so it is displayed in hexadecimal.
+
+ - ``%m``
+ The log message itself. Kea log messages all begin with a message identifier
+ followed by arbitrary log text. Every message in Kea has a unique
+ identifier, which can be used as an index to the :ref:`kea-messages`, where
+ more information can be obtained. In our example log above, the identifier
+ is ``DHCPSRV_TIMERMGR_START_TIMER``. The log text is typically a brief
+ description detailing the condition that caused the message to be logged. In
+ our example, the information logged,
+ ``starting timer: reclaim-expired-leases``, explains that the timer for the
+ expired lease reclamation cycle has been started.
+
+.. Warning::
+
+ Omitting ``%m`` omits the log message text from the output, making it
+ rather useless. ``%m`` should be considered mandatory.
+
+Finally, note that spacing between components, the square brackets around the
+log source and PID, and the final carriage return ``\n`` are all literal text
+specified as part of the pattern.
+
+.. Warning::
+
+ To ensure that each log entry is a separate line, patterns
+ must end with an ``\n``. There may be use cases where it is not desired
+ so we do not enforce its inclusion. If it is omitted from
+ the pattern, the log entries will run together in one long "line".
+
+The default pattern for ``syslog`` output is:
+
+::
+
+ "%-5p [%c.%t] %m\n"
+
+It omits the date and time as well as the process ID, as this
+information is typically output by ``syslog``. Note that Kea uses the pattern
+to construct the text it sends to ``syslog`` (or any other destination). It has
+no influence on the content ``syslog`` may add or formatting it may do.
+
+Consult the OS documentation for ``syslog`` behavior, as there are multiple
+implementations.
+
+A complete list of logging parameters supported by Kea is shown in the table below:
+
+.. table:: List of supported format string components by Kea's logger
+ :class: longtable
+ :widths: 8 40
+
+ +-----------+------------------------------------------------------------------------+
+ | Component | Value |
+ +===========+========================================================================+
+ | ``%b`` | Outputs file that called the log e.g., logger_impl.cc |
+ +-----------+------------------------------------------------------------------------+
+ | ``%c`` | Outputs the logger of the event e.g., kea-dhcp4.hosts |
+ +-----------+------------------------------------------------------------------------+
+ | ``%d`` | ``%d{}`` formats UTC time output e.g., ``%d{%Y-%m-%d %H:%M:%S.%q}`` |
+ +-----------+------------------------------------------------------------------------+
+ | ``%D`` | ``%D{}`` formats LOCAL time output e.g., ``%D{%Y-%m-%d %H:%M:%S.%q}`` |
+ +-----------+------------------------------------------------------------------------+
+ | ``%E`` | Outputs environment variables e.g., ``%E{PATH}`` |
+ +-----------+------------------------------------------------------------------------+
+ | ``%F`` | Outputs filename where logging request was issued e.g., logger_impl.cc |
+ +-----------+------------------------------------------------------------------------+
+ | ``%h`` | Outputs hostname of the system e.g., host-1 |
+ +-----------+------------------------------------------------------------------------+
+ | ``%H`` | Outputs fully qualified domain name e.g., host-1.example.com |
+ +-----------+------------------------------------------------------------------------+
+ | ``%l`` | Equivalent to ``%F:%L`` e.g., logger_impl.cc:179 |
+ +-----------+------------------------------------------------------------------------+
+ | ``%L`` | Outputs the line number where the log was called e.g., 179 |
+ +-----------+------------------------------------------------------------------------+
+ | ``%m`` | Outputs the actual log message |
+ +-----------+------------------------------------------------------------------------+
+ | ``%M`` | Outputs caller (function name) of the log message |
+ +-----------+------------------------------------------------------------------------+
+ | ``%n`` | Outputs line separator suppored by platform e.g., ``\n`` in Linux |
+ +-----------+------------------------------------------------------------------------+
+ | ``%p`` | Outputs log severity e.g., INFO |
+ +-----------+------------------------------------------------------------------------+
+ | ``%r`` | Outputs milliseconds since program start e.g., 1235 |
+ +-----------+------------------------------------------------------------------------+
+ | ``%t`` | Outputs thread id that generated the log message e.g., 281472855306256 |
+ +-----------+------------------------------------------------------------------------+
+ | ``%T`` | Outputs thread name that generated the log message e.g., 168005 |
+ +-----------+------------------------------------------------------------------------+
+ | ``%i`` | Outputs process id that generated the log message e.g., 168030 |
+ +-----------+------------------------------------------------------------------------+
+ | ``%%`` | Outputs a literal percent sign |
+ +-----------+------------------------------------------------------------------------+
+
+Padding and truncation are also possible with modifiers preceeding the component. This is
+done by placing a number and other modifier characters between the component and the %
+sign. There are five ways of modifying the output shown by example here.
+
+ - ``%20p``
+ Left pads with spaces (align right) if the severity is shorter than 20 characters.
+
+ - ``%-15r``
+ Right pads with spaces (align left) if the milliseconds since program start is shorter
+ than 15 characters.
+
+ - ``%.30m``
+ Truncates from the beginning of the message if the message is longer than 30
+ characters.
+
+ - ``%10.35E{PATH}``
+ Left pad with spaces (align right) if the environment variable "PATH" is shorter than
+ 10 characters. If the content is longer than 35 characters, then truncate from the
+ beginning of the string.
+
+ - ``%-15.40m``
+ Right pad with spaces (align left) if the log message is shorter than 15 characters.
+ If the message is longer than 40 characters, truncate from the beginning.
+
+Supported parameters depend on the liblog4cplus version used to compile Kea. This can
+be checked by executing ``kea-dhcp4 -W | grep -i log4cplus`` which will produce output
+like this: ``LOG4CPLUS_VERSION: 2.0.5``. Consult the documentation in the
+`log4cplus wiki <https://github.com/log4cplus/log4cplus/wiki>`__ for further information
+about the version you have installed.
+
+Example Logger Configurations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In this example, we want to set the server logging to write to the
+console using standard output.
+
+::
+
+ "Server": {
+ "loggers": [
+ {
+ "name": "kea-dhcp4",
+ "output-options": [
+ {
+ "output": "stdout"
+ }
+ ],
+ "severity": "WARN"
+ }
+ ]
+ }
+
+As a second example, we want to store DEBUG log messages in a file
+that is at most 2MB and keep up to eight copies of old log files. Once the
+logfile grows to 2MB, it should be renamed and a new file should be created.
+
+::
+
+ "Server": {
+ "loggers": [
+ {
+ "name": "kea-dhcp6",
+ "output-options": [
+ {
+ "output": "/var/log/kea-debug.log",
+ "maxver": 8,
+ "maxsize": 204800,
+ "flush": true,
+ "pattern": "%d{%j %H:%M:%S.%q} %c %m\n"
+ }
+ ],
+ "severity": "DEBUG",
+ "debuglevel": 99
+ }
+ ]
+ }
+
+Notice that the above configuration uses a custom pattern which produces output like this:
+
+::
+
+ 220 13:50:31.783 kea-dhcp4.dhcp4 DHCP4_STARTED Kea DHCPv4 server version 1.6.0-beta2-git started
+
+
+.. _logging-during-startup:
+
+Logging During Kea Startup
+--------------------------
+
+The logging configuration is specified in the configuration file.
+However, when Kea starts, the configuration file is not read until partway into the
+initialization process. Prior to that, the logging settings are set to
+default values, although it is possible to modify some aspects of the
+settings by means of environment variables. In the absence of
+any logging configuration in the configuration file, the settings of the
+(possibly modified) default configuration will persist while the program
+is running.
+
+The following environment variables can be used to control the behavior
+of logging during startup:
+
+``KEA_LOCKFILE_DIR``
+
+ Specifies a directory where the logging system should create its lock
+ file. If not specified, it is prefix/var/run/kea, where "prefix"
+ defaults to /usr/local. This variable must not end with a slash.
+ There is one special value: "none", which instructs Kea not to create
+ a lock file at all. This may cause issues if several processes log to
+ the same file.
+
+``KEA_LOGGER_DESTINATION``
+
+ Specifies logging output. There are several special values:
+
+ ``stdout``
+ Log to standard output.
+
+ ``stderr``
+ Log to standard error.
+
+ ``syslog[:fac]``
+ Log via syslog. The optional "fac" (which is separated from the word
+ "syslog" by a colon) specifies the facility to be used for the log
+ messages. Unless specified, messages are logged using the
+ facility "local0".
+
+ Any other value is treated as a name of the output file. If not
+ otherwise specified, Kea logs to standard output.
+
+Logging Levels
+==============
+
+All Kea servers follow the overall intention to let the user
+know what is going on while not overloading the logging system with too much information, as that
+could easily be used as a denial-of-service attack.
+
+Unlike the FATAL, ERROR, WARN and
+INFO levels, DEBUG has additional parameters. The following list details
+the basic information that is logged on each level. Sometimes the circumstances
+determine whether a piece of information is logged on a higher
+or lower level. For example, if a packet is being dropped due to configured classification, that
+is an execution of the configured policy and would be logged on debuglevel 15. However, if the
+packet is dropped due to an exception being thrown, it is much more important, as it may indicate
+a software bug, serious problems with memory, or database connectivity problems. As such it may
+be logged on much higher levels, such as WARN or even ERROR.
+
+- 0 - singular messages printed during startup or shutdown of the server.
+- 10 - log information about received API commands.
+- 15 - information about reasons why a packet was dropped.
+- 40 - tracing information, including processing decisions, results
+ of expression evaluations, and more.
+- 45 - similar to level 40, but with more details, e.g. the subnet being
+ selected for an incoming packet.
+- 50 - evaluations of expressions, status received from hook points, lease
+ processing, packet processing details, including unpacking, packing, sending, etc.
+- 55 - includes all details available, including full packet contents
+ with all options printed.
+
+The debug levels apply only to messages logged on DEBUG, and are configured using
+the ``debuglevel`` option. See the :ref:`debuglevel` section for details.
diff --git a/doc/sphinx/arm/quickstart.rst b/doc/sphinx/arm/quickstart.rst
new file mode 100644
index 0000000..81cc316
--- /dev/null
+++ b/doc/sphinx/arm/quickstart.rst
@@ -0,0 +1,382 @@
+.. _quickstart:
+
+***********
+Quick Start
+***********
+
+This section describes the basic steps needed to get Kea up and running.
+For further details, full customizations, and troubleshooting, see the
+respective chapters elsewhere in this Kea Administrator Reference Manual (ARM).
+
+.. _quick-start-tarball:
+
+Quick Start Guide Using tarball
+===============================
+
+1. Install required runtime and build dependencies. See
+ :ref:`build-requirements` for details.
+
+2. Download the Kea source tarball from
+ `the ISC.org downloads page <https://www.isc.org/download/>`__ or
+ `the ISC downloads site <https://downloads.isc.org/isc/kea/>`__ or
+ `the ISC Cloudsmith page <https://cloudsmith.io/~isc/packages/?q=format%3Araw>`__.
+
+3. Extract the tarball. For example:
+
+ .. parsed-literal::
+
+ $ tar -xvzf kea-|release|.tar.gz
+
+4. Go into the source directory and run the configure script:
+
+ .. parsed-literal::
+
+ $ cd kea-|release|
+ $ ./configure [your extra parameters]
+
+5. Build it:
+
+ .. code-block:: console
+
+ $ make
+
+6. Install it (by default it will be placed in ``/usr/local/``, so
+ root privileges are likely required for this step):
+
+ .. code-block:: console
+
+ $ make install
+
+.. _quick-start-repo:
+
+Quick Start Guide Using Native Packages
+=======================================
+
+ISC provides native Alpine, deb, and RPM packages, which make Kea installation
+much easier. Unless specific compilation options are desired, it is usually
+easier to install Kea using native packages.
+
+1. Go to `Kea on cloudsmith.io <https://cloudsmith.io/~isc/repos/>`__.
+
+2. Choose the Cloudsmith repository e.g. |cloudsmith_repo| for Kea |version|.
+
+3. Click on the arrow besides the ``Set Me Up`` button and select your OS flavor
+ out of: ``Alpine``, ``Debian``, ``RedHat``.
+
+4. Follow the instructions written there.
+
+.. note::
+ For example, the Debian setup instructions for Kea 2.4 can be found here:
+ https://cloudsmith.io/~isc/repos/kea-2-4/setup/#formats-deb
+
+ The dropdown near the top of the page offers instructions for
+ other operating systems.
+
+5. Kea is split into various packages. The entire list is available on the
+ Cloudsmith repository page under ``Packages``, or it can be retrieved
+ using ``apk``/``apt``/``dnf``.
+
+.. list-table::
+ :stub-columns: 1
+ :widths: 10 90
+
+ * - Debian/Ubuntu
+
+ - .. code-block:: console
+
+ $ apt search isc-kea
+
+ .. note::
+ ``isc-kea-dhcp4-server`` and ``isc-kea-dhcp6-server`` are empty
+ transitional packages. The working server packages are
+ ``isc-kea-dhcp4`` and ``isc-kea-dhcp6``.
+
+ * - Fedora/RedHat
+
+ - .. code-block:: console
+
+ $ dnf search 'isc-kea*'
+
+ * - Alpine
+
+ - .. code-block:: console
+
+ $ apk search isc-kea
+
+6. Install the metapackage containing all the tools, libraries, servers,
+ documentation, and open source hooks:
+
+.. list-table::
+ :stub-columns: 1
+ :widths: 10 90
+
+ * - Debian/Ubuntu
+
+ - .. code-block:: console
+
+ $ sudo apt install isc-kea
+
+ * - Fedora/RedHat
+
+ - .. code-block:: console
+
+ $ sudo dnf install isc-kea
+
+ * - Alpine
+
+ - .. code-block:: console
+
+ # apk add isc-kea
+
+or specific packages:
+
+.. list-table::
+ :stub-columns: 1
+ :widths: 10 90
+
+ * - Debian/Ubuntu
+
+ - .. code-block:: console
+
+ $ sudo apt install isc-kea-dhcp6
+
+ * - Fedora/RedHat
+
+ - .. code-block:: console
+
+ $ sudo dnf install isc-kea-dhcp6
+
+ * - Alpine
+
+ - .. code-block:: console
+
+ $ apk add isc-kea-dhcp6
+
+or every single Kea-related package, including development headers, debug
+symbols, and premium hooks (if available):
+
+.. list-table::
+ :stub-columns: 1
+ :widths: 10 90
+
+ * - Debian/Ubuntu
+
+ - .. code-block:: console
+
+ $ sudo apt install 'isc-kea*'
+
+ * - Fedora/RedHat
+
+ - .. code-block:: console
+
+ $ sudo dnf install 'isc-kea*'
+
+ * - Alpine
+
+ - Installing packages via globbing (``*``) is not available for Alpine,
+ but it can be simulated with the following command:
+
+ .. code-block:: console
+
+ # apk search isc-kea | sed 's/-[0-9].*//g' | xargs apk add
+
+or all packages with a specified version number:
+
+.. list-table::
+ :stub-columns: 1
+ :widths: 10 90
+
+ * - Debian/Ubuntu
+
+ - .. code-block:: console
+
+ $ sudo apt install 'isc-kea*=2.4.0-isc20230921141113'
+
+ * - Fedora/RedHat
+
+ - .. code-block:: console
+
+ $ sudo dnf install 'isc-kea*2.4.0-isc20230921141113*'
+
+ * - Alpine
+
+ - Installing packages via globbing (``*``) is not available for Alpine,
+ but it can be simulated with the following command:
+
+ .. code-block:: console
+
+ # apk search isc-kea | sed 's/-[0-9].*//g' | grep r20230921141113 | xargs apk add
+
+8. All installed packages should be now available directly.
+
+ You can start a server up manually:
+
+ .. code-block:: console
+
+ # kea-dhcp6 -c /etc/kea/kea-dhcp6.conf
+
+ or using systemd:
+
+ .. code-block:: console
+
+ # systemctl restart kea-dhcp6
+
+ or using OpenRC on Alpine:
+
+ .. code-block:: console
+
+ # service kea-dhcp6 restart
+
+.. note::
+ :iscman:`keactrl` is not available in packages, as similar functionality is provided
+ by the native systemctl scripts.
+
+9. On Debian/Ubuntu systems, the service is enabled at boot time automatically
+ when the package is installed. On Fedora/RHEL and Alpine, the service is not
+ enabled automatically, so, if desired, it must be enabled manually.
+
+ With systemd on Fedora/RedHat:
+
+ .. code-block:: console
+
+ # systemctl enable kea-dhcp6
+
+ With OpenRC on Alpine:
+
+ .. code-block:: console
+
+ # rc-update add kea-dhcp6
+
+
+.. _quick-start-docker:
+
+Quick Start Guide Using Docker Containers
+=========================================
+
+1. Go to `ISC docker repository on cloudsmith.io <https://cloudsmith.io/~isc/repos/docker/packages/>`__.
+
+2. Create an ipvlan network attached to the client-facing host interface and
+ assigned to the subnet that is served by Kea.
+
+.. code-block:: console
+
+ $ docker network create --driver ipvlan --ipv6 --subnet 2001:db8::/64 --opt parent=eth0 ipvlan0
+
+3. Pick the desired image and pull it locally.
+
+.. code-block:: console
+
+ $ docker pull docker.cloudsmith.io/isc/docker/kea-dhcp6
+
+
+4. Create a container out of the image. Mount the configuration volume and the
+ data volume if needed.
+
+.. code-block:: console
+
+ $ docker create \
+ --name kea-dhcp6 \
+ --network ipvlan0 \
+ --volume /local/kea/config:/etc/kea \
+ --volume /local/kea/data:/var/lib/kea \
+ docker.cloudsmith.io/isc/docker/kea-dhcp6
+
+5. Start the docker container.
+
+.. code-block:: console
+
+ $ docker start kea-dhcp6
+
+6. To stop the docker container, run:
+
+.. code-block:: console
+
+ $ docker stop kea-dhcp6
+
+.. note::
+
+ Refer to the `kea-docker readme <https://gitlab.isc.org/isc-projects/kea-docker#user-content-docker-files-for-building-kea-containers>`__ for more complex scenarios.
+
+
+.. _quick-start-services:
+
+Quick Start Guide for DHCPv4 and DHCPv6 Services
+================================================
+
+1. Edit the Kea configuration files, which by default are installed in
+ the ``[kea-install-dir]/etc/kea/`` directory. These are:
+ ``kea-dhcp4.conf``, ``kea-dhcp6.conf``, ``kea-dhcp-ddns.conf`` and
+ ``kea-ctrl-agent.conf``, ``keactrl.conf`` for DHCPv4 server, DHCPv6 server,
+ D2, Control Agent, and the keactrl script, respectively.
+
+2. To start the DHCPv4 server in the background, run the
+ following command (as root):
+
+ .. code-block:: console
+
+ # keactrl start -s dhcp4
+
+ Or run the following command to start the DHCPv6 server:
+
+ .. code-block:: console
+
+ # keactrl start -s dhcp6
+
+ Note that it is also possible to start all servers simultaneously:
+
+ .. code-block:: console
+
+ # keactrl start
+
+3. Verify that the Kea server(s) is/are running:
+
+ .. code-block:: console
+
+ # keactrl status
+
+ A server status of "inactive" may indicate a configuration error.
+ Please check the log file (by default named
+ ``[kea-install-dir]/var/log/kea-dhcp4.log``,
+ ``[kea-install-dir]/var/log/kea-dhcp6.log``,
+ ``[kea-install-dir]/var/log/kea-ddns.log``, or
+ ``[kea-install-dir]/var/log/kea-ctrl-agent.log``) for the details of
+ any errors.
+
+4. If the server has started successfully, test that it is
+ responding to DHCP queries and that the client receives a
+ configuration from the server; for example, use the `ISC DHCP
+ client <https://www.isc.org/download/>`__.
+
+5. To stop running the server(s):
+
+ .. code-block:: console
+
+ # keactrl stop
+
+For system-specific instructions, please read the
+`system-specific notes <https://kb.isc.org/docs/installing-kea>`__,
+available in the Kea section of `ISC's
+Knowledgebase <https://kb.isc.org/docs>`__.
+
+The details of :iscman:`keactrl` script usage can be found in :ref:`keactrl`.
+
+Once Kea services are up and running, consider deploying a dashboard solution
+to monitor running services. For more details, see :ref:`stork`.
+
+.. _quick-start-direct-run:
+
+Running the Kea Servers Directly
+================================
+
+The Kea servers can be started directly, without the need to use
+:iscman:`keactrl` or ``systemctl``. To start the DHCPv4 server run the following command:
+
+.. code-block:: console
+
+ # kea-dhcp4 -c /path/to/your/kea4/config/file.json
+
+Similarly, to start the DHCPv6 server, run the following command:
+
+.. code-block:: console
+
+ # kea-dhcp6 -c /path/to/your/kea6/config/file.json
diff --git a/doc/sphinx/arm/rst_arm_sources.mk b/doc/sphinx/arm/rst_arm_sources.mk
new file mode 100644
index 0000000..dc2cf45
--- /dev/null
+++ b/doc/sphinx/arm/rst_arm_sources.mk
@@ -0,0 +1,54 @@
+rst_arm_sources += arm/acknowledgments.rst
+rst_arm_sources += arm/admin.rst
+rst_arm_sources += arm/agent.rst
+rst_arm_sources += arm/classify.rst
+rst_arm_sources += arm/config-backend.rst
+rst_arm_sources += arm/config-templates.rst
+rst_arm_sources += arm/config.rst
+rst_arm_sources += arm/congestion-handling.rst
+rst_arm_sources += arm/ctrl-channel.rst
+rst_arm_sources += arm/database-connectivity.rst
+rst_arm_sources += arm/ddns.rst
+rst_arm_sources += arm/dhcp4-srv.rst
+rst_arm_sources += arm/dhcp6-srv.rst
+rst_arm_sources += arm/ext-gss-tsig.rst
+rst_arm_sources += arm/ext-netconf.rst
+rst_arm_sources += arm/ext-radius.rst
+rst_arm_sources += arm/hammer.rst
+rst_arm_sources += arm/hooks-bootp.rst
+rst_arm_sources += arm/hooks-cb-cmds.rst
+rst_arm_sources += arm/hooks-class-cmds.rst
+rst_arm_sources += arm/hooks-ddns-tuning.rst
+rst_arm_sources += arm/hooks-flex-id.rst
+rst_arm_sources += arm/hooks-flex-option.rst
+rst_arm_sources += arm/hooks-gss-tsig.rst
+rst_arm_sources += arm/hooks-ha.rst
+rst_arm_sources += arm/hooks-host-cache.rst
+rst_arm_sources += arm/hooks-host-cmds.rst
+rst_arm_sources += arm/hooks-lease-cmds.rst
+rst_arm_sources += arm/hooks-lease-query.rst
+rst_arm_sources += arm/hooks-limits.rst
+rst_arm_sources += arm/hooks-cb-mysql.rst
+rst_arm_sources += arm/hooks-cb-pgsql.rst
+rst_arm_sources += arm/hooks-legal-log.rst
+rst_arm_sources += arm/hooks-perfmon.rst
+rst_arm_sources += arm/hooks-ping-check.rst
+rst_arm_sources += arm/hooks-radius.rst
+rst_arm_sources += arm/hooks-rbac.rst
+rst_arm_sources += arm/hooks-run-script.rst
+rst_arm_sources += arm/hooks-stat-cmds.rst
+rst_arm_sources += arm/hooks-subnet-cmds.rst
+rst_arm_sources += arm/hooks-user-chk.rst
+rst_arm_sources += arm/hooks.rst
+rst_arm_sources += arm/install.rst
+rst_arm_sources += arm/integrations.rst
+rst_arm_sources += arm/intro.rst
+rst_arm_sources += arm/keactrl.rst
+rst_arm_sources += arm/lease-expiration.rst
+rst_arm_sources += arm/lfc.rst
+rst_arm_sources += arm/logging.rst
+rst_arm_sources += arm/quickstart.rst
+rst_arm_sources += arm/security.rst
+rst_arm_sources += arm/shell.rst
+rst_arm_sources += arm/stats.rst
+rst_arm_sources += arm/stork.rst
diff --git a/doc/sphinx/arm/security.rst b/doc/sphinx/arm/security.rst
new file mode 100644
index 0000000..3063fb0
--- /dev/null
+++ b/doc/sphinx/arm/security.rst
@@ -0,0 +1,445 @@
+.. _security:
+
+************
+Kea Security
+************
+
+Kea was originally designed to be installed in a protected environment, in a network
+datacenter; it did not offer hardened security features. However, due to customer demand
+and evolving network requirements, support for basic HTTP authentication and Transport
+Layer Security (TLS) have been added to Kea.
+
+.. _tls:
+
+TLS/HTTPS Support
+=================
+
+Since Kea 1.9.6, TLS can be used to secure HTTP communication. There are three levels of
+protection possible:
+
+- No TLS. The connection is plain-text, unencrypted HTTP. (This is
+ the only option available in versions prior to Kea 1.9.6.)
+
+- Encryption, which protects against passive attacks and
+ eavesdropping. In this case, the server is authenticated but the client is
+ not. This is the typical mode when securing a website, where
+ clients and servers are not under the control of a common entity.
+
+- Mutual authentication between the client and the server. This is the
+ strictest security mode and is the default when TLS is
+ enabled.
+
+.. note::
+
+ TLS mutual authentication is for TLS entities only. When TLS and
+ an HTTP authentication scheme are used together, there is no binding between
+ the two security mechanisms, and therefore no proof that the TLS client and server
+ are the same as the HTTP authentication client and server.
+
+.. _tls_config:
+
+Building Kea with TLS/HTTPS Support
+-----------------------------------
+
+TLS/HTTPS support is available with either the OpenSSL or the Botan
+cryptographic library. There are some constraints on the Boost library
+that must be used:
+
+- OpenSSL versions older than 1.0.2 are obsolete and should not be used.
+ Kea TLS support has not been tested with and is not supported on these versions.
+
+- OpenSSL version 1.0.2 has extended support, but only for OpenSSL premium
+ customers. Kea TLS support has been tested but is not supported on this version.
+
+- OpenSSL versions 1.1.x and later have been tested and are supported. Many
+ recent operating system versions include TLS 1.3 support.
+
+- OpenSSL 3.x has been released and Kea will build with it.
+
+- LibreSSL 3.2.4 has been tested. LibreSSL shares the OpenSSL 1.0.2 API, so
+ it should work, but is not supported.
+
+- Botan 1.x versions are obsolete and should not be used.
+ Kea TLS support has not been tested and is not supported with these versions.
+
+- Botan versions 2.14.0 and later have been tested and are supported. Kea TLS
+ support requires the four Asio header files which are included in Botan
+ packages and which are installed only if Botan is configured with the
+ ``--with-boost`` option.
+
+ Many packages provided by operating systems, such as Ubuntu 20.10,
+ do not build Botan with Boost support, making those packages
+ unusable for Kea with TLS.
+
+ It is still possible to take these files from the corresponding
+ Botan distribution and install them manually in the Botan include
+ directory, but this should be a last-resort procedure.
+
+ Without these header files, or with a Botan version prior
+ to 2.14.0, Kea can still build, but the TLS/HTTPS support is disabled;
+ any attempt to use it will fail with a fatal error.
+
+- Very old Boost versions provide SSL support (based on OpenSSL)
+ without offering a choice of the TLS version; Kea can still use them,
+ but they are not recommended.
+
+- Boost versions prior to 1.64 provide SSL support with a fixed
+ choice of the TLS version; Kea enforces the use of TLS 1.2 with them.
+
+- Boost versions 1.64 or newer provide SSL support with a generic
+ TLS version; the best (highest) version available on both peers is
+ selected.
+
+TLS/HTTPS Configuration
+-----------------------
+
+The TLS configuration parameters are:
+
+- ``trust-anchor`` - this string parameter specifies the name of a file
+ or directory where the certification authority (CA) certificate of
+ the other peer can be found. With OpenSSL, the directory must include
+ hash symbolic links. With Botan, the directory is recursively
+ searched for certificates.
+
+- ``cert-file`` - this string parameter specifies the name of the file
+ containing the end-entity certificate of the Kea instance
+ being configured.
+
+- ``key-file`` - this string parameter specifies the private key of the
+ end-entity certificate of the Kea instance being configured.
+ The file must not be encrypted; it is highly recommended to
+ restrict its access.
+
+The three string parameters must be either all unspecified (TLS disabled)
+or all specified (TLS enabled).
+
+TLS is asymmetric: the authentication of the server by the client is
+mandatory but the authentication of the client by the server is optional.
+In TLS terms, this means the server may require the client certificate, or
+may not; there is a server-specific TLS parameter.
+
+- ``cert-required`` - this boolean parameter allows a server to not
+ require the client certificate. Its default value is ``true``, which
+ means the client certificate is required and the
+ client must be authenticated. This flag has no meaning on the client side; the server
+ always provides a certificate which is validated by the client.
+
+Objects in files must be in the PEM format. Files can contain more
+than one certificate, but this has not been tested and is not supported.
+
+Botan requires CA certificates to have the standard CA certificate
+attributes, verifies that end-entity certificates are version 3
+certificates (as required by the TLS standard), and supports only PKCS 8
+files for the private key.
+
+.. note::
+
+ Some cryptographic libraries (e.g. Botan and recent OpenSSL) enforce
+ minimal strength (i.e. key length), e.g. at least 2048 for RSA.
+
+A sample set of certificates and associated objects is available at
+``src/lib/asiolink/testutils/ca`` in the Kea sources, with a ``doc.txt`` file
+explaining how they were generated using the ``openssl`` command. These
+files are for testing purposes only. **Do not use them in production.**
+
+TLS handshake, the phase where the cryptographic parameters are exchanged
+and authentication is verified, can fail in multiple ways. Error messages
+often do not help to pinpoint the source of the problem.
+Both OpenSSL and Botan provide a command-line tool with a ``verify`` command
+which can be used to understand and fix handshake issues.
+
+OpenSSL Tuning
+--------------
+
+Kea accepts the default OpenSSL configuration parameters, but administrators can
+also fine-tune the OpenSSL settings. For example, it may be desirable to limit
+the TLS version.
+
+The default OpenSSL configuration file is named ``openssl.cnf``. It can
+be found in a system-dependent ``etc`` directory, and the location can be overridden
+using the ``OPENSSL_CONF`` environment variable. For OpenSSL versions greater than
+1.0.2, the minimum acceptable protocol can be set via the ``MinProtocol`` variable.
+
+For these examples, we assume that no variables are already set and no sections already
+exist; it is, of course, possible to reuse existing variables and sections.
+
+In the default application, ``openssl_conf``, the corresponding variable
+must be set to the name of the section that handles defaults: in this example,
+``default_conf``. If ``openssl_conf`` is not yet set, add this command
+at the beginning of the OpenSSL configuration file (before the first
+section):
+
+.. code-block:: ini
+
+ openssl_conf = default_conf
+
+In the ``default_conf`` section, the ``ssl_conf`` variable must be set
+to the name of the section that handles SSL/TLS defaults: in this
+example, ``ssl_sect``.
+
+.. code-block:: ini
+
+ [ default_conf ]
+ ssl_conf = ssl_sect
+
+In the ``ssl_sect`` section, the ``system_default`` variable must be
+set to the name of the section that handles system defaults: in
+this example, ``system_default_sect``.
+
+.. code-block:: ini
+
+ [ ssl_sect ]
+ system_default = system_default_sect
+
+In the ``system_default_sect`` section, the ``MinProtocol`` variable must be
+set to the desired minimal SSL/TLS version: in this example, ``TLSv1.2``.
+
+.. code-block:: ini
+
+ [ system_default_sect ]
+ MinProtocol = TLSv1.2
+
+The same steps can be used to enforce other crypto parameters if
+desired.
+
+It is highly recommended to read the ``openssl.cnf`` manual page,
+normally called ``config.5ssl`` and displayed using ``man config``.
+
+Securing a Kea Deployment
+=========================
+
+Below is a list of considerations for administrators wishing to improve Kea's
+security. In many cases, there are trade-offs between convenience and security.
+
+Component-Based Design
+----------------------
+
+The Kea architecture is modular, with separate daemons for separate tasks.
+A Kea deployment may include DHCPv4, DHCPv6, and Dynamic DNS daemons; a Control Agent
+daemon run on each application server; the ``kea-lfc utility`` for doing periodic lease
+file cleanup; MySQL and or PostgreSQL databases, run either locally on the application
+servers or accessed over the internal network; and a Stork monitoring system.
+This modular architecture allows the administrator to minimize the attack surface
+by minimizing the code that is loaded and running.
+For example, :iscman:`kea-dhcp-ddns` should not be run unless DNS updates are required.
+Similarly, :iscman:`kea-lfc` is never triggered (and can be safely removed or never installed) if memfile is not used.
+Potential Kea security issues can be minimized by running only those processes required in the local environment.
+
+Limiting Application Permissions
+--------------------------------
+
+The DHCPv4 and DHCPv6 protocols assume the server opens privileged UDP port 67
+(DHCPv4) or 547 (DHCPv6), which requires root access under normal circumstances. However, via the
+capabilities mechanism on Linux systems, Kea can run from an unprivileged account. See
+:ref:`non-root` for details on how to run Kea without root access.
+
+The Control Agent (CA) can accept incoming HTTP or HTTPS connections. The default port is 8000, which
+does not require privileged access.
+
+Securing Kea Administrative Access
+----------------------------------
+
+The three primary Kea daemons (:iscman:`kea-dhcp4`, :iscman:`kea-dhcp6` and :iscman:`kea-dhcp-ddns`) all support a control
+channel, which is implemented as a UNIX socket. The control channel, which opens a UNIX socket, is disabled by default;
+however, many configuration examples have it enabled, as it is a very popular feature. To
+read from or write to this socket, root access is generally required, although if Kea is configured
+to run as non-root, the owner of the process can write to it. Access can be controlled using normal
+file-access control on POSIX systems (owner, group, others, read/write).
+
+Kea configuration is controlled by a JSON file on the Kea server. This file can be viewed or edited
+by anyone with file permissions (which are controlled by the operating system). Note that
+passwords are stored in clear text in the configuration file, so anyone with access to read the
+configuration file can find this information. As a practical matter, anyone with permission to edit
+the configuration file has control over Kea.
+Limiting user permission to read or write the Kea configuration file is an important security step.
+
+Securing Database Connections
+-----------------------------
+
+Kea can use an external MySQL or PostgreSQL database to store configuration, host reservations,
+or/and leases, or/and for forensic logging. The use of databases is a popular feature, but it is optional;
+it is also possible to store data in a flat file on disk.
+
+When using a database, Kea stores and uses the following credentials to authenticate with the database:
+username, password, host, port, and database name. **These are stored in clear text
+in the configuration file.**
+
+Depending on the database configuration, it is also possible to verify whether the system user matches the
+database username. Consult the MySQL or PostgreSQL manual for details.
+
+Information Leakage Through Logging
+-----------------------------------
+
+It is possible for Kea to log an entire configuration file, including passwords and secrets.
+Since Kea 1.9.7, this issue has been resolved by replacing the value of all entries ending in
+``password`` or ``secret`` with asterisks, as was already done for database logs.
+
+Logs are sent to stdout, stderr, files, or syslog; system file permissions system apply to
+stdout/stderr and files. Syslog may export the logs over the network, exposing them further to possible snooping.
+
+Cryptography Components
+-----------------------
+
+Kea supports the use of either of two cryptographic libraries: Botan or OpenSSL.
+The choice is made at compile time, and creates both compile and runtime dependencies
+between the Kea and the selected library. While OpenSSL is the most popular choice for
+deployments, Botan remains a fully supported alternative.
+
+The primary use cases for the cryptographic libraries are:
+
+- TLS support for the Control Agent (CA), introduced in Kea 1.9.6.
+- TSIG signatures when sending DNS updates.
+- calculating DHCID records when sending DNS updates.
+- random number generation (but not for usage requiring a crypto grade generator).
+
+For OpenSSL and Botan, only the low-level crypto interface is used (e.g. libcrypto). Kea does not link
+with libssl. Some dependent software systems, such as database client libraries, can also depend on a crypto
+library.
+
+One way to limit exposure for potential OpenSSL or Botan vulnerabilities is not to use DDNS. The
+libraries would still be needed to build and run Kea, but the code would never be used, so any
+potential bugs in the libraries would not be exploitable.
+
+TSIG Signatures
+---------------
+
+Kea supports the following algorithms when signing DNS updates with TSIG signatures:
+
+- HMAC-MD5
+- HMAC-SHA1
+- HMAC-SHA224
+- HMAC-SHA256
+- HMAC-SHA384
+- HMAC-SHA512
+
+See :ref:`d2-tsig-key-list-config` for an up-to-date list.
+
+Kea uses SHA256 to calculate DHCID records. This is irrelevant from the cryptography perspective, as
+the DHCID record is only used to generate unique identifiers for two devices that may have been
+assigned the same IP address at different times.
+
+Raw Socket Support
+------------------
+
+In principle, Kea DHCPv4 uses raw sockets to receive traffic from clients. The difficulty is with
+receiving packets from devices that do not yet have an IPv4 address. When dealing with direct traffic
+(where both client and server are connected to the same link, not separated by relays), the kernel
+normally drops the packet as the source IP address is 0.0.0.0. Therefore, Kea needs to open raw
+sockets to be able to receive this traffic.
+
+However, this is not necessary if all the traffic is coming via relays, which is often the case in
+many networks. In that case normal UDP sockets can be used instead. There is a ``dhcp-socket-type``
+parameter that controls this behavior.
+
+The default is to permit raw socket usage, as it is more versatile.
+
+When using raw sockets, Kea is able to receive raw layer 2 packets, bypassing most firewalls
+(including iptables). This effectively means that when raw sockets are used, the iptables cannot be
+used to block DHCP traffic. This is a design choice of the Linux kernel.
+
+Kea can be switched to use UDP sockets. This is an option when all traffic is relayed.
+However, it does not work for directly connected devices. If Kea is limited to UDP sockets,
+iptables should work properly.
+
+If raw sockets are not required, disabling this access can improve security.
+
+Remote Administrative Access
+----------------------------
+
+Kea's Control Agent (CA) exposes a RESTful API over HTTP or HTTPS (HTTP over TLS). The CA is an
+optional feature that is disabled by default, but it is very popular. When enabled, it listens on the
+loopback address (127.0.0.1 or ::1) by default, unless configured otherwise. See :ref:`tls`
+for information about protecting the TLS traffic. Limiting the incoming connections with a firewall, such as
+iptables, is generally a good idea.
+
+Note that in High Availability (HA) deployments, DHCP partners connect to each other using a CA
+connection.
+
+Authentication for Kea's RESTful API
+------------------------------------
+
+Kea 1.9.0 added support for basic HTTP authentication (`RFC 7617 <https://tools.ietf.org/html/rfc7617>`_),
+to control access for incoming REST commands over HTTP. The credentials (username, password) are
+stored in a local Kea configuration file on disk. The username is logged with the API command, so it
+is possible to determine which authenticated user performed each command. The access control details
+are logged using a dedicated ``auth`` logger. Basic HTTP
+authentication is weak on its own as there are known dictionary attacks, but those attacks require
+a "man in the middle" to get access to the HTTP traffic. That can be eliminated by using basic HTTP
+authentication exclusively over TLS. In fact, if possible, using client certificates for TLS is better than
+using basic HTTP authentication.
+
+Kea 1.9.2 introduced a new ``auth`` hook point. With this new hook point, it is possible to develop an external
+hook library to extend the access controls, integrate with another authentication authority, or add role-based
+access control to the Control Agent.
+
+Kea Security Processes
+======================
+
+The following sections discuss how the Kea DHCP development team ensures code quality and handles vulnerabilities.
+
+Vulnerability Handling
+----------------------
+
+ISC is an experienced and active participant in the industry-standard vulnerability disclosure
+process and maintains accurate documentation on our process and vulnerabilities in ISC software.
+See https://kb.isc.org/docs/aa-00861 for ISC's Software Defect and Security Vulnerability Disclosure Policy.
+
+In case of a security vulnerability in Kea, ISC notifies support customers ahead of any public
+disclosure, and provides a patch and/or updated installer package to remediate the
+vulnerability.
+
+When a security update is published, both the source tarballs and the ISC-maintained packages are
+published on the same day. This enables users of the native Linux update mechanisms (such as
+Debian's and Ubuntu's apt or RedHat's dnf) to update their systems promptly.
+
+Code Quality and Testing
+------------------------
+
+Kea undergoes extensive tests during its development. The following are some of the
+processes that are used to ensure adequate code quality:
+
+- Each line of code goes through a formal review before it is accepted. The review process is
+ documented and available publicly.
+- Roughly 50% of the source code is dedicated to unit tests. As of December 2020, there were over 6000
+ unit tests and the number is increasing with time. Unit tests are required to commit any new feature.
+- There are around 1500 system tests for Kea. These simulate both correct and invalid
+ situations, covering network packets (mostly DHCP, but also DNS, HTTP, HTTPS and others),
+ command-line usage, API calls, database interactions, scripts, and more.
+- There are performance tests with over 80 scenarios that test Kea overall performance and
+ resiliency to various levels of traffic, and measuring various metrics (latency, leases per seconds,
+ packets per seconds, CPU usage, memory utilization, and others).
+- Kea uses Continuous Integration (CI). This means that the great majority of tests (all unit and system
+ tests, and in some cases also performance tests) are run for every commit. Many "lighter" tests are
+ run on branches, before the code is even accepted.
+- Many unit and system tests check for negative scenarios, such as incomplete,
+ broken, or truncated packets, API commands, and configuration files, as well as incorrect sequences (such as sending
+ packets in an invalid order) and more.
+- The Kea development team uses many tools that perform automatic code quality checks, such as danger, as well as
+ internally developed sanity checkers.
+- The Kea team uses the following static code analyzers: Coverity Scan, shellcheck, and danger.
+- The Kea team uses the following dynamic code analyzers: Valgrind and Thread Sanitizer (TSAN).
+
+Fuzz Testing
+------------
+
+The Kea team has a process for running fuzz testing, using `AFL <https://github.com/google/AFL>`_. There
+are two modes which are run: the first mode fuzzes incoming packets, effectively throwing millions of mostly
+broken packets at Kea per day, while the second mode fuzzes configuration structures and forces Kea to
+attempt to load them. Kea has been fuzzed since around 2018 in both modes. The input seeds
+(the data being used to generate or "fuzz" other input) are changed periodically.
+
+Release Integrity
+-----------------
+
+All ISC software releases are signed with PGP and distributed via the ISC website, which is itself
+DNSSEC-signed, so users can be confident the software has not been tampered with.
+
+Bus Factor
+----------
+
+According to the `Core Infrastructure project <https://bestpractices.coreinfrastructure.org/>`_, a "bus
+factor" or "truck factor" is the minimum number of project members that have to suddenly disappear
+from a project ("be hit by a bus") before the project stalls due to lack of knowledgeable or competent
+personnel. It is hard to estimate precisely, but the bus factor for Kea is somewhere around five. As of
+2021, there are six core developers and two quality assurance engineers, with many additional casual
+contributors (product manager, support team, IT, etc.). The team is geographically dispersed.
diff --git a/doc/sphinx/arm/shell.rst b/doc/sphinx/arm/shell.rst
new file mode 100644
index 0000000..62a9900
--- /dev/null
+++ b/doc/sphinx/arm/shell.rst
@@ -0,0 +1,165 @@
+.. _kea-shell:
+
+*************
+The Kea Shell
+*************
+
+.. _shell-overview:
+
+Overview of the Kea Shell
+=========================
+
+The Kea Control Agent (CA, see
+:ref:`kea-ctrl-agent`) provides a RESTful control interface
+over HTTP. That API is typically expected to be used by various IPAMs
+and similar management systems. Nevertheless, there may be cases when an
+administrator wants to send a command to the CA directly, and the Kea shell
+provides a way to do this. It is a simple command-line,
+scripting-friendly, text client that is able to connect to the CA, send
+it commands with parameters, retrieve the responses, and display them.
+
+As the primary purpose of the Kea shell is as a tool in a scripting
+environment, it is not interactive. However, by following simple guidelines it can
+be run manually.
+
+Kea 1.9.0 introduced basic HTTP authentication support.
+
+Shell Usage
+===========
+
+:iscman:`kea-shell` is run as follows:
+
+.. code-block:: console
+
+ $ kea-shell [--host hostname] [--port number] [--path path] [--auth-user] [--auth-password] [--timeout seconds] [--service service-name] [command]
+
+where:
+
+- ``--host hostname`` specifies the hostname of the CA. If not
+ specified, "localhost" is used.
+
+- ``--port number`` specifies the TCP port on which the CA listens. If
+ not specified, 8000 is used.
+
+- ``--path path`` specifies the path in the URL to connect to. If not
+ specified, an empty path is used. As the CA listens at the empty
+ path, this parameter is useful only with a reverse proxy.
+
+- ``--auth-user`` specifies the user ID for basic HTTP authentication.
+ If not specified or specified as the empty string, authentication is
+ not used.
+
+- ``--auth-password`` specifies the password for basic HTTP authentication.
+ If not specified but the user ID is specified, an empty password is used.
+
+- ``--timeout seconds`` specifies the timeout (in seconds) for the
+ connection. If not given, 10 seconds is used.
+
+- ``--service service-name`` specifies the target of a command. If not
+ given, the CA is used as the target. This may be used more than once
+ to specify multiple targets.
+
+- ``command`` specifies the command to be sent. If not specified, the
+ :isccmd:`list-commands` command is used.
+
+Other switches are:
+
+- ``-h`` - prints a help message.
+
+- ``-v`` - prints the software version.
+
+See :ref:`shell-tls` for new command-line arguments associated with TLS/HTTPS support.
+
+Once started, the shell reads the parameters for the command from standard
+input, which are expected to be in JSON format. When all have been read,
+the shell establishes a connection with the CA using HTTP, sends the
+command, and awaits a response. Once that is received, it is displayed
+on standard output.
+
+For a list of available commands, see :ref:`ctrl-channel`;
+additional commands may be provided by hook libraries. For a list of
+all supported commands from the CA, use the :isccmd:`list-commands` command.
+
+The following shows a simple example of usage:
+
+.. code-block:: console
+
+ $ kea-shell --host 192.0.2.1 --port 8001 --service dhcp4 list-commands
+ ^D
+
+After the command line is entered, the program waits for command
+parameters to be entered. Since :isccmd:`list-commands` does not take any
+arguments, Ctrl-D (represented in the above example by "^D")
+indicates end-of-file and terminates the parameter input. The shell
+then contacts the CA and prints out the list of available commands
+returned for the service named ``dhcp4``.
+
+The Kea shell will likely be most frequently used in
+scripts; the next example shows a simple scripted execution. It sends
+the command :isccmd:`config-write` to the CA (the ``--service`` parameter has not
+been used), along with the parameters specified in param.json. The
+result will be stored in result.json.
+
+.. code-block:: console
+
+ $ cat param.json
+ "filename": "my-config-file.json"
+ $ cat param.json | kea-shell --host 192.0.2.1 config-write > result.json
+
+When a reverse proxy is used to de-multiplex requests to different
+servers, the default empty path in the URL is not enough, so the
+``--path`` parameter should be used. For instance, if requests to the
+"/kea" path are forwarded to the CA this can be used:
+
+.. code-block:: console
+
+ $ kea-shell --host 192.0.2.1 --port 8001 --path kea ...
+
+The Kea shell requires Python to be installed on the system. It has been
+tested with various versions of Python 3, up to 3.5.
+Since not every Kea deployment uses this feature and there are
+deployments that do not have Python, the Kea shell is not enabled by
+default. To use it, specify ``--enable-shell`` when running ``configure``
+during the installation of Kea. When building on Debian systems,
+``--with-site-packages=...`` may also be useful.
+
+.. note::
+
+ From Kea 2.4.0 version, the :iscman:`kea-shell` no longer supports Python 2.7.
+
+The Kea shell is intended to serve more as a demonstration of the
+RESTful interface's capabilities (and, perhaps, an illustration for
+people interested in integrating their management environments with Kea)
+than as a serious management client. It is not likely to be
+significantly expanded in the future; it is, and will remain, a simple
+tool.
+
+.. note::
+
+ When using this tool with basic HTTP authentication, please keep in
+ mind that command-line arguments are not hidden from local users.
+
+.. _shell-tls:
+
+TLS Support
+===========
+
+Since Kea 1.9.6, :iscman:`kea-shell` supports HTTPS connections. The TLS/HTTPS
+support requires Python 3. The additional command-line arguments are:
+
+- ``--ca`` specifies the file or directory name of the Certification
+ Authority. If not specified, HTTPS is not used.
+
+- ``--cert`` specifies the file name of the user end-entity public key
+ certificate. If specified, the file name of the user key must also be specified.
+
+- ``--key`` specifies the file name of the user key file. If specified,
+ the file name of the user certificate must also be specified.
+ Encrypted key files are not supported.
+
+For example, a basic HTTPS request to get a list of commands could
+look like this:
+
+.. code-block:: console
+
+ $ kea-shell --host 127.0.0.1 --port 8000 --ca ./kea-ca.crt list-commands
diff --git a/doc/sphinx/arm/stats.rst b/doc/sphinx/arm/stats.rst
new file mode 100644
index 0000000..b37b37a
--- /dev/null
+++ b/doc/sphinx/arm/stats.rst
@@ -0,0 +1,1043 @@
+.. _stats:
+
+**********
+Statistics
+**********
+
+Statistics Overview
+===================
+
+Both Kea DHCP servers support statistics gathering. A working DHCP
+server encounters various events that can cause certain statistics to be
+collected. For example, a DHCPv4 server may receive a packet
+(the ``pkt4-received`` statistic increases by one) that after parsing is
+identified as a DHCPDISCOVER (``pkt4-discover-received``). The server
+processes it and decides to send a DHCPOFFER representing its answer
+(the ``pkt4-offer-sent`` and ``pkt4-sent statistics`` increase by one). Such
+events happen frequently, so it is not uncommon for the statistics to have
+values in the high thousands. They can serve as an easy and powerful
+tool for observing a server's and a network's health. For example, if
+the ``pkt4-received`` statistic stops growing, it means that the clients'
+packets are not reaching the server.
+
+There are four types of statistics:
+
+- *integer* - this is the most common type. It is implemented as a
+ 64-bit integer (int64_t in C++), so it can hold any value between
+ -2^63 to 2^63-1.
+
+- *big integer* - this type is inteded for holding large numbers. It is
+ implemented as a 128-bit integer (boost::multiprecision::int128_t in C++), so
+ it can hold any value between -2^127 to 2^127-1.
+
+- *floating point* - this type is intended to store floating-point
+ precision. It is implemented as a C++ double type.
+
+- *duration* - this type is intended for recording time periods. It
+ uses the \`boost::posix_time::time_duration type, which stores hours,
+ minutes, seconds, and microseconds.
+
+- *string* - this type is intended for recording statistics in text
+ form. It uses the C++ std::string type.
+
+During normal operation, the DHCPv4 and DHCPv6 servers gather
+statistics. For a list of DHCPv4 and DHCPv6 statistics, see
+:ref:`dhcp4-stats` and :ref:`dhcp6-stats`, respectively.
+
+To extract data from the statistics module, the control channel can be
+used. See :ref:`ctrl-channel` for details. It is possible to
+retrieve a single statistic or all statistics, reset the statistics (i.e.
+set them to a neutral value, typically zero), or even completely remove a
+single statistic or all statistics. See the section :ref:`command-stats`
+for a list of statistics-oriented commands.
+
+Statistics can be used by external tools to monitor Kea. One example of such a tool is Stork.
+See :ref:`stork` for details on how to use it and other data sources to retrieve statistics periodically
+to get better insight into Kea's health and operational status.
+
+.. _stats-lifecycle:
+
+Statistics Lifecycle
+====================
+
+All of the statistics supported by Kea's servers are initialized upon the servers' startup
+and are returned in response to the commands such as
+:isccmd:`statistic-get-all`. The runtime statistics concerning DHCP packets
+processed are initially set to 0 and are reset upon the server
+restart.
+
+Per-subnet statistics are recalculated when reconfiguration takes place.
+
+In general, once a statistic is initialized it is held in the manager until
+explicitly removed, via :isccmd:`statistic-remove` or
+:isccmd:`statistic-remove-all`,
+or when the server is shut down.
+
+Removing a statistic that is updated frequently makes little sense, as
+it will be re-added when the server code next records that statistic.
+The :isccmd:`statistic-remove` and
+:isccmd:`statistic-remove-all` commands are
+intended to remove statistics that are not expected to be observed in
+the near future. For example, a misconfigured device in a network may
+cause clients to report duplicate addresses, so the server will report
+increasing values of ``pkt4-decline-received``. Once the problem is found
+and the device is removed, the system administrator may want to remove
+the ``pkt4-decline-received`` statistic so that it is no longer reported, until
+and unless a duplicate address is again detected.
+
+.. isccmd:: stats
+.. _command-stats:
+
+Commands for Manipulating Statistics
+====================================
+
+There are several commands defined that can be used for accessing
+(``-get``), resetting to zero or a neutral value (``-reset``), or removing a
+statistic completely (``-remove``). The statistics time-based
+limit (``-sample-age-set``) and size-based limit (``-sample-count-set``), which
+control how long or how many samples of a given statistic are retained, can also
+be changed.
+
+The difference between ``-reset`` and ``-remove`` is somewhat subtle.
+The ``-reset`` command sets the value of the statistic to zero or a neutral value,
+so that after this operation, the statistic has a value of 0 (integer),
+0.0 (float), 0h0m0s0us (duration), or "" (string).
+When requested, a statistic with the values mentioned is returned.
+``-remove`` removes a statistic completely, so the statistic is no longer
+reported. However, the server code may add it back if there is a reason
+to record it.
+
+.. note::
+
+ The following sections describe commands that can be sent to the
+ server; the examples are not fragments of a configuration file. For
+ more information on sending commands to Kea, see
+ :ref:`ctrl-channel`.
+
+.. isccmd:: statistic-get
+.. _command-statistic-get:
+
+The ``statistic-get`` Command
+-----------------------------
+
+The :isccmd:`statistic-get` command retrieves a single statistic. It takes a
+single-string parameter called ``name``, which specifies the statistic
+name. An example command may look like this:
+
+::
+
+ {
+ "command": "statistic-get",
+ "arguments": {
+ "name": "pkt4-received"
+ }
+ }
+
+The server returns details of the requested statistic, with a result of
+0 indicating success and the specified statistic as the value of the
+``arguments`` parameter. If the requested statistic is not found, the
+response contains an empty map, i.e. only { } as an argument, but
+the status code still indicates success (0).
+
+Here is an example response:
+
+::
+
+ {
+ "command": "statistic-get",
+ "arguments": {
+ "pkt4-received": [ [ 125, "2019-07-30 10:11:19.498739" ], [ 100, "2019-07-30 10:11:19.498662" ] ]
+ },
+ "result": 0
+ }
+
+.. isccmd:: statistic-reset
+.. _command-statistic-reset:
+
+The ``statistic-reset`` Command
+-------------------------------
+
+The :isccmd:`statistic-reset` command sets the specified statistic to its
+neutral value: 0 for integer, 0.0 for float, 0h0m0s0us for time
+duration, and "" for string type. It takes a single-string parameter
+called ``name``, which specifies the statistic name. An example command
+may look like this:
+
+::
+
+ {
+ "command": "statistic-reset",
+ "arguments": {
+ "name": "pkt4-received"
+ }
+ }
+
+If the specific statistic is found and the reset is successful, the
+server responds with a status of 0, indicating success, and an empty
+parameters field. If an error is encountered (e.g. the requested
+statistic was not found), the server returns a status code of 1 (error)
+and the text field contains the error description.
+
+.. isccmd:: statistic-remove
+.. _command-statistic-remove:
+
+The ``statistic-remove`` Command
+--------------------------------
+
+The :isccmd:`statistic-remove` command deletes a single statistic. It
+takes a single-string parameter called ``name``, which specifies the
+statistic name. An example command may look like this:
+
+::
+
+ {
+ "command": "statistic-remove",
+ "arguments": {
+ "name": "pkt4-received"
+ }
+ }
+
+If the specific statistic is found and its removal is successful, the
+server responds with a status of 0, indicating success, and an empty
+parameters field. If an error is encountered (e.g. the requested
+statistic was not found), the server returns a status code of 1 (error)
+and the text field contains the error description.
+
+.. isccmd:: statistic-get-all
+.. _command-statistic-get-all:
+
+The ``statistic-get-all`` Command
+---------------------------------
+
+The :isccmd:`statistic-get-all` command retrieves all statistics recorded. An
+example command may look like this:
+
+::
+
+ {
+ "command": "statistic-get-all",
+ "arguments": { }
+ }
+
+The server responds with details of all recorded statistics, with a
+result set to 0 to indicate that it iterated over all statistics (even
+when the total number of statistics is zero).
+
+Here is an example response returning all collected statistics:
+
+::
+
+ {
+ "command": "statistic-get-all",
+ "arguments": {
+ "cumulative-assigned-addresses": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836166"
+ ]
+ ],
+ "declined-addresses": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836156"
+ ]
+ ],
+ "pkt4-ack-received": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616247"
+ ]
+ ],
+ "pkt4-ack-sent": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616290"
+ ]
+ ],
+ "pkt4-decline-received": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616296"
+ ]
+ ],
+ "pkt4-discover-received": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616303"
+ ]
+ ],
+ "pkt4-inform-received": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616308"
+ ]
+ ],
+ "pkt4-nak-received": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616312"
+ ]
+ ],
+ "pkt4-nak-sent": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616314"
+ ]
+ ],
+ "pkt4-offer-received": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616318"
+ ]
+ ],
+ "pkt4-offer-sent": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616323"
+ ]
+ ],
+ "pkt4-parse-failed": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616326"
+ ]
+ ],
+ "pkt4-receive-drop": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616330"
+ ]
+ ],
+ "pkt4-received": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616335"
+ ]
+ ],
+ "pkt4-release-received": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616339"
+ ]
+ ],
+ "pkt4-request-received": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616343"
+ ]
+ ],
+ "pkt4-sent": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616348"
+ ]
+ ],
+ "pkt4-unknown-received": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616354"
+ ]
+ ],
+ "reclaimed-declined-addresses": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836159"
+ ]
+ ],
+ "reclaimed-leases": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836163"
+ ]
+ ],
+ "subnet[1].assigned-addresses": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836173"
+ ]
+ ],
+ "subnet[1].cumulative-assigned-addresses": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836098"
+ ]
+ ],
+ "subnet[1].declined-addresses": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836178"
+ ]
+ ],
+ "subnet[1].pool[0].assigned-addresses": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836205"
+ ]
+ ],
+ "subnet[1].pool[0].cumulative-assigned-addresses": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836137"
+ ]
+ ],
+ "subnet[1].pool[0].declined-addresses": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836213"
+ ]
+ ],
+ "subnet[1].pool[0].reclaimed-declined-addresses": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836225"
+ ]
+ ],
+ "subnet[1].pool[0].reclaimed-leases": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836236"
+ ]
+ ],
+ "subnet[1].pool[0].total-addresses": [
+ [
+ 11010049,
+ "2023-06-13 20:42:46.836128"
+ ]
+ ],
+ "subnet[1].reclaimed-declined-addresses": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836186"
+ ]
+ ],
+ "subnet[1].reclaimed-leases": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836194"
+ ]
+ ],
+ "subnet[1].total-addresses": [
+ [
+ 11010049,
+ "2023-06-13 20:42:46.836083"
+ ]
+ ],
+ "subnet[1].v4-lease-reuses": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836105"
+ ]
+ ],
+ "subnet[1].v4-reservation-conflicts": [
+ [
+ 0,
+ "2023-06-13 20:42:46.836111"
+ ]
+ ],
+ "v4-allocation-fail": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616358"
+ ]
+ ],
+ "v4-allocation-fail-classes": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616363"
+ ]
+ ],
+ "v4-allocation-fail-no-pools": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616368"
+ ]
+ ],
+ "v4-allocation-fail-shared-network": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616372"
+ ]
+ ],
+ "v4-allocation-fail-subnet": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616376"
+ ]
+ ],
+ "v4-lease-reuses": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616410"
+ ]
+ ],
+ "v4-reservation-conflicts": [
+ [
+ 0,
+ "2023-06-13 20:42:46.616412"
+ ]
+ ]
+ },
+ "result": 0
+ }
+
+or
+
+::
+
+ {
+ "command": "statistic-get-all",
+ "arguments": {
+ "cumulative-assigned-nas": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196757"
+ ]
+ ],
+ "cumulative-assigned-pds": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196758"
+ ]
+ ],
+ "declined-addresses": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196754"
+ ]
+ ],
+ "pkt6-advertise-received": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177731"
+ ]
+ ],
+ "pkt6-advertise-sent": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177739"
+ ]
+ ],
+ "pkt6-decline-received": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177739"
+ ]
+ ],
+ "pkt6-dhcpv4-query-received": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177740"
+ ]
+ ],
+ "pkt6-dhcpv4-response-received": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177740"
+ ]
+ ],
+ "pkt6-dhcpv4-response-sent": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177741"
+ ]
+ ],
+ "pkt6-infrequest-received": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177742"
+ ]
+ ],
+ "pkt6-parse-failed": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177742"
+ ]
+ ],
+ "pkt6-rebind-received": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177743"
+ ]
+ ],
+ "pkt6-receive-drop": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177743"
+ ]
+ ],
+ "pkt6-received": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177744"
+ ]
+ ],
+ "pkt6-release-received": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177744"
+ ]
+ ],
+ "pkt6-renew-received": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177745"
+ ]
+ ],
+ "pkt6-reply-received": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177745"
+ ]
+ ],
+ "pkt6-reply-sent": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177746"
+ ]
+ ],
+ "pkt6-request-received": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177747"
+ ]
+ ],
+ "pkt6-sent": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177747"
+ ]
+ ],
+ "pkt6-solicit-received": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177748"
+ ]
+ ],
+ "pkt6-unknown-received": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177748"
+ ]
+ ],
+ "reclaimed-declined-addresses": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196755"
+ ]
+ ],
+ "reclaimed-leases": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196756"
+ ]
+ ],
+ "subnet[1].assigned-nas": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196760"
+ ]
+ ],
+ "subnet[1].assigned-pds": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196761"
+ ]
+ ],
+ "subnet[1].cumulative-assigned-nas": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196727"
+ ]
+ ],
+ "subnet[1].cumulative-assigned-pds": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196729"
+ ]
+ ],
+ "subnet[1].declined-addresses": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196763"
+ ]
+ ],
+ "subnet[1].pd-pool[0].assigned-pds": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196785"
+ ]
+ ],
+ "subnet[1].pd-pool[0].cumulative-assigned-pds": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196744"
+ ]
+ ],
+ "subnet[1].pd-pool[0].reclaimed-leases": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196789"
+ ]
+ ],
+ "subnet[1].pd-pool[0].total-pds": [
+ [
+ 256,
+ "2023-06-13 21:28:57.196741"
+ ]
+ ],
+ "subnet[1].pool[0].assigned-nas": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196773"
+ ]
+ ],
+ "subnet[1].pool[0].cumulative-assigned-nas": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196739"
+ ]
+ ],
+ "subnet[1].pool[0].declined-addresses": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196775"
+ ]
+ ],
+ "subnet[1].pool[0].reclaimed-declined-addresses": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196779"
+ ]
+ ],
+ "subnet[1].pool[0].reclaimed-leases": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196783"
+ ]
+ ],
+ "subnet[1].pool[0].total-nas": [
+ [
+ 281474976710656,
+ "2023-06-13 21:28:57.196736"
+ ]
+ ],
+ "subnet[1].reclaimed-declined-addresses": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196766"
+ ]
+ ],
+ "subnet[1].reclaimed-leases": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196770"
+ ]
+ ],
+ "subnet[1].total-nas": [
+ [
+ 281474976710656,
+ "2023-06-13 21:28:57.196720"
+ ]
+ ],
+ "subnet[1].total-pds": [
+ [
+ 256,
+ "2023-06-13 21:28:57.196724"
+ ]
+ ],
+ "subnet[1].v6-ia-na-lease-reuses": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196731"
+ ]
+ ],
+ "subnet[1].v6-ia-pd-lease-reuses": [
+ [
+ 0,
+ "2023-06-13 21:28:57.196733"
+ ]
+ ],
+ "v6-allocation-fail": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177749"
+ ]
+ ],
+ "v6-allocation-fail-classes": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177755"
+ ]
+ ],
+ "v6-allocation-fail-no-pools": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177756"
+ ]
+ ],
+ "v6-allocation-fail-shared-network": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177756"
+ ]
+ ],
+ "v6-allocation-fail-subnet": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177757"
+ ]
+ ],
+ "v6-ia-na-lease-reuses": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177757"
+ ]
+ ],
+ "v6-ia-pd-lease-reuses": [
+ [
+ 0,
+ "2023-06-13 21:28:57.177758"
+ ]
+ ]
+ },
+ "result": 0
+ }
+
+or
+
+::
+
+ {
+ "command": "statistic-get-all",
+ "arguments": {
+ "ncr-error": [
+ [
+ 0,
+ "2023-06-13 21:42:54.627751"
+ ]
+ ],
+ "ncr-invalid": [
+ [
+ 0,
+ "2023-06-13 21:42:54.627749"
+ ]
+ ],
+ "ncr-received": [
+ [
+ 0,
+ "2023-06-13 21:42:54.627737"
+ ]
+ ],
+ "update-error": [
+ [
+ 0,
+ "2023-06-13 21:42:54.627759"
+ ]
+ ],
+ "update-sent": [
+ [
+ 0,
+ "2023-06-13 21:42:54.627752"
+ ]
+ ],
+ "update-signed": [
+ [
+ 0,
+ "2023-06-13 21:42:54.627753"
+ ]
+ ],
+ "update-success": [
+ [
+ 0,
+ "2023-06-13 21:42:54.627755"
+ ]
+ ],
+ "update-timeout": [
+ [
+ 0,
+ "2023-06-13 21:42:54.627757"
+ ]
+ ],
+ "update-unsigned": [
+ [
+ 0,
+ "2023-06-13 21:42:54.627754"
+ ]
+ ]
+ },
+ "result": 0
+ }
+
+.. isccmd:: statistic-reset-all
+.. _command-statistic-reset-all:
+
+The ``statistic-reset-all`` Command
+-----------------------------------
+
+The :isccmd:`statistic-reset` command sets all statistics to their neutral
+values: 0 for integer, 0.0 for float, 0h0m0s0us for time duration, and
+"" for string type. An example command may look like this:
+
+::
+
+ {
+ "command": "statistic-reset-all",
+ "arguments": { }
+ }
+
+If the operation is successful, the server responds with a status of 0,
+indicating success, and an empty parameters field. If an error is
+encountered, the server returns a status code of 1 (error) and the text
+field contains the error description.
+
+.. isccmd:: statistic-remove-all
+.. _command-statistic-remove-all:
+
+The ``statistic-remove-all`` Command
+------------------------------------
+
+The :isccmd:`statistic-remove-all` command attempts to delete all statistics. An
+example command may look like this:
+
+::
+
+ {
+ "command": "statistic-remove-all",
+ "arguments": { }
+ }
+
+If the removal of all statistics is successful, the server responds with
+a status of 0, indicating success, and an empty parameters field. If an
+error is encountered, the server returns a status code of 1 (error) and
+the text field contains the error description.
+
+.. isccmd:: statistic-sample-age-set
+.. _command-statistic-sample-age-set:
+
+The ``statistic-sample-age-set`` Command
+----------------------------------------
+
+The :isccmd:`statistic-sample-age-set` command sets a time-based limit
+on samples for a given statistic. It takes two parameters: a string
+called ``name``, which specifies the statistic name, and an integer value called
+``duration``, which specifies the time limit for the given statistic in seconds.
+An example command may look like this:
+
+::
+
+ {
+ "command": "statistic-sample-age-set",
+ "arguments": {
+ "name": "pkt4-received",
+ "duration": 1245
+ }
+
+ }
+
+If the command is successful, the server responds with a status of
+0, indicating success,
+and an empty parameters field. If an error is encountered (e.g. the
+requested statistic was not found), the server returns a status code
+of 1 (error) and the text field contains the error description.
+
+.. isccmd:: statistic-sample-age-set-all
+.. _command-statistic-sample-age-set-all:
+
+The ``statistic-sample-age-set-all`` Command
+--------------------------------------------
+
+The :isccmd:`statistic-sample-age-set-all` command sets time-based limits
+on samples for all statistics. It takes a single-integer parameter
+called ``duration``, which specifies the time limit for the statistic
+in seconds. An example command may look like this:
+
+::
+
+ {
+ "command": "statistic-sample-age-set-all",
+ "arguments": {
+ "duration": 1245
+ }
+
+ }
+
+If the command is successful, the server responds with a status of
+0, indicating success,
+and an empty parameters field. If an error is encountered, the server returns
+a status code of 1 (error) and the text field contains the error description.
+
+.. isccmd:: statistic-sample-count-set
+.. _command-statistic-sample-count-set:
+
+The ``statistic-sample-count-set`` Command
+------------------------------------------
+
+The :isccmd:`statistic-sample-count-set` command sets a size-based limit
+on samples for a given statistic. An example command may look
+like this:
+
+::
+
+ {
+ "command": "statistic-sample-count-set",
+ "arguments": {
+ "name": "pkt4-received",
+ "max-samples": 100
+ }
+
+ }
+
+If the command is successful, the server responds with a status of
+0, indicating success,
+and an empty parameters field. If an error is encountered (e.g. the
+requested statistic was not found), the server returns a status code
+of 1 (error) and the text field contains the error description.
+
+.. isccmd:: statistic-sample-count-set-all
+.. _command-statistic-sample-count-set-all:
+
+The ``statistic-sample-count-set-all`` Command
+----------------------------------------------
+
+The :isccmd:`statistic-sample-count-set-all` command sets size-based limits
+on samples for all statistics. An example command may look
+like this:
+
+::
+
+ {
+ "command": "statistic-sample-count-set-all",
+ "arguments": {
+ "max-samples": 100
+ }
+
+ }
+
+If the command is successful, the server responds with a status of
+0, indicating success,
+and an empty parameters field. If an error is encountered, the server returns
+a status code of 1 (error) and the text field contains the error description.
+
+.. _time-series:
+
+Time Series
+===========
+
+With certain statistics, a single isolated data point may be useful. However,
+some statistics, such as received
+packet size, packet processing time, or number of database queries needed to
+process a packet, are not cumulative and it is useful to keep many data
+points, perhaps to do some statistical analysis afterwards.
+
+
+Each Kea statistic holds 20 data points; setting such
+a limit prevents unlimited memory growth.
+There are two ways to define the limits: time-based (e.g. keep samples from
+the last 5 minutes) and size-based. The size-based
+limit can be changed using one of two commands: :isccmd:`statistic-sample-count-set`,
+to set a size limit for a single statistic, and :isccmd:`statistic-sample-count-set-all`,
+to set size-based limits for all statistics. To set time-based
+limits for a single statistic, use :isccmd:`statistic-sample-age-set`; use
+:isccmd:`statistic-sample-age-set-all` to set time-based limits for all statistics.
+For a given statistic only one type of limit can be active; storage
+is limited by either time or size, not both.
diff --git a/doc/sphinx/arm/stork.rst b/doc/sphinx/arm/stork.rst
new file mode 100644
index 0000000..bfa4a06
--- /dev/null
+++ b/doc/sphinx/arm/stork.rst
@@ -0,0 +1,50 @@
+.. _stork:
+
+*************************
+Monitoring Kea With Stork
+*************************
+
+Most administrators want to be able to monitor any Kea services that are running. Kea offers so many
+pieces of information - configuration files, API, statistics, logs, open database content, and more -
+that it may sometimes
+be overwhelming to keep up. ISC's Stork project is intended to address this problem for both Kea
+and BIND 9. Stork is useful in a variety of ways:
+
+- Stork can be used as a dashboard. It provides insight into what exactly is happening
+ on the servers. In particular, it allows users to: see up-to-date details regarding pool
+ utilization in subnets and shared networks; monitor the state of the HA pair (and
+ provide extra insight in case of failover and recovery events); list, filter, and
+ search for specific host reservations; and more. Only
+ a single Stork server needs to be deployed, and one Stork agent on each machine to be monitored.
+
+- The Stork agent can integrate Kea with Prometheus and Grafana. Once the Stork
+ agent is active on the server, it serves as a Prometheus exporter. Users who have deployed
+ Prometheus in their networks can visualize statistics as time series using Grafana.
+
+- Stork can act as both a dashboard and an integrator for Prometheus/Grafana. Once Stork
+ is linked to where Grafana is deployed on the network, users can inspect the current status and
+ visit a customized link to Grafana to see how a given property behaves over time.
+
+Stork is available as source code, but also as native deb and RPM packages, which makes it easy
+to install on most popular systems. For more details, please see the
+`Stork ARM <https://stork.readthedocs.io>`_ or the `Stork project page <https://gitlab.isc.org/isc-projects/stork>`_.
+The ARM has a nice collection of screenshots that is frequently updated, to give users
+an idea of what is currently available. Stork is in the midst of full development with
+monthly releases, so please check back frequently.
+
+.. _grafana:
+.. _prometheus:
+
+Kea Statistics in Grafana
+=========================
+
+The ISC Stork project provides an agent that can be deployed alongside Kea. It
+exposes Kea statistics in a format that is accepted by Prometheus.
+One of the major benefits of Prometheus is that it turns repeated one-time observations into time series,
+which lets users monitor how certain behaviors change over time. It is easy to use other tools
+to visualize data available in Prometheus; the most common approach is to use
+Grafana to provide visual dashboards. The Stork project provides dashboard
+definitions for Kea that can be imported into Grafana very easily.
+
+Learn more about Prometheus and Grafana on their websites: `Prometheus <https://prometheus.io/>`
+and `Grafana <https://grafana.com/>`.
diff --git a/doc/sphinx/conf.py b/doc/sphinx/conf.py
new file mode 100644
index 0000000..b36ec1d
--- /dev/null
+++ b/doc/sphinx/conf.py
@@ -0,0 +1,307 @@
+# -*- coding: utf-8 -*-
+#
+# Configuration file for the Sphinx documentation builder.
+#
+# This file does only contain a selection of the most common options. For a
+# full list see the documentation:
+# http://www.sphinx-doc.org/en/master/config
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+# to avoid "sphinx.errors.SphinxParallelError: RecursionError: maximum recursion depth exceeded while pickling an object"
+import sys
+sys.setrecursionlimit(5000)
+
+# -- Project information -----------------------------------------------------
+
+project = 'Kea'
+copyright = '2019-2024, Internet Systems Consortium'
+author = 'Internet Systems Consortium'
+
+# get current kea version
+config_ac_path = '../../configure.ac'
+changelog_path = '../../ChangeLog'
+release = 'UNRELEASED'
+with open(config_ac_path) as f:
+ for line in f.readlines():
+ if line.startswith('AC_INIT(kea'):
+ parts = line.split(',')
+ release = parts[1].strip()
+ # If the first line of the ChangeLog announces release, it means
+ # that this is the final release.
+ dash_parts = release.split('-')
+ candidate_release = dash_parts[0]
+ with open(changelog_path) as changelog_file:
+ first_line = changelog_file.readline()
+ if candidate_release in first_line and "released" in first_line:
+ release = candidate_release
+ break
+version = release
+dashed_version_series='-'.join(version.split('.')[0:2])
+
+# -- General configuration ---------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinx.ext.todo',
+ 'sphinx.ext.mathjax',
+]
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# Additional docs
+messages_doc = 'kea-messages'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = "en"
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = [
+ '_build', 'Thumbs.db', '.DS_Store',
+ # included files need to be excluded to avoid duplicate labels
+ 'arm/platforms.rst',
+ 'arm/hooks-bootp.rst',
+ 'arm/hooks-class-cmds.rst',
+ 'arm/hooks-cb-cmds.rst',
+ 'arm/config-backend.rst',
+ 'arm/hooks-ha.rst',
+ 'arm/hooks-host-cache.rst',
+ 'arm/hooks-lease-cmds.rst',
+ 'arm/hooks-lease-query.rst',
+ 'arm/hooks-limits.rst',
+ 'arm/hooks-perfmon.rst',
+ 'arm/hooks-ping-check.rst',
+ 'arm/hooks-radius.rst',
+ 'arm/hooks-rbac.rst',
+ 'arm/hooks-run-script.rst',
+ 'arm/hooks-stat-cmds.rst',
+ 'arm/hooks-ddns-tuning.rst',
+ 'arm/hammer.rst',
+ 'arm/ext-netconf.rst',
+ 'arm/ext-gss-tsig.rst',
+ 'arm/ext-radius.rst',
+ 'grammar/grammar-ca-parser.rst',
+ 'grammar/grammar-d2-parser.rst',
+ 'grammar/grammar-dhcp4-parser.rst',
+ 'grammar/grammar-dhcp6-parser.rst',
+ 'grammar/grammar-netconf-parser.rst',
+ 'arm/hooks-flex-id.rst',
+ 'arm/hooks-flex-option.rst',
+ 'arm/hooks-legal-log.rst',
+ 'arm/hooks-gss-tsig.rst',
+ 'arm/hooks-host-cmds.rst',
+ 'arm/hooks-cb-mysql.rst',
+ 'arm/hooks-cb-pgsql.rst',
+ 'arm/hooks-limits.rst',
+ 'arm/hooks-rbac.rst',
+ 'arm/hooks-run-script.rst',
+ 'arm/hooks-subnet-cmds.rst',
+ 'arm/hooks-user-chk.rst',
+]
+
+# Report broken references.
+nitpicky = True
+
+# Leave quotes and dashes unchanged and don't convert them to typographically
+# correct entities.
+smartquotes = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = None
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+#html_theme = 'alabaster'
+html_theme = 'sphinx_rtd_theme'
+html_logo = 'static/kea-imageonly-100bw.png'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#
+#html_theme_options = {
+# "logo": "kea-logo-100x70.png",
+#}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['static']
+
+# Custom sidebar templates, must be a dictionary that maps document names
+# to template names.
+#
+# The default sidebars (for documents that don't match any pattern) are
+# defined by theme itself. Builtin themes are using these templates by
+# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
+# 'searchbox.html']``.
+#
+# html_sidebars = {}
+
+
+# -- Options for HTMLHelp output ---------------------------------------------
+
+# Output file base name for HTML help builder.
+#htmlhelp_basename = 'KeaAdministratorReferenceManualdoc'
+
+
+# -- Options for LaTeX output ------------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ #
+ # 'papersize': 'letterpaper',
+
+ # The font size ('10pt', '11pt' or '12pt').
+ #
+ # 'pointsize': '10pt',
+
+ # Additional stuff for the LaTeX preamble.
+ #
+ # 'preamble': '',
+
+ # Latex figure (float) alignment
+ #
+ # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, 'kea-arm.tex', 'Kea Administrator Reference Manual Documentation', author, 'manual'),
+]
+
+latex_logo = 'static/kea-logo-200.png'
+
+if os.getenv("READTHEDOCS", "False") == "False":
+ latex_documents.append((messages_doc, 'kea-messages.tex', 'Kea Messages Manual', author, 'manual'))
+
+
+# -- Options for manual page output ------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'kea-arm', 'Kea Administrator Reference Manual Documentation', [author], 1),
+ ('man/kea-admin.8', 'kea-admin', 'Shell script for managing Kea databases', author, 8),
+ ('man/keactrl.8', 'keactrl', 'Shell script for managing Kea', author, 8),
+ ('man/kea-ctrl-agent.8', 'kea-ctrl-agent', 'Control Agent process in Kea', author, 8),
+ ('man/kea-dhcp4.8', 'kea-dhcp4', 'DHCPv4 server in Kea', author, 8),
+ ('man/kea-dhcp6.8', 'kea-dhcp6', 'DHCPv6 server in Kea', author, 8),
+ ('man/kea-dhcp-ddns.8', 'kea-dhcp-ddns', 'DHCP-DDNS process in Kea', author, 8),
+ ('man/kea-lfc.8', 'kea-lfc', 'Lease File Cleanup process in Kea', author, 8),
+ ('man/kea-netconf.8', 'kea-netconf', 'NETCONF agent for configuring Kea', author, 8),
+ ('man/kea-shell.8', 'kea-shell', 'Text client for Control Agent process', author, 8),
+ ('man/perfdhcp.8', 'perfdhcp', 'DHCP benchmarking tool', author, 8),
+]
+
+
+# -- Extension configuration -------------------------------------------------
+
+# -- Options for todo extension ----------------------------------------------
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = True
+
+# -- Substitutions -----------------------------------------------------------
+
+rst_prolog="""
+.. |cloudsmith_repo| replace:: kea-{dashed_version_series}
+""".format(dashed_version_series=dashed_version_series)
+
+# -- Functions ---------------------------------------------------------------
+
+# Do generation of api.rst and kea-messages.rst here in conf.py instead of Makefile.am
+# so they are available on ReadTheDocs as there makefiles are not used for building docs.
+def run_generate_docs(_):
+ import os
+ import sys
+ src_dir = os.path.abspath(os.path.dirname(__file__))
+ print(src_dir)
+ sys.path.append(src_dir)
+
+ import api2doc
+ with open(os.path.join(src_dir, 'api-files.txt')) as af:
+ api_files = af.read().split()
+ api_files = [os.path.abspath(os.path.join(src_dir, '../..', af)) for af in api_files]
+ api2doc.generate(api_files, os.path.join(src_dir, 'api.rst'))
+
+ import mes2doc
+ with open(os.path.join(src_dir, 'mes-files.txt')) as mf:
+ mes_files = mf.read().split()
+ mes_files = [os.path.abspath(os.path.join(src_dir, '../..', mf)) for mf in mes_files]
+ mes2doc.generate(mes_files, os.path.join(src_dir, 'kea-messages.rst'))
+
+ # Sphinx has some limitations. It can't import files from outside its directory, which
+ # in our case is src/sphinx. On the other hand, we need to have platforms.rst file
+ # in top level directory, so it's easily accessible by prospective and first time
+ # users. Furthermore, ReadTheDocs does not use the makefile system at all and they rely
+ # on sphinx-build only. As a result we need to conduct some Makefile-like operations
+ # here. This requires us to copy (or link) the file from the top level to sphinx subdir.
+ #
+ # The first entry on this list is the actual file to copy, the second is a unique name
+ # that will be used when copied over to arm/ directory.
+ FILES_TO_COPY = [
+ [ '../../platforms.rst', 'platforms.rst' ],
+ [ '../examples/template-power-user-home/info.md', 'template-power-user-home.md' ],
+ [ '../examples/template-power-user-home/kea-ca-1.conf', 'template-power-user-home-ca-1.conf' ],
+ [ '../examples/template-power-user-home/kea-ca-2.conf', 'template-power-user-home-ca-2.conf' ],
+ [ '../examples/template-power-user-home/kea-dhcp4-1.conf', 'template-power-user-home-dhcp4-1.conf' ],
+ [ '../examples/template-power-user-home/kea-dhcp4-2.conf', 'template-power-user-home-dhcp4-2.conf' ],
+ [ '../examples/template-ha-mt-tls/info.md', 'template-ha-mt-tls.md' ],
+ [ '../examples/template-ha-mt-tls/kea-ca-1.conf', 'template-ha-mt-tls-ca-1.conf' ],
+ [ '../examples/template-ha-mt-tls/kea-ca-2.conf', 'template-ha-mt-tls-ca-2.conf' ],
+ [ '../examples/template-ha-mt-tls/kea-dhcp4-1.conf', 'template-ha-mt-tls-dhcp4-1.conf' ],
+ [ '../examples/template-ha-mt-tls/kea-dhcp4-2.conf', 'template-ha-mt-tls-dhcp4-2.conf' ]
+ ]
+
+ from shutil import copyfile
+ for [a, b] in FILES_TO_COPY:
+ src = os.path.join(src_dir, a)
+ dst = os.path.join(src_dir, 'arm', b)
+ print("Copying %s to %s" % (src, dst))
+ copyfile(src, dst)
+
+# custom setup hook
+def setup(app):
+ app.add_crossref_type('isccmd', 'isccmd')
+ app.add_crossref_type('ischooklib', 'ischooklib')
+ app.add_crossref_type('iscman', 'iscman')
+ if hasattr(app, 'add_css_file'):
+ app.add_css_file('kea.css')
+ else:
+ app.add_stylesheet('kea.css')
+
+ app.connect('builder-inited', run_generate_docs)
diff --git a/doc/sphinx/grammar/grammar-ca-parser.rst b/doc/sphinx/grammar/grammar-ca-parser.rst
new file mode 100644
index 0000000..e7d35ce
--- /dev/null
+++ b/doc/sphinx/grammar/grammar-ca-parser.rst
@@ -0,0 +1,253 @@
+This grammar is generated from ``agent_parser.yy``. See :ref:`kea-ctrl-agent` for more details.
+
+.. code-block:: BNF
+ :linenos:
+
+ Grammar
+
+ $accept ::= start EOF
+
+ start ::= START_JSON json
+
+ start ::= START_AGENT agent_syntax_map
+
+ start ::= START_SUB_AGENT sub_agent
+
+ sub_agent ::= "{" global_params "}"
+
+ json ::= value
+
+ value ::= INTEGER
+ | FLOAT
+ | BOOLEAN
+ | STRING
+ | NULL
+ | map
+ | list_generic
+
+ map ::= "{" map_content "}"
+
+ map_value ::= map
+
+ map_content ::=
+ | not_empty_map
+
+ not_empty_map ::= STRING ":" value
+ | not_empty_map "," STRING ":" value
+ | not_empty_map ","
+
+ list_generic ::= "[" list_content "]"
+
+ list_content ::=
+ | not_empty_list
+
+ not_empty_list ::= value
+ | not_empty_list "," value
+ | not_empty_list ","
+
+ unknown_map_entry ::= STRING ":"
+
+ agent_syntax_map ::= "{" global_object "}"
+
+ global_object ::= "Control-agent" ":" "{" global_params "}"
+ | global_object_comma
+
+ global_object_comma ::= global_object ","
+
+ global_params ::= global_param
+ | global_params "," global_param
+ | global_params ","
+
+ global_param ::= http_host
+ | http_port
+ | trust_anchor
+ | cert_file
+ | key_file
+ | cert_required
+ | authentication
+ | control_sockets
+ | hooks_libraries
+ | loggers
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ http_host ::= "http-host" ":" STRING
+
+ http_port ::= "http-port" ":" INTEGER
+
+ trust_anchor ::= "trust-anchor" ":" STRING
+
+ cert_file ::= "cert-file" ":" STRING
+
+ key_file ::= "key-file" ":" STRING
+
+ cert_required ::= "cert-required" ":" BOOLEAN
+
+ user_context ::= "user-context" ":" map_value
+
+ comment ::= "comment" ":" STRING
+
+ hooks_libraries ::= "hooks-libraries" ":" "[" hooks_libraries_list "]"
+
+ hooks_libraries_list ::=
+ | not_empty_hooks_libraries_list
+
+ not_empty_hooks_libraries_list ::= hooks_library
+ | not_empty_hooks_libraries_list "," hooks_library
+ | not_empty_hooks_libraries_list ","
+
+ hooks_library ::= "{" hooks_params "}"
+
+ hooks_params ::= hooks_param
+ | hooks_params "," hooks_param
+ | hooks_params ","
+ | unknown_map_entry
+
+ hooks_param ::= library
+ | parameters
+
+ library ::= "library" ":" STRING
+
+ parameters ::= "parameters" ":" map_value
+
+ control_sockets ::= "control-sockets" ":" "{" control_sockets_params "}"
+
+ control_sockets_params ::= control_socket
+ | control_sockets_params "," control_socket
+ | control_sockets_params ","
+
+ control_socket ::= dhcp4_server_socket
+ | dhcp6_server_socket
+ | d2_server_socket
+ | unknown_map_entry
+
+ dhcp4_server_socket ::= "dhcp4" ":" "{" control_socket_params "}"
+
+ dhcp6_server_socket ::= "dhcp6" ":" "{" control_socket_params "}"
+
+ d2_server_socket ::= "d2" ":" "{" control_socket_params "}"
+
+ control_socket_params ::= control_socket_param
+ | control_socket_params "," control_socket_param
+ | control_socket_params ","
+
+ control_socket_param ::= socket_name
+ | socket_type
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ socket_name ::= "socket-name" ":" STRING
+
+ socket_type ::= "socket-type" ":" socket_type_value
+
+ socket_type_value ::= "unix"
+
+ authentication ::= "authentication" ":" "{" auth_params "}"
+
+ auth_params ::= auth_param
+ | auth_params "," auth_param
+ | auth_params ","
+
+ auth_param ::= auth_type
+ | realm
+ | directory
+ | clients
+ | comment
+ | user_context
+ | unknown_map_entry
+
+ auth_type ::= "type" ":" auth_type_value
+
+ auth_type_value ::= "basic"
+
+ realm ::= "realm" ":" STRING
+
+ directory ::= "directory" ":" STRING
+
+ clients ::= "clients" ":" "[" clients_list "]"
+
+ clients_list ::=
+ | not_empty_clients_list
+
+ not_empty_clients_list ::= basic_auth
+ | not_empty_clients_list "," basic_auth
+ | not_empty_clients_list ","
+
+ basic_auth ::= "{" clients_params "}"
+
+ clients_params ::= clients_param
+ | clients_params "," clients_param
+ | clients_params ","
+
+ clients_param ::= user
+ | user_file
+ | password
+ | password_file
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ user ::= "user" ":" STRING
+
+ user_file ::= "user-file" ":" STRING
+
+ password ::= "password" ":" STRING
+
+ password_file ::= "password-file" ":" STRING
+
+ loggers ::= "loggers" ":" "[" loggers_entries "]"
+
+ loggers_entries ::= logger_entry
+ | loggers_entries "," logger_entry
+ | loggers_entries ","
+
+ logger_entry ::= "{" logger_params "}"
+
+ logger_params ::= logger_param
+ | logger_params "," logger_param
+ | logger_params ","
+
+ logger_param ::= name
+ | output_options_list
+ | debuglevel
+ | severity
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ name ::= "name" ":" STRING
+
+ debuglevel ::= "debuglevel" ":" INTEGER
+
+ severity ::= "severity" ":" STRING
+
+ output_options_list ::= "output-options" ":" "[" output_options_list_content "]"
+
+ output_options_list_content ::= output_entry
+ | output_options_list_content "," output_entry
+ | output_options_list_content ","
+
+ output_entry ::= "{" output_params_list "}"
+
+ output_params_list ::= output_params
+ | output_params_list "," output_params
+ | output_params_list ","
+
+ output_params ::= output
+ | flush
+ | maxsize
+ | maxver
+ | pattern
+
+ output ::= "output" ":" STRING
+
+ flush ::= "flush" ":" BOOLEAN
+
+ maxsize ::= "maxsize" ":" INTEGER
+
+ maxver ::= "maxver" ":" INTEGER
+
+ pattern ::= "pattern" ":" STRING
+
diff --git a/doc/sphinx/grammar/grammar-d2-parser.rst b/doc/sphinx/grammar/grammar-d2-parser.rst
new file mode 100644
index 0000000..28467f4
--- /dev/null
+++ b/doc/sphinx/grammar/grammar-d2-parser.rst
@@ -0,0 +1,310 @@
+This grammar is generated from ``d2_parser.yy``. See :ref:`dhcp-ddns-server` for more details.
+
+.. code-block:: BNF
+ :linenos:
+
+ Grammar
+
+ $accept ::= start EOF
+
+ start ::= TOPLEVEL_JSON sub_json
+
+ start ::= TOPLEVEL_DHCPDDNS syntax_map
+
+ start ::= SUB_DHCPDDNS sub_dhcpddns
+
+ start ::= SUB_TSIG_KEY sub_tsig_key
+
+ start ::= SUB_TSIG_KEYS sub_tsig_keys
+
+ start ::= SUB_DDNS_DOMAIN sub_ddns_domain
+
+ start ::= SUB_DDNS_DOMAINS sub_ddns_domains
+
+ start ::= SUB_DNS_SERVER sub_dns_server
+
+ start ::= SUB_DNS_SERVERS sub_dns_servers
+
+ start ::= SUB_HOOKS_LIBRARY sub_hooks_library
+
+ value ::= INTEGER
+ | FLOAT
+ | BOOLEAN
+ | STRING
+ | NULL
+ | map2
+ | list_generic
+
+ sub_json ::= value
+
+ map2 ::= "{" map_content "}"
+
+ map_value ::= map2
+
+ map_content ::=
+ | not_empty_map
+
+ not_empty_map ::= STRING ":" value
+ | not_empty_map "," STRING ":" value
+ | not_empty_map ","
+
+ list_generic ::= "[" list_content "]"
+
+ list_content ::=
+ | not_empty_list
+
+ not_empty_list ::= value
+ | not_empty_list "," value
+ | not_empty_list ","
+
+ unknown_map_entry ::= STRING ":"
+
+ syntax_map ::= "{" global_object "}"
+
+ global_object ::= "DhcpDdns" ":" "{" dhcpddns_params "}"
+ | global_object_comma
+
+ global_object_comma ::= global_object ","
+
+ sub_dhcpddns ::= "{" dhcpddns_params "}"
+
+ dhcpddns_params ::= dhcpddns_param
+ | dhcpddns_params "," dhcpddns_param
+ | dhcpddns_params ","
+
+ dhcpddns_param ::= ip_address
+ | port
+ | dns_server_timeout
+ | ncr_protocol
+ | ncr_format
+ | forward_ddns
+ | reverse_ddns
+ | tsig_keys
+ | control_socket
+ | hooks_libraries
+ | loggers
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ ip_address ::= "ip-address" ":" STRING
+
+ port ::= "port" ":" INTEGER
+
+ dns_server_timeout ::= "dns-server-timeout" ":" INTEGER
+
+ ncr_protocol ::= "ncr-protocol" ":" ncr_protocol_value
+
+ ncr_protocol_value ::= "UDP"
+ | "TCP"
+
+ ncr_format ::= "ncr-format" ":" "JSON"
+
+ user_context ::= "user-context" ":" map_value
+
+ comment ::= "comment" ":" STRING
+
+ forward_ddns ::= "forward-ddns" ":" "{" ddns_mgr_params "}"
+
+ reverse_ddns ::= "reverse-ddns" ":" "{" ddns_mgr_params "}"
+
+ ddns_mgr_params ::=
+ | not_empty_ddns_mgr_params
+
+ not_empty_ddns_mgr_params ::= ddns_mgr_param
+ | ddns_mgr_params "," ddns_mgr_param
+ | ddns_mgr_params ","
+
+ ddns_mgr_param ::= ddns_domains
+ | unknown_map_entry
+
+ ddns_domains ::= "ddns-domains" ":" "[" ddns_domain_list "]"
+
+ sub_ddns_domains ::= "[" ddns_domain_list "]"
+
+ ddns_domain_list ::=
+ | not_empty_ddns_domain_list
+
+ not_empty_ddns_domain_list ::= ddns_domain
+ | not_empty_ddns_domain_list "," ddns_domain
+ | not_empty_ddns_domain_list ","
+
+ ddns_domain ::= "{" ddns_domain_params "}"
+
+ sub_ddns_domain ::= "{" ddns_domain_params "}"
+
+ ddns_domain_params ::= ddns_domain_param
+ | ddns_domain_params "," ddns_domain_param
+ | ddns_domain_params ","
+
+ ddns_domain_param ::= ddns_domain_name
+ | ddns_key_name
+ | dns_servers
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ ddns_domain_name ::= "name" ":" STRING
+
+ ddns_key_name ::= "key-name" ":" STRING
+
+ dns_servers ::= "dns-servers" ":" "[" dns_server_list "]"
+
+ sub_dns_servers ::= "[" dns_server_list "]"
+
+ dns_server_list ::= dns_server
+ | dns_server_list "," dns_server
+ | dns_server_list ","
+
+ dns_server ::= "{" dns_server_params "}"
+
+ sub_dns_server ::= "{" dns_server_params "}"
+
+ dns_server_params ::= dns_server_param
+ | dns_server_params "," dns_server_param
+ | dns_server_params ","
+
+ dns_server_param ::= dns_server_hostname
+ | dns_server_ip_address
+ | dns_server_port
+ | ddns_key_name
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ dns_server_hostname ::= "hostname" ":" STRING
+
+ dns_server_ip_address ::= "ip-address" ":" STRING
+
+ dns_server_port ::= "port" ":" INTEGER
+
+ tsig_keys ::= "tsig-keys" ":" "[" tsig_keys_list "]"
+
+ sub_tsig_keys ::= "[" tsig_keys_list "]"
+
+ tsig_keys_list ::=
+ | not_empty_tsig_keys_list
+
+ not_empty_tsig_keys_list ::= tsig_key
+ | not_empty_tsig_keys_list "," tsig_key
+ | not_empty_tsig_keys_list ","
+
+ tsig_key ::= "{" tsig_key_params "}"
+
+ sub_tsig_key ::= "{" tsig_key_params "}"
+
+ tsig_key_params ::= tsig_key_param
+ | tsig_key_params "," tsig_key_param
+ | tsig_key_params ","
+
+ tsig_key_param ::= tsig_key_name
+ | tsig_key_algorithm
+ | tsig_key_digest_bits
+ | tsig_key_secret
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ tsig_key_name ::= "name" ":" STRING
+
+ tsig_key_algorithm ::= "algorithm" ":" STRING
+
+ tsig_key_digest_bits ::= "digest-bits" ":" INTEGER
+
+ tsig_key_secret ::= "secret" ":" STRING
+
+ control_socket ::= "control-socket" ":" "{" control_socket_params "}"
+
+ control_socket_params ::= control_socket_param
+ | control_socket_params "," control_socket_param
+ | control_socket_params ","
+
+ control_socket_param ::= control_socket_type
+ | control_socket_name
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ control_socket_type ::= "socket-type" ":" STRING
+
+ control_socket_name ::= "socket-name" ":" STRING
+
+ hooks_libraries ::= "hooks-libraries" ":" "[" hooks_libraries_list "]"
+
+ hooks_libraries_list ::=
+ | not_empty_hooks_libraries_list
+
+ not_empty_hooks_libraries_list ::= hooks_library
+ | not_empty_hooks_libraries_list "," hooks_library
+ | not_empty_hooks_libraries_list ","
+
+ hooks_library ::= "{" hooks_params "}"
+
+ sub_hooks_library ::= "{" hooks_params "}"
+
+ hooks_params ::= hooks_param
+ | hooks_params "," hooks_param
+ | hooks_params ","
+ | unknown_map_entry
+
+ hooks_param ::= library
+ | parameters
+
+ library ::= "library" ":" STRING
+
+ parameters ::= "parameters" ":" map_value
+
+ loggers ::= "loggers" ":" "[" loggers_entries "]"
+
+ loggers_entries ::= logger_entry
+ | loggers_entries "," logger_entry
+ | loggers_entries ","
+
+ logger_entry ::= "{" logger_params "}"
+
+ logger_params ::= logger_param
+ | logger_params "," logger_param
+ | logger_params ","
+
+ logger_param ::= name
+ | output_options_list
+ | debuglevel
+ | severity
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ name ::= "name" ":" STRING
+
+ debuglevel ::= "debuglevel" ":" INTEGER
+
+ severity ::= "severity" ":" STRING
+
+ output_options_list ::= "output-options" ":" "[" output_options_list_content "]"
+
+ output_options_list_content ::= output_entry
+ | output_options_list_content "," output_entry
+ | output_options_list_content ","
+
+ output_entry ::= "{" output_params_list "}"
+
+ output_params_list ::= output_params
+ | output_params_list "," output_params
+ | output_params_list ","
+
+ output_params ::= output
+ | flush
+ | maxsize
+ | maxver
+ | pattern
+
+ output ::= "output" ":" STRING
+
+ flush ::= "flush" ":" BOOLEAN
+
+ maxsize ::= "maxsize" ":" INTEGER
+
+ maxver ::= "maxver" ":" INTEGER
+
+ pattern ::= "pattern" ":" STRING
+
diff --git a/doc/sphinx/grammar/grammar-dhcp4-parser.rst b/doc/sphinx/grammar/grammar-dhcp4-parser.rst
new file mode 100644
index 0000000..c9c84ca
--- /dev/null
+++ b/doc/sphinx/grammar/grammar-dhcp4-parser.rst
@@ -0,0 +1,1056 @@
+This grammar is generated from ``dhcp4_parser.yy``. See :ref:`dhcp4` for more details.
+
+.. code-block:: BNF
+ :linenos:
+
+ Grammar
+
+ $accept ::= start EOF
+
+ start ::= TOPLEVEL_JSON sub_json
+
+ start ::= TOPLEVEL_DHCP4 syntax_map
+
+ start ::= SUB_DHCP4 sub_dhcp4
+
+ start ::= SUB_INTERFACES4 sub_interfaces4
+
+ start ::= SUB_SUBNET4 sub_subnet4
+
+ start ::= SUB_POOL4 sub_pool4
+
+ start ::= SUB_RESERVATION sub_reservation
+
+ start ::= SUB_OPTION_DEFS sub_option_def_list
+
+ start ::= SUB_OPTION_DEF sub_option_def
+
+ start ::= SUB_OPTION_DATA sub_option_data
+
+ start ::= SUB_HOOKS_LIBRARY sub_hooks_library
+
+ start ::= SUB_DHCP_DDNS sub_dhcp_ddns
+
+ start ::= SUB_CONFIG_CONTROL sub_config_control
+
+ value ::= INTEGER
+ | FLOAT
+ | BOOLEAN
+ | STRING
+ | NULL
+ | map2
+ | list_generic
+
+ sub_json ::= value
+
+ map2 ::= "{" map_content "}"
+
+ map_value ::= map2
+
+ map_content ::=
+ | not_empty_map
+
+ not_empty_map ::= STRING ":" value
+ | not_empty_map "," STRING ":" value
+ | not_empty_map ","
+
+ list_generic ::= "[" list_content "]"
+
+ list_content ::=
+ | not_empty_list
+
+ not_empty_list ::= value
+ | not_empty_list "," value
+ | not_empty_list ","
+
+ list_strings ::= "[" list_strings_content "]"
+
+ list_strings_content ::=
+ | not_empty_list_strings
+
+ not_empty_list_strings ::= STRING
+ | not_empty_list_strings "," STRING
+ | not_empty_list_strings ","
+
+ unknown_map_entry ::= STRING ":"
+
+ syntax_map ::= "{" global_object "}"
+
+ global_object ::= "Dhcp4" ":" "{" global_params "}"
+ | global_object_comma
+
+ global_object_comma ::= global_object ","
+
+ sub_dhcp4 ::= "{" global_params "}"
+
+ global_params ::= global_param
+ | global_params "," global_param
+ | global_params ","
+
+ global_param ::= valid_lifetime
+ | min_valid_lifetime
+ | max_valid_lifetime
+ | renew_timer
+ | rebind_timer
+ | decline_probation_period
+ | subnet4_list
+ | shared_networks
+ | interfaces_config
+ | lease_database
+ | hosts_database
+ | hosts_databases
+ | host_reservation_identifiers
+ | client_classes
+ | option_def_list
+ | option_data_list
+ | hooks_libraries
+ | expired_leases_processing
+ | dhcp4o6_port
+ | control_socket
+ | dhcp_queue_control
+ | dhcp_ddns
+ | echo_client_id
+ | match_client_id
+ | authoritative
+ | next_server
+ | server_hostname
+ | boot_file_name
+ | user_context
+ | comment
+ | sanity_checks
+ | reservations
+ | config_control
+ | server_tag
+ | reservation_mode
+ | reservations_global
+ | reservations_in_subnet
+ | reservations_out_of_pool
+ | calculate_tee_times
+ | t1_percent
+ | t2_percent
+ | cache_threshold
+ | cache_max_age
+ | loggers
+ | hostname_char_set
+ | hostname_char_replacement
+ | ddns_send_updates
+ | ddns_override_no_update
+ | ddns_override_client_update
+ | ddns_replace_client_name
+ | ddns_generated_prefix
+ | ddns_qualifying_suffix
+ | ddns_update_on_renew
+ | ddns_use_conflict_resolution
+ | ddns_conflict_resolution_mode
+ | ddns_ttl_percent
+ | store_extended_info
+ | statistic_default_sample_count
+ | statistic_default_sample_age
+ | dhcp_multi_threading
+ | early_global_reservations_lookup
+ | ip_reservations_unique
+ | reservations_lookup_first
+ | compatibility
+ | parked_packet_limit
+ | allocator
+ | offer_lifetime
+ | unknown_map_entry
+
+ valid_lifetime ::= "valid-lifetime" ":" INTEGER
+
+ min_valid_lifetime ::= "min-valid-lifetime" ":" INTEGER
+
+ max_valid_lifetime ::= "max-valid-lifetime" ":" INTEGER
+
+ renew_timer ::= "renew-timer" ":" INTEGER
+
+ rebind_timer ::= "rebind-timer" ":" INTEGER
+
+ calculate_tee_times ::= "calculate-tee-times" ":" BOOLEAN
+
+ t1_percent ::= "t1-percent" ":" FLOAT
+
+ t2_percent ::= "t2-percent" ":" FLOAT
+
+ cache_threshold ::= "cache-threshold" ":" FLOAT
+
+ cache_max_age ::= "cache-max-age" ":" INTEGER
+
+ decline_probation_period ::= "decline-probation-period" ":" INTEGER
+
+ server_tag ::= "server-tag" ":" STRING
+
+ parked_packet_limit ::= "parked-packet-limit" ":" INTEGER
+
+ allocator ::= "allocator" ":" STRING
+
+ echo_client_id ::= "echo-client-id" ":" BOOLEAN
+
+ match_client_id ::= "match-client-id" ":" BOOLEAN
+
+ authoritative ::= "authoritative" ":" BOOLEAN
+
+ ddns_send_updates ::= "ddns-send-updates" ":" BOOLEAN
+
+ ddns_override_no_update ::= "ddns-override-no-update" ":" BOOLEAN
+
+ ddns_override_client_update ::= "ddns-override-client-update" ":" BOOLEAN
+
+ ddns_replace_client_name ::= "ddns-replace-client-name" ":" ddns_replace_client_name_value
+
+ ddns_replace_client_name_value ::= "when-present"
+ | "never"
+ | "always"
+ | "when-not-present"
+ | BOOLEAN
+
+ ddns_generated_prefix ::= "ddns-generated-prefix" ":" STRING
+
+ ddns_qualifying_suffix ::= "ddns-qualifying-suffix" ":" STRING
+
+ ddns_update_on_renew ::= "ddns-update-on-renew" ":" BOOLEAN
+
+ ddns_use_conflict_resolution ::= "ddns-use-conflict-resolution" ":" BOOLEAN
+
+ ddns_conflict_resolution_mode ::= "ddns-conflict-resolution-mode" ":" ddns_conflict_resolution_mode_value
+
+ ddns_conflict_resolution_mode_value ::= "check-with-dhcid"
+ | "no-check-with-dhcid"
+ | "check-exists-with-dhcid"
+ | "no-check-without-dhcid"
+
+ ddns_ttl_percent ::= "ddns-ttl-percent" ":" FLOAT
+
+ hostname_char_set ::= "hostname-char-set" ":" STRING
+
+ hostname_char_replacement ::= "hostname-char-replacement" ":" STRING
+
+ store_extended_info ::= "store-extended-info" ":" BOOLEAN
+
+ statistic_default_sample_count ::= "statistic-default-sample-count" ":" INTEGER
+
+ statistic_default_sample_age ::= "statistic-default-sample-age" ":" INTEGER
+
+ early_global_reservations_lookup ::= "early-global-reservations-lookup" ":" BOOLEAN
+
+ ip_reservations_unique ::= "ip-reservations-unique" ":" BOOLEAN
+
+ reservations_lookup_first ::= "reservations-lookup-first" ":" BOOLEAN
+
+ offer_lifetime ::= "offer-lifetime" ":" INTEGER
+
+ interfaces_config ::= "interfaces-config" ":" "{" interfaces_config_params "}"
+
+ interfaces_config_params ::= interfaces_config_param
+ | interfaces_config_params "," interfaces_config_param
+ | interfaces_config_params ","
+
+ interfaces_config_param ::= interfaces_list
+ | dhcp_socket_type
+ | outbound_interface
+ | re_detect
+ | service_sockets_require_all
+ | service_sockets_retry_wait_time
+ | service_sockets_max_retries
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ sub_interfaces4 ::= "{" interfaces_config_params "}"
+
+ interfaces_list ::= "interfaces" ":" list_strings
+
+ dhcp_socket_type ::= "dhcp-socket-type" ":" socket_type
+
+ socket_type ::= "raw"
+ | "udp"
+
+ outbound_interface ::= "outbound-interface" ":" outbound_interface_value
+
+ outbound_interface_value ::= "same-as-inbound"
+ | "use-routing"
+
+ re_detect ::= "re-detect" ":" BOOLEAN
+
+ service_sockets_require_all ::= "service-sockets-require-all" ":" BOOLEAN
+
+ service_sockets_retry_wait_time ::= "service-sockets-retry-wait-time" ":" INTEGER
+
+ service_sockets_max_retries ::= "service-sockets-max-retries" ":" INTEGER
+
+ lease_database ::= "lease-database" ":" "{" database_map_params "}"
+
+ sanity_checks ::= "sanity-checks" ":" "{" sanity_checks_params "}"
+
+ sanity_checks_params ::= sanity_checks_param
+ | sanity_checks_params "," sanity_checks_param
+ | sanity_checks_params ","
+
+ sanity_checks_param ::= lease_checks
+ | extended_info_checks
+
+ lease_checks ::= "lease-checks" ":" STRING
+
+ extended_info_checks ::= "extended-info-checks" ":" STRING
+
+ hosts_database ::= "hosts-database" ":" "{" database_map_params "}"
+
+ hosts_databases ::= "hosts-databases" ":" "[" database_list "]"
+
+ database_list ::=
+ | not_empty_database_list
+
+ not_empty_database_list ::= database
+ | not_empty_database_list "," database
+ | not_empty_database_list ","
+
+ database ::= "{" database_map_params "}"
+
+ database_map_params ::= database_map_param
+ | database_map_params "," database_map_param
+ | database_map_params ","
+
+ database_map_param ::= database_type
+ | user
+ | password
+ | host
+ | port
+ | name
+ | persist
+ | lfc_interval
+ | readonly
+ | connect_timeout
+ | read_timeout
+ | write_timeout
+ | tcp_user_timeout
+ | max_reconnect_tries
+ | reconnect_wait_time
+ | on_fail
+ | retry_on_startup
+ | max_row_errors
+ | trust_anchor
+ | cert_file
+ | key_file
+ | cipher_list
+ | unknown_map_entry
+
+ database_type ::= "type" ":" db_type
+
+ db_type ::= "memfile"
+ | "mysql"
+ | "postgresql"
+
+ user ::= "user" ":" STRING
+
+ password ::= "password" ":" STRING
+
+ host ::= "host" ":" STRING
+
+ port ::= "port" ":" INTEGER
+
+ name ::= "name" ":" STRING
+
+ persist ::= "persist" ":" BOOLEAN
+
+ lfc_interval ::= "lfc-interval" ":" INTEGER
+
+ readonly ::= "readonly" ":" BOOLEAN
+
+ connect_timeout ::= "connect-timeout" ":" INTEGER
+
+ read_timeout ::= "read-timeout" ":" INTEGER
+
+ write_timeout ::= "write-timeout" ":" INTEGER
+
+ tcp_user_timeout ::= "tcp-user-timeout" ":" INTEGER
+
+ max_reconnect_tries ::= "max-reconnect-tries" ":" INTEGER
+
+ reconnect_wait_time ::= "reconnect-wait-time" ":" INTEGER
+
+ on_fail ::= "on-fail" ":" on_fail_mode
+
+ on_fail_mode ::= "stop-retry-exit"
+ | "serve-retry-exit"
+ | "serve-retry-continue"
+
+ retry_on_startup ::= "retry-on-startup" ":" BOOLEAN
+
+ max_row_errors ::= "max-row-errors" ":" INTEGER
+
+ trust_anchor ::= "trust-anchor" ":" STRING
+
+ cert_file ::= "cert-file" ":" STRING
+
+ key_file ::= "key-file" ":" STRING
+
+ cipher_list ::= "cipher-list" ":" STRING
+
+ host_reservation_identifiers ::= "host-reservation-identifiers" ":" "[" host_reservation_identifiers_list "]"
+
+ host_reservation_identifiers_list ::= host_reservation_identifier
+ | host_reservation_identifiers_list "," host_reservation_identifier
+ | host_reservation_identifiers_list ","
+
+ host_reservation_identifier ::= duid_id
+ | hw_address_id
+ | circuit_id
+ | client_id
+ | flex_id
+
+ duid_id ::= "duid"
+
+ hw_address_id ::= "hw-address"
+
+ circuit_id ::= "circuit-id"
+
+ client_id ::= "client-id"
+
+ flex_id ::= "flex-id"
+
+ dhcp_multi_threading ::= "multi-threading" ":" "{" multi_threading_params "}"
+
+ multi_threading_params ::= multi_threading_param
+ | multi_threading_params "," multi_threading_param
+ | multi_threading_params ","
+
+ multi_threading_param ::= enable_multi_threading
+ | thread_pool_size
+ | packet_queue_size
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ enable_multi_threading ::= "enable-multi-threading" ":" BOOLEAN
+
+ thread_pool_size ::= "thread-pool-size" ":" INTEGER
+
+ packet_queue_size ::= "packet-queue-size" ":" INTEGER
+
+ hooks_libraries ::= "hooks-libraries" ":" "[" hooks_libraries_list "]"
+
+ hooks_libraries_list ::=
+ | not_empty_hooks_libraries_list
+
+ not_empty_hooks_libraries_list ::= hooks_library
+ | not_empty_hooks_libraries_list "," hooks_library
+ | not_empty_hooks_libraries_list ","
+
+ hooks_library ::= "{" hooks_params "}"
+
+ sub_hooks_library ::= "{" hooks_params "}"
+
+ hooks_params ::= hooks_param
+ | hooks_params "," hooks_param
+ | hooks_params ","
+ | unknown_map_entry
+
+ hooks_param ::= library
+ | parameters
+
+ library ::= "library" ":" STRING
+
+ parameters ::= "parameters" ":" map_value
+
+ expired_leases_processing ::= "expired-leases-processing" ":" "{" expired_leases_params "}"
+
+ expired_leases_params ::= expired_leases_param
+ | expired_leases_params "," expired_leases_param
+ | expired_leases_params ","
+
+ expired_leases_param ::= reclaim_timer_wait_time
+ | flush_reclaimed_timer_wait_time
+ | hold_reclaimed_time
+ | max_reclaim_leases
+ | max_reclaim_time
+ | unwarned_reclaim_cycles
+
+ reclaim_timer_wait_time ::= "reclaim-timer-wait-time" ":" INTEGER
+
+ flush_reclaimed_timer_wait_time ::= "flush-reclaimed-timer-wait-time" ":" INTEGER
+
+ hold_reclaimed_time ::= "hold-reclaimed-time" ":" INTEGER
+
+ max_reclaim_leases ::= "max-reclaim-leases" ":" INTEGER
+
+ max_reclaim_time ::= "max-reclaim-time" ":" INTEGER
+
+ unwarned_reclaim_cycles ::= "unwarned-reclaim-cycles" ":" INTEGER
+
+ subnet4_list ::= "subnet4" ":" "[" subnet4_list_content "]"
+
+ subnet4_list_content ::=
+ | not_empty_subnet4_list
+
+ not_empty_subnet4_list ::= subnet4
+ | not_empty_subnet4_list "," subnet4
+ | not_empty_subnet4_list ","
+
+ subnet4 ::= "{" subnet4_params "}"
+
+ sub_subnet4 ::= "{" subnet4_params "}"
+
+ subnet4_params ::= subnet4_param
+ | subnet4_params "," subnet4_param
+ | subnet4_params ","
+
+ subnet4_param ::= valid_lifetime
+ | min_valid_lifetime
+ | max_valid_lifetime
+ | renew_timer
+ | rebind_timer
+ | option_data_list
+ | pools_list
+ | subnet
+ | interface
+ | id
+ | client_class
+ | require_client_classes
+ | reservations
+ | reservation_mode
+ | reservations_global
+ | reservations_in_subnet
+ | reservations_out_of_pool
+ | relay
+ | match_client_id
+ | authoritative
+ | next_server
+ | server_hostname
+ | boot_file_name
+ | subnet_4o6_interface
+ | subnet_4o6_interface_id
+ | subnet_4o6_subnet
+ | user_context
+ | comment
+ | calculate_tee_times
+ | t1_percent
+ | t2_percent
+ | cache_threshold
+ | cache_max_age
+ | ddns_send_updates
+ | ddns_override_no_update
+ | ddns_override_client_update
+ | ddns_replace_client_name
+ | ddns_generated_prefix
+ | ddns_qualifying_suffix
+ | ddns_update_on_renew
+ | ddns_use_conflict_resolution
+ | ddns_conflict_resolution_mode
+ | ddns_ttl_percent
+ | hostname_char_set
+ | hostname_char_replacement
+ | store_extended_info
+ | allocator
+ | offer_lifetime
+ | unknown_map_entry
+
+ subnet ::= "subnet" ":" STRING
+
+ subnet_4o6_interface ::= "4o6-interface" ":" STRING
+
+ subnet_4o6_interface_id ::= "4o6-interface-id" ":" STRING
+
+ subnet_4o6_subnet ::= "4o6-subnet" ":" STRING
+
+ interface ::= "interface" ":" STRING
+
+ client_class ::= "client-class" ":" STRING
+
+ require_client_classes ::= "require-client-classes" ":" list_strings
+
+ reservations_global ::= "reservations-global" ":" BOOLEAN
+
+ reservations_in_subnet ::= "reservations-in-subnet" ":" BOOLEAN
+
+ reservations_out_of_pool ::= "reservations-out-of-pool" ":" BOOLEAN
+
+ reservation_mode ::= "reservation-mode" ":" hr_mode
+
+ hr_mode ::= "disabled"
+ | "out-of-pool"
+ | "global"
+ | "all"
+
+ id ::= "id" ":" INTEGER
+
+ shared_networks ::= "shared-networks" ":" "[" shared_networks_content "]"
+
+ shared_networks_content ::=
+ | shared_networks_list
+
+ shared_networks_list ::= shared_network
+ | shared_networks_list "," shared_network
+ | shared_networks_list ","
+
+ shared_network ::= "{" shared_network_params "}"
+
+ shared_network_params ::= shared_network_param
+ | shared_network_params "," shared_network_param
+ | shared_network_params ","
+
+ shared_network_param ::= name
+ | subnet4_list
+ | interface
+ | renew_timer
+ | rebind_timer
+ | option_data_list
+ | match_client_id
+ | authoritative
+ | next_server
+ | server_hostname
+ | boot_file_name
+ | relay
+ | reservation_mode
+ | reservations_global
+ | reservations_in_subnet
+ | reservations_out_of_pool
+ | client_class
+ | require_client_classes
+ | valid_lifetime
+ | min_valid_lifetime
+ | max_valid_lifetime
+ | user_context
+ | comment
+ | calculate_tee_times
+ | t1_percent
+ | t2_percent
+ | cache_threshold
+ | cache_max_age
+ | ddns_send_updates
+ | ddns_override_no_update
+ | ddns_override_client_update
+ | ddns_replace_client_name
+ | ddns_generated_prefix
+ | ddns_qualifying_suffix
+ | ddns_update_on_renew
+ | ddns_use_conflict_resolution
+ | ddns_conflict_resolution_mode
+ | ddns_ttl_percent
+ | hostname_char_set
+ | hostname_char_replacement
+ | store_extended_info
+ | allocator
+ | offer_lifetime
+ | unknown_map_entry
+
+ option_def_list ::= "option-def" ":" "[" option_def_list_content "]"
+
+ sub_option_def_list ::= "{" option_def_list "}"
+
+ option_def_list_content ::=
+ | not_empty_option_def_list
+
+ not_empty_option_def_list ::= option_def_entry
+ | not_empty_option_def_list "," option_def_entry
+ | not_empty_option_def_list ","
+
+ option_def_entry ::= "{" option_def_params "}"
+
+ sub_option_def ::= "{" option_def_params "}"
+
+ option_def_params ::=
+ | not_empty_option_def_params
+
+ not_empty_option_def_params ::= option_def_param
+ | not_empty_option_def_params "," option_def_param
+ | not_empty_option_def_params ","
+
+ option_def_param ::= option_def_name
+ | option_def_code
+ | option_def_type
+ | option_def_record_types
+ | option_def_space
+ | option_def_encapsulate
+ | option_def_array
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ option_def_name ::= name
+
+ code ::= "code" ":" INTEGER
+
+ option_def_code ::= code
+
+ option_def_type ::= "type" ":" STRING
+
+ option_def_record_types ::= "record-types" ":" STRING
+
+ space ::= "space" ":" STRING
+
+ option_def_space ::= space
+
+ option_def_encapsulate ::= "encapsulate" ":" STRING
+
+ option_def_array ::= "array" ":" BOOLEAN
+
+ option_data_list ::= "option-data" ":" "[" option_data_list_content "]"
+
+ option_data_list_content ::=
+ | not_empty_option_data_list
+
+ not_empty_option_data_list ::= option_data_entry
+ | not_empty_option_data_list "," option_data_entry
+ | not_empty_option_data_list ","
+
+ option_data_entry ::= "{" option_data_params "}"
+
+ sub_option_data ::= "{" option_data_params "}"
+
+ option_data_params ::=
+ | not_empty_option_data_params
+
+ not_empty_option_data_params ::= option_data_param
+ | not_empty_option_data_params "," option_data_param
+ | not_empty_option_data_params ","
+
+ option_data_param ::= option_data_name
+ | option_data_data
+ | option_data_code
+ | option_data_space
+ | option_data_csv_format
+ | option_data_always_send
+ | option_data_never_send
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ option_data_name ::= name
+
+ option_data_data ::= "data" ":" STRING
+
+ option_data_code ::= code
+
+ option_data_space ::= space
+
+ option_data_csv_format ::= "csv-format" ":" BOOLEAN
+
+ option_data_always_send ::= "always-send" ":" BOOLEAN
+
+ option_data_never_send ::= "never-send" ":" BOOLEAN
+
+ pools_list ::= "pools" ":" "[" pools_list_content "]"
+
+ pools_list_content ::=
+ | not_empty_pools_list
+
+ not_empty_pools_list ::= pool_list_entry
+ | not_empty_pools_list "," pool_list_entry
+ | not_empty_pools_list ","
+
+ pool_list_entry ::= "{" pool_params "}"
+
+ sub_pool4 ::= "{" pool_params "}"
+
+ pool_params ::= pool_param
+ | pool_params "," pool_param
+ | pool_params ","
+
+ pool_param ::= pool_entry
+ | pool_id
+ | option_data_list
+ | client_class
+ | require_client_classes
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ pool_entry ::= "pool" ":" STRING
+
+ pool_id ::= "pool-id" ":" INTEGER
+
+ user_context ::= "user-context" ":" map_value
+
+ comment ::= "comment" ":" STRING
+
+ reservations ::= "reservations" ":" "[" reservations_list "]"
+
+ reservations_list ::=
+ | not_empty_reservations_list
+
+ not_empty_reservations_list ::= reservation
+ | not_empty_reservations_list "," reservation
+ | not_empty_reservations_list ","
+
+ reservation ::= "{" reservation_params "}"
+
+ sub_reservation ::= "{" reservation_params "}"
+
+ reservation_params ::=
+ | not_empty_reservation_params
+
+ not_empty_reservation_params ::= reservation_param
+ | not_empty_reservation_params "," reservation_param
+ | not_empty_reservation_params ","
+
+ reservation_param ::= duid
+ | reservation_client_classes
+ | client_id_value
+ | circuit_id_value
+ | flex_id_value
+ | ip_address
+ | hw_address
+ | hostname
+ | option_data_list
+ | next_server
+ | server_hostname
+ | boot_file_name
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ next_server ::= "next-server" ":" STRING
+
+ server_hostname ::= "server-hostname" ":" STRING
+
+ boot_file_name ::= "boot-file-name" ":" STRING
+
+ ip_address ::= "ip-address" ":" STRING
+
+ ip_addresses ::= "ip-addresses" ":" list_strings
+
+ duid ::= "duid" ":" STRING
+
+ hw_address ::= "hw-address" ":" STRING
+
+ client_id_value ::= "client-id" ":" STRING
+
+ circuit_id_value ::= "circuit-id" ":" STRING
+
+ flex_id_value ::= "flex-id" ":" STRING
+
+ hostname ::= "hostname" ":" STRING
+
+ reservation_client_classes ::= "client-classes" ":" list_strings
+
+ relay ::= "relay" ":" "{" relay_map "}"
+
+ relay_map ::= ip_address
+ | ip_addresses
+
+ client_classes ::= "client-classes" ":" "[" client_classes_list "]"
+
+ client_classes_list ::= client_class_entry
+ | client_classes_list "," client_class_entry
+ | client_classes_list ","
+
+ client_class_entry ::= "{" client_class_params "}"
+
+ client_class_params ::=
+ | not_empty_client_class_params
+
+ not_empty_client_class_params ::= client_class_param
+ | not_empty_client_class_params "," client_class_param
+ | not_empty_client_class_params ","
+
+ client_class_param ::= client_class_name
+ | client_class_test
+ | client_class_template_test
+ | only_if_required
+ | option_def_list
+ | option_data_list
+ | next_server
+ | server_hostname
+ | boot_file_name
+ | user_context
+ | comment
+ | unknown_map_entry
+ | valid_lifetime
+ | min_valid_lifetime
+ | max_valid_lifetime
+ | offer_lifetime
+
+ client_class_name ::= name
+
+ client_class_test ::= "test" ":" STRING
+
+ client_class_template_test ::= "template-test" ":" STRING
+
+ only_if_required ::= "only-if-required" ":" BOOLEAN
+
+ dhcp4o6_port ::= "dhcp4o6-port" ":" INTEGER
+
+ control_socket ::= "control-socket" ":" "{" control_socket_params "}"
+
+ control_socket_params ::= control_socket_param
+ | control_socket_params "," control_socket_param
+ | control_socket_params ","
+
+ control_socket_param ::= control_socket_type
+ | control_socket_name
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ control_socket_type ::= "socket-type" ":" STRING
+
+ control_socket_name ::= "socket-name" ":" STRING
+
+ dhcp_queue_control ::= "dhcp-queue-control" ":" "{" queue_control_params "}"
+
+ queue_control_params ::= queue_control_param
+ | queue_control_params "," queue_control_param
+ | queue_control_params ","
+
+ queue_control_param ::= enable_queue
+ | queue_type
+ | capacity
+ | user_context
+ | comment
+ | arbitrary_map_entry
+
+ enable_queue ::= "enable-queue" ":" BOOLEAN
+
+ queue_type ::= "queue-type" ":" STRING
+
+ capacity ::= "capacity" ":" INTEGER
+
+ arbitrary_map_entry ::= STRING ":" value
+
+ dhcp_ddns ::= "dhcp-ddns" ":" "{" dhcp_ddns_params "}"
+
+ sub_dhcp_ddns ::= "{" dhcp_ddns_params "}"
+
+ dhcp_ddns_params ::= dhcp_ddns_param
+ | dhcp_ddns_params "," dhcp_ddns_param
+ | dhcp_ddns_params ","
+
+ dhcp_ddns_param ::= enable_updates
+ | server_ip
+ | server_port
+ | sender_ip
+ | sender_port
+ | max_queue_size
+ | ncr_protocol
+ | ncr_format
+ | dep_override_no_update
+ | dep_override_client_update
+ | dep_replace_client_name
+ | dep_generated_prefix
+ | dep_qualifying_suffix
+ | dep_hostname_char_set
+ | dep_hostname_char_replacement
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ enable_updates ::= "enable-updates" ":" BOOLEAN
+
+ server_ip ::= "server-ip" ":" STRING
+
+ server_port ::= "server-port" ":" INTEGER
+
+ sender_ip ::= "sender-ip" ":" STRING
+
+ sender_port ::= "sender-port" ":" INTEGER
+
+ max_queue_size ::= "max-queue-size" ":" INTEGER
+
+ ncr_protocol ::= "ncr-protocol" ":" ncr_protocol_value
+
+ ncr_protocol_value ::= "udp"
+ | "tcp"
+
+ ncr_format ::= "ncr-format" ":" "JSON"
+
+ dep_qualifying_suffix ::= "qualifying-suffix" ":" STRING
+
+ dep_override_no_update ::= "override-no-update" ":" BOOLEAN
+
+ dep_override_client_update ::= "override-client-update" ":" BOOLEAN
+
+ dep_replace_client_name ::= "replace-client-name" ":" ddns_replace_client_name_value
+
+ dep_generated_prefix ::= "generated-prefix" ":" STRING
+
+ dep_hostname_char_set ::= "hostname-char-set" ":" STRING
+
+ dep_hostname_char_replacement ::= "hostname-char-replacement" ":" STRING
+
+ config_control ::= "config-control" ":" "{" config_control_params "}"
+
+ sub_config_control ::= "{" config_control_params "}"
+
+ config_control_params ::= config_control_param
+ | config_control_params "," config_control_param
+ | config_control_params ","
+
+ config_control_param ::= config_databases
+ | config_fetch_wait_time
+
+ config_databases ::= "config-databases" ":" "[" database_list "]"
+
+ config_fetch_wait_time ::= "config-fetch-wait-time" ":" INTEGER
+
+ loggers ::= "loggers" ":" "[" loggers_entries "]"
+
+ loggers_entries ::= logger_entry
+ | loggers_entries "," logger_entry
+ | loggers_entries ","
+
+ logger_entry ::= "{" logger_params "}"
+
+ logger_params ::= logger_param
+ | logger_params "," logger_param
+ | logger_params ","
+
+ logger_param ::= name
+ | output_options_list
+ | debuglevel
+ | severity
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ debuglevel ::= "debuglevel" ":" INTEGER
+
+ severity ::= "severity" ":" STRING
+
+ output_options_list ::= "output-options" ":" "[" output_options_list_content "]"
+
+ output_options_list_content ::= output_entry
+ | output_options_list_content "," output_entry
+ | output_options_list_content ","
+
+ output_entry ::= "{" output_params_list "}"
+
+ output_params_list ::= output_params
+ | output_params_list "," output_params
+ | output_params_list ","
+
+ output_params ::= output
+ | flush
+ | maxsize
+ | maxver
+ | pattern
+
+ output ::= "output" ":" STRING
+
+ flush ::= "flush" ":" BOOLEAN
+
+ maxsize ::= "maxsize" ":" INTEGER
+
+ maxver ::= "maxver" ":" INTEGER
+
+ pattern ::= "pattern" ":" STRING
+
+ compatibility ::= "compatibility" ":" "{" compatibility_params "}"
+
+ compatibility_params ::= compatibility_param
+ | compatibility_params "," compatibility_param
+ | compatibility_params ","
+
+ compatibility_param ::= lenient_option_parsing
+ | ignore_dhcp_server_identifier
+ | ignore_rai_link_selection
+ | exclude_first_last_24
+ | unknown_map_entry
+
+ lenient_option_parsing ::= "lenient-option-parsing" ":" BOOLEAN
+
+ ignore_dhcp_server_identifier ::= "ignore-dhcp-server-identifier" ":" BOOLEAN
+
+ ignore_rai_link_selection ::= "ignore-rai-link-selection" ":" BOOLEAN
+
+ exclude_first_last_24 ::= "exclude-first-last-24" ":" BOOLEAN
+
diff --git a/doc/sphinx/grammar/grammar-dhcp6-parser.rst b/doc/sphinx/grammar/grammar-dhcp6-parser.rst
new file mode 100644
index 0000000..79b6fa3
--- /dev/null
+++ b/doc/sphinx/grammar/grammar-dhcp6-parser.rst
@@ -0,0 +1,1098 @@
+This grammar is generated from ``dhcp6_parser.yy``. See :ref:`dhcp6` for more details.
+
+.. code-block:: BNF
+ :linenos:
+
+ Grammar
+
+ $accept ::= start EOF
+
+ start ::= TOPLEVEL_JSON sub_json
+
+ start ::= TOPLEVEL_DHCP6 syntax_map
+
+ start ::= SUB_DHCP6 sub_dhcp6
+
+ start ::= SUB_INTERFACES6 sub_interfaces6
+
+ start ::= SUB_SUBNET6 sub_subnet6
+
+ start ::= SUB_POOL6 sub_pool6
+
+ start ::= SUB_PD_POOL sub_pd_pool
+
+ start ::= SUB_RESERVATION sub_reservation
+
+ start ::= SUB_OPTION_DEFS sub_option_def_list
+
+ start ::= SUB_OPTION_DEF sub_option_def
+
+ start ::= SUB_OPTION_DATA sub_option_data
+
+ start ::= SUB_HOOKS_LIBRARY sub_hooks_library
+
+ start ::= SUB_DHCP_DDNS sub_dhcp_ddns
+
+ start ::= SUB_CONFIG_CONTROL sub_config_control
+
+ value ::= INTEGER
+ | FLOAT
+ | BOOLEAN
+ | STRING
+ | NULL
+ | map2
+ | list_generic
+
+ sub_json ::= value
+
+ map2 ::= "{" map_content "}"
+
+ map_value ::= map2
+
+ map_content ::=
+ | not_empty_map
+
+ not_empty_map ::= STRING ":" value
+ | not_empty_map "," STRING ":" value
+ | not_empty_map ","
+
+ list_generic ::= "[" list_content "]"
+
+ list_content ::=
+ | not_empty_list
+
+ not_empty_list ::= value
+ | not_empty_list "," value
+ | not_empty_list ","
+
+ list_strings ::= "[" list_strings_content "]"
+
+ list_strings_content ::=
+ | not_empty_list_strings
+
+ not_empty_list_strings ::= STRING
+ | not_empty_list_strings "," STRING
+ | not_empty_list_strings ","
+
+ unknown_map_entry ::= STRING ":"
+
+ syntax_map ::= "{" global_object "}"
+
+ global_object ::= "Dhcp6" ":" "{" global_params "}"
+ | global_object_comma
+
+ global_object_comma ::= global_object ","
+
+ sub_dhcp6 ::= "{" global_params "}"
+
+ global_params ::= global_param
+ | global_params "," global_param
+ | global_params ","
+
+ global_param ::= data_directory
+ | preferred_lifetime
+ | min_preferred_lifetime
+ | max_preferred_lifetime
+ | valid_lifetime
+ | min_valid_lifetime
+ | max_valid_lifetime
+ | renew_timer
+ | rebind_timer
+ | decline_probation_period
+ | subnet6_list
+ | shared_networks
+ | interfaces_config
+ | lease_database
+ | hosts_database
+ | hosts_databases
+ | mac_sources
+ | relay_supplied_options
+ | host_reservation_identifiers
+ | client_classes
+ | option_def_list
+ | option_data_list
+ | hooks_libraries
+ | expired_leases_processing
+ | server_id
+ | dhcp4o6_port
+ | control_socket
+ | dhcp_queue_control
+ | dhcp_ddns
+ | user_context
+ | comment
+ | sanity_checks
+ | reservations
+ | config_control
+ | server_tag
+ | reservation_mode
+ | reservations_global
+ | reservations_in_subnet
+ | reservations_out_of_pool
+ | calculate_tee_times
+ | t1_percent
+ | t2_percent
+ | cache_threshold
+ | cache_max_age
+ | loggers
+ | hostname_char_set
+ | hostname_char_replacement
+ | ddns_send_updates
+ | ddns_override_no_update
+ | ddns_override_client_update
+ | ddns_replace_client_name
+ | ddns_generated_prefix
+ | ddns_qualifying_suffix
+ | ddns_update_on_renew
+ | ddns_use_conflict_resolution
+ | ddns_conflict_resolution_mode
+ | ddns_ttl_percent
+ | store_extended_info
+ | statistic_default_sample_count
+ | statistic_default_sample_age
+ | dhcp_multi_threading
+ | early_global_reservations_lookup
+ | ip_reservations_unique
+ | reservations_lookup_first
+ | compatibility
+ | parked_packet_limit
+ | allocator
+ | pd_allocator
+ | unknown_map_entry
+
+ data_directory ::= "data-directory" ":" STRING
+
+ preferred_lifetime ::= "preferred-lifetime" ":" INTEGER
+
+ min_preferred_lifetime ::= "min-preferred-lifetime" ":" INTEGER
+
+ max_preferred_lifetime ::= "max-preferred-lifetime" ":" INTEGER
+
+ valid_lifetime ::= "valid-lifetime" ":" INTEGER
+
+ min_valid_lifetime ::= "min-valid-lifetime" ":" INTEGER
+
+ max_valid_lifetime ::= "max-valid-lifetime" ":" INTEGER
+
+ renew_timer ::= "renew-timer" ":" INTEGER
+
+ rebind_timer ::= "rebind-timer" ":" INTEGER
+
+ calculate_tee_times ::= "calculate-tee-times" ":" BOOLEAN
+
+ t1_percent ::= "t1-percent" ":" FLOAT
+
+ t2_percent ::= "t2-percent" ":" FLOAT
+
+ cache_threshold ::= "cache-threshold" ":" FLOAT
+
+ cache_max_age ::= "cache-max-age" ":" INTEGER
+
+ decline_probation_period ::= "decline-probation-period" ":" INTEGER
+
+ ddns_send_updates ::= "ddns-send-updates" ":" BOOLEAN
+
+ ddns_override_no_update ::= "ddns-override-no-update" ":" BOOLEAN
+
+ ddns_override_client_update ::= "ddns-override-client-update" ":" BOOLEAN
+
+ ddns_replace_client_name ::= "ddns-replace-client-name" ":" ddns_replace_client_name_value
+
+ ddns_replace_client_name_value ::= "when-present"
+ | "never"
+ | "always"
+ | "when-not-present"
+ | BOOLEAN
+
+ ddns_generated_prefix ::= "ddns-generated-prefix" ":" STRING
+
+ ddns_qualifying_suffix ::= "ddns-qualifying-suffix" ":" STRING
+
+ ddns_update_on_renew ::= "ddns-update-on-renew" ":" BOOLEAN
+
+ ddns_use_conflict_resolution ::= "ddns-use-conflict-resolution" ":" BOOLEAN
+
+ ddns_conflict_resolution_mode ::= "ddns-conflict-resolution-mode" ":" ddns_conflict_resolution_mode_value
+
+ ddns_conflict_resolution_mode_value ::= "check-with-dhcid"
+ | "no-check-with-dhcid"
+ | "check-exists-with-dhcid"
+ | "no-check-without-dhcid"
+
+ ddns_ttl_percent ::= "ddns-ttl-percent" ":" FLOAT
+
+ hostname_char_set ::= "hostname-char-set" ":" STRING
+
+ hostname_char_replacement ::= "hostname-char-replacement" ":" STRING
+
+ store_extended_info ::= "store-extended-info" ":" BOOLEAN
+
+ statistic_default_sample_count ::= "statistic-default-sample-count" ":" INTEGER
+
+ statistic_default_sample_age ::= "statistic-default-sample-age" ":" INTEGER
+
+ server_tag ::= "server-tag" ":" STRING
+
+ parked_packet_limit ::= "parked-packet-limit" ":" INTEGER
+
+ allocator ::= "allocator" ":" STRING
+
+ pd_allocator ::= "pd-allocator" ":" STRING
+
+ early_global_reservations_lookup ::= "early-global-reservations-lookup" ":" BOOLEAN
+
+ ip_reservations_unique ::= "ip-reservations-unique" ":" BOOLEAN
+
+ reservations_lookup_first ::= "reservations-lookup-first" ":" BOOLEAN
+
+ interfaces_config ::= "interfaces-config" ":" "{" interfaces_config_params "}"
+
+ sub_interfaces6 ::= "{" interfaces_config_params "}"
+
+ interfaces_config_params ::= interfaces_config_param
+ | interfaces_config_params "," interfaces_config_param
+ | interfaces_config_params ","
+
+ interfaces_config_param ::= interfaces_list
+ | re_detect
+ | service_sockets_require_all
+ | service_sockets_retry_wait_time
+ | service_sockets_max_retries
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ interfaces_list ::= "interfaces" ":" list_strings
+
+ re_detect ::= "re-detect" ":" BOOLEAN
+
+ service_sockets_require_all ::= "service-sockets-require-all" ":" BOOLEAN
+
+ service_sockets_retry_wait_time ::= "service-sockets-retry-wait-time" ":" INTEGER
+
+ service_sockets_max_retries ::= "service-sockets-max-retries" ":" INTEGER
+
+ lease_database ::= "lease-database" ":" "{" database_map_params "}"
+
+ hosts_database ::= "hosts-database" ":" "{" database_map_params "}"
+
+ hosts_databases ::= "hosts-databases" ":" "[" database_list "]"
+
+ database_list ::=
+ | not_empty_database_list
+
+ not_empty_database_list ::= database
+ | not_empty_database_list "," database
+ | not_empty_database_list ","
+
+ database ::= "{" database_map_params "}"
+
+ database_map_params ::= database_map_param
+ | database_map_params "," database_map_param
+ | database_map_params ","
+
+ database_map_param ::= database_type
+ | user
+ | password
+ | host
+ | port
+ | name
+ | persist
+ | lfc_interval
+ | readonly
+ | connect_timeout
+ | read_timeout
+ | write_timeout
+ | tcp_user_timeout
+ | max_reconnect_tries
+ | reconnect_wait_time
+ | on_fail
+ | retry_on_startup
+ | max_row_errors
+ | trust_anchor
+ | cert_file
+ | key_file
+ | cipher_list
+ | unknown_map_entry
+
+ database_type ::= "type" ":" db_type
+
+ db_type ::= "memfile"
+ | "mysql"
+ | "postgresql"
+
+ user ::= "user" ":" STRING
+
+ password ::= "password" ":" STRING
+
+ host ::= "host" ":" STRING
+
+ port ::= "port" ":" INTEGER
+
+ name ::= "name" ":" STRING
+
+ persist ::= "persist" ":" BOOLEAN
+
+ lfc_interval ::= "lfc-interval" ":" INTEGER
+
+ readonly ::= "readonly" ":" BOOLEAN
+
+ connect_timeout ::= "connect-timeout" ":" INTEGER
+
+ read_timeout ::= "read-timeout" ":" INTEGER
+
+ write_timeout ::= "write-timeout" ":" INTEGER
+
+ tcp_user_timeout ::= "tcp-user-timeout" ":" INTEGER
+
+ reconnect_wait_time ::= "reconnect-wait-time" ":" INTEGER
+
+ on_fail ::= "on-fail" ":" on_fail_mode
+
+ on_fail_mode ::= "stop-retry-exit"
+ | "serve-retry-exit"
+ | "serve-retry-continue"
+
+ retry_on_startup ::= "retry-on-startup" ":" BOOLEAN
+
+ max_row_errors ::= "max-row-errors" ":" INTEGER
+
+ max_reconnect_tries ::= "max-reconnect-tries" ":" INTEGER
+
+ trust_anchor ::= "trust-anchor" ":" STRING
+
+ cert_file ::= "cert-file" ":" STRING
+
+ key_file ::= "key-file" ":" STRING
+
+ cipher_list ::= "cipher-list" ":" STRING
+
+ sanity_checks ::= "sanity-checks" ":" "{" sanity_checks_params "}"
+
+ sanity_checks_params ::= sanity_checks_param
+ | sanity_checks_params "," sanity_checks_param
+ | sanity_checks_params ","
+
+ sanity_checks_param ::= lease_checks
+ | extended_info_checks
+
+ lease_checks ::= "lease-checks" ":" STRING
+
+ extended_info_checks ::= "extended-info-checks" ":" STRING
+
+ mac_sources ::= "mac-sources" ":" "[" mac_sources_list "]"
+
+ mac_sources_list ::= mac_sources_value
+ | mac_sources_list "," mac_sources_value
+ | mac_sources_list ","
+
+ mac_sources_value ::= duid_id
+ | string_id
+
+ duid_id ::= "duid"
+
+ string_id ::= STRING
+
+ host_reservation_identifiers ::= "host-reservation-identifiers" ":" "[" host_reservation_identifiers_list "]"
+
+ host_reservation_identifiers_list ::= host_reservation_identifier
+ | host_reservation_identifiers_list "," host_reservation_identifier
+ | host_reservation_identifiers_list ","
+
+ host_reservation_identifier ::= duid_id
+ | hw_address_id
+ | flex_id
+
+ hw_address_id ::= "hw-address"
+
+ flex_id ::= "flex-id"
+
+ relay_supplied_options ::= "relay-supplied-options" ":" "[" list_content "]"
+
+ dhcp_multi_threading ::= "multi-threading" ":" "{" multi_threading_params "}"
+
+ multi_threading_params ::= multi_threading_param
+ | multi_threading_params "," multi_threading_param
+ | multi_threading_params ","
+
+ multi_threading_param ::= enable_multi_threading
+ | thread_pool_size
+ | packet_queue_size
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ enable_multi_threading ::= "enable-multi-threading" ":" BOOLEAN
+
+ thread_pool_size ::= "thread-pool-size" ":" INTEGER
+
+ packet_queue_size ::= "packet-queue-size" ":" INTEGER
+
+ hooks_libraries ::= "hooks-libraries" ":" "[" hooks_libraries_list "]"
+
+ hooks_libraries_list ::=
+ | not_empty_hooks_libraries_list
+
+ not_empty_hooks_libraries_list ::= hooks_library
+ | not_empty_hooks_libraries_list "," hooks_library
+ | not_empty_hooks_libraries_list ","
+
+ hooks_library ::= "{" hooks_params "}"
+
+ sub_hooks_library ::= "{" hooks_params "}"
+
+ hooks_params ::= hooks_param
+ | hooks_params "," hooks_param
+ | hooks_params ","
+ | unknown_map_entry
+
+ hooks_param ::= library
+ | parameters
+
+ library ::= "library" ":" STRING
+
+ parameters ::= "parameters" ":" map_value
+
+ expired_leases_processing ::= "expired-leases-processing" ":" "{" expired_leases_params "}"
+
+ expired_leases_params ::= expired_leases_param
+ | expired_leases_params "," expired_leases_param
+ | expired_leases_params ","
+
+ expired_leases_param ::= reclaim_timer_wait_time
+ | flush_reclaimed_timer_wait_time
+ | hold_reclaimed_time
+ | max_reclaim_leases
+ | max_reclaim_time
+ | unwarned_reclaim_cycles
+
+ reclaim_timer_wait_time ::= "reclaim-timer-wait-time" ":" INTEGER
+
+ flush_reclaimed_timer_wait_time ::= "flush-reclaimed-timer-wait-time" ":" INTEGER
+
+ hold_reclaimed_time ::= "hold-reclaimed-time" ":" INTEGER
+
+ max_reclaim_leases ::= "max-reclaim-leases" ":" INTEGER
+
+ max_reclaim_time ::= "max-reclaim-time" ":" INTEGER
+
+ unwarned_reclaim_cycles ::= "unwarned-reclaim-cycles" ":" INTEGER
+
+ subnet6_list ::= "subnet6" ":" "[" subnet6_list_content "]"
+
+ subnet6_list_content ::=
+ | not_empty_subnet6_list
+
+ not_empty_subnet6_list ::= subnet6
+ | not_empty_subnet6_list "," subnet6
+ | not_empty_subnet6_list ","
+
+ subnet6 ::= "{" subnet6_params "}"
+
+ sub_subnet6 ::= "{" subnet6_params "}"
+
+ subnet6_params ::= subnet6_param
+ | subnet6_params "," subnet6_param
+ | subnet6_params ","
+
+ subnet6_param ::= preferred_lifetime
+ | min_preferred_lifetime
+ | max_preferred_lifetime
+ | valid_lifetime
+ | min_valid_lifetime
+ | max_valid_lifetime
+ | renew_timer
+ | rebind_timer
+ | option_data_list
+ | pools_list
+ | pd_pools_list
+ | subnet
+ | interface
+ | interface_id
+ | id
+ | rapid_commit
+ | client_class
+ | require_client_classes
+ | reservations
+ | reservation_mode
+ | reservations_global
+ | reservations_in_subnet
+ | reservations_out_of_pool
+ | relay
+ | user_context
+ | comment
+ | calculate_tee_times
+ | t1_percent
+ | t2_percent
+ | cache_threshold
+ | cache_max_age
+ | hostname_char_set
+ | hostname_char_replacement
+ | ddns_send_updates
+ | ddns_override_no_update
+ | ddns_override_client_update
+ | ddns_replace_client_name
+ | ddns_generated_prefix
+ | ddns_qualifying_suffix
+ | ddns_update_on_renew
+ | ddns_use_conflict_resolution
+ | ddns_conflict_resolution_mode
+ | ddns_ttl_percent
+ | store_extended_info
+ | allocator
+ | pd_allocator
+ | unknown_map_entry
+
+ subnet ::= "subnet" ":" STRING
+
+ interface ::= "interface" ":" STRING
+
+ interface_id ::= "interface-id" ":" STRING
+
+ client_class ::= "client-class" ":" STRING
+
+ require_client_classes ::= "require-client-classes" ":" list_strings
+
+ reservations_global ::= "reservations-global" ":" BOOLEAN
+
+ reservations_in_subnet ::= "reservations-in-subnet" ":" BOOLEAN
+
+ reservations_out_of_pool ::= "reservations-out-of-pool" ":" BOOLEAN
+
+ reservation_mode ::= "reservation-mode" ":" hr_mode
+
+ hr_mode ::= "disabled"
+ | "out-of-pool"
+ | "global"
+ | "all"
+
+ id ::= "id" ":" INTEGER
+
+ rapid_commit ::= "rapid-commit" ":" BOOLEAN
+
+ shared_networks ::= "shared-networks" ":" "[" shared_networks_content "]"
+
+ shared_networks_content ::=
+ | shared_networks_list
+
+ shared_networks_list ::= shared_network
+ | shared_networks_list "," shared_network
+ | shared_networks_list ","
+
+ shared_network ::= "{" shared_network_params "}"
+
+ shared_network_params ::= shared_network_param
+ | shared_network_params "," shared_network_param
+ | shared_network_params ","
+
+ shared_network_param ::= name
+ | subnet6_list
+ | interface
+ | interface_id
+ | renew_timer
+ | rebind_timer
+ | option_data_list
+ | relay
+ | reservation_mode
+ | reservations_global
+ | reservations_in_subnet
+ | reservations_out_of_pool
+ | client_class
+ | require_client_classes
+ | preferred_lifetime
+ | min_preferred_lifetime
+ | max_preferred_lifetime
+ | rapid_commit
+ | valid_lifetime
+ | min_valid_lifetime
+ | max_valid_lifetime
+ | user_context
+ | comment
+ | calculate_tee_times
+ | t1_percent
+ | t2_percent
+ | cache_threshold
+ | cache_max_age
+ | hostname_char_set
+ | hostname_char_replacement
+ | ddns_send_updates
+ | ddns_override_no_update
+ | ddns_override_client_update
+ | ddns_replace_client_name
+ | ddns_generated_prefix
+ | ddns_qualifying_suffix
+ | ddns_update_on_renew
+ | ddns_use_conflict_resolution
+ | ddns_conflict_resolution_mode
+ | ddns_ttl_percent
+ | store_extended_info
+ | allocator
+ | pd_allocator
+ | unknown_map_entry
+
+ option_def_list ::= "option-def" ":" "[" option_def_list_content "]"
+
+ sub_option_def_list ::= "{" option_def_list "}"
+
+ option_def_list_content ::=
+ | not_empty_option_def_list
+
+ not_empty_option_def_list ::= option_def_entry
+ | not_empty_option_def_list "," option_def_entry
+ | not_empty_option_def_list ","
+
+ option_def_entry ::= "{" option_def_params "}"
+
+ sub_option_def ::= "{" option_def_params "}"
+
+ option_def_params ::=
+ | not_empty_option_def_params
+
+ not_empty_option_def_params ::= option_def_param
+ | not_empty_option_def_params "," option_def_param
+ | not_empty_option_def_params ","
+
+ option_def_param ::= option_def_name
+ | option_def_code
+ | option_def_type
+ | option_def_record_types
+ | option_def_space
+ | option_def_encapsulate
+ | option_def_array
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ option_def_name ::= name
+
+ code ::= "code" ":" INTEGER
+
+ option_def_code ::= code
+
+ option_def_type ::= "type" ":" STRING
+
+ option_def_record_types ::= "record-types" ":" STRING
+
+ space ::= "space" ":" STRING
+
+ option_def_space ::= space
+
+ option_def_encapsulate ::= "encapsulate" ":" STRING
+
+ option_def_array ::= "array" ":" BOOLEAN
+
+ option_data_list ::= "option-data" ":" "[" option_data_list_content "]"
+
+ option_data_list_content ::=
+ | not_empty_option_data_list
+
+ not_empty_option_data_list ::= option_data_entry
+ | not_empty_option_data_list "," option_data_entry
+ | not_empty_option_data_list ","
+
+ option_data_entry ::= "{" option_data_params "}"
+
+ sub_option_data ::= "{" option_data_params "}"
+
+ option_data_params ::=
+ | not_empty_option_data_params
+
+ not_empty_option_data_params ::= option_data_param
+ | not_empty_option_data_params "," option_data_param
+ | not_empty_option_data_params ","
+
+ option_data_param ::= option_data_name
+ | option_data_data
+ | option_data_code
+ | option_data_space
+ | option_data_csv_format
+ | option_data_always_send
+ | option_data_never_send
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ option_data_name ::= name
+
+ option_data_data ::= "data" ":" STRING
+
+ option_data_code ::= code
+
+ option_data_space ::= space
+
+ option_data_csv_format ::= "csv-format" ":" BOOLEAN
+
+ option_data_always_send ::= "always-send" ":" BOOLEAN
+
+ option_data_never_send ::= "never-send" ":" BOOLEAN
+
+ pools_list ::= "pools" ":" "[" pools_list_content "]"
+
+ pools_list_content ::=
+ | not_empty_pools_list
+
+ not_empty_pools_list ::= pool_list_entry
+ | not_empty_pools_list "," pool_list_entry
+ | not_empty_pools_list ","
+
+ pool_list_entry ::= "{" pool_params "}"
+
+ sub_pool6 ::= "{" pool_params "}"
+
+ pool_params ::= pool_param
+ | pool_params "," pool_param
+ | pool_params ","
+
+ pool_param ::= pool_entry
+ | pool_id
+ | option_data_list
+ | client_class
+ | require_client_classes
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ pool_entry ::= "pool" ":" STRING
+
+ pool_id ::= "pool-id" ":" INTEGER
+
+ user_context ::= "user-context" ":" map_value
+
+ comment ::= "comment" ":" STRING
+
+ pd_pools_list ::= "pd-pools" ":" "[" pd_pools_list_content "]"
+
+ pd_pools_list_content ::=
+ | not_empty_pd_pools_list
+
+ not_empty_pd_pools_list ::= pd_pool_entry
+ | not_empty_pd_pools_list "," pd_pool_entry
+ | not_empty_pd_pools_list ","
+
+ pd_pool_entry ::= "{" pd_pool_params "}"
+
+ sub_pd_pool ::= "{" pd_pool_params "}"
+
+ pd_pool_params ::= pd_pool_param
+ | pd_pool_params "," pd_pool_param
+ | pd_pool_params ","
+
+ pd_pool_param ::= pd_prefix
+ | pd_prefix_len
+ | pd_delegated_len
+ | option_data_list
+ | client_class
+ | require_client_classes
+ | excluded_prefix
+ | excluded_prefix_len
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ pd_prefix ::= "prefix" ":" STRING
+
+ pd_prefix_len ::= "prefix-len" ":" INTEGER
+
+ excluded_prefix ::= "excluded-prefix" ":" STRING
+
+ excluded_prefix_len ::= "excluded-prefix-len" ":" INTEGER
+
+ pd_delegated_len ::= "delegated-len" ":" INTEGER
+
+ reservations ::= "reservations" ":" "[" reservations_list "]"
+
+ reservations_list ::=
+ | not_empty_reservations_list
+
+ not_empty_reservations_list ::= reservation
+ | not_empty_reservations_list "," reservation
+ | not_empty_reservations_list ","
+
+ reservation ::= "{" reservation_params "}"
+
+ sub_reservation ::= "{" reservation_params "}"
+
+ reservation_params ::=
+ | not_empty_reservation_params
+
+ not_empty_reservation_params ::= reservation_param
+ | not_empty_reservation_params "," reservation_param
+ | not_empty_reservation_params ","
+
+ reservation_param ::= duid
+ | reservation_client_classes
+ | ip_addresses
+ | prefixes
+ | hw_address
+ | hostname
+ | flex_id_value
+ | option_data_list
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ ip_addresses ::= "ip-addresses" ":" list_strings
+
+ prefixes ::= "prefixes" ":" list_strings
+
+ duid ::= "duid" ":" STRING
+
+ hw_address ::= "hw-address" ":" STRING
+
+ hostname ::= "hostname" ":" STRING
+
+ flex_id_value ::= "flex-id" ":" STRING
+
+ reservation_client_classes ::= "client-classes" ":" list_strings
+
+ relay ::= "relay" ":" "{" relay_map "}"
+
+ relay_map ::= ip_address
+ | ip_addresses
+
+ ip_address ::= "ip-address" ":" STRING
+
+ client_classes ::= "client-classes" ":" "[" client_classes_list "]"
+
+ client_classes_list ::= client_class_entry
+ | client_classes_list "," client_class_entry
+ | client_classes_list ","
+
+ client_class_entry ::= "{" client_class_params "}"
+
+ client_class_params ::=
+ | not_empty_client_class_params
+
+ not_empty_client_class_params ::= client_class_param
+ | not_empty_client_class_params "," client_class_param
+ | not_empty_client_class_params ","
+
+ client_class_param ::= client_class_name
+ | client_class_test
+ | client_class_template_test
+ | only_if_required
+ | option_data_list
+ | user_context
+ | comment
+ | preferred_lifetime
+ | min_preferred_lifetime
+ | max_preferred_lifetime
+ | valid_lifetime
+ | min_valid_lifetime
+ | max_valid_lifetime
+ | unknown_map_entry
+
+ client_class_name ::= name
+
+ client_class_test ::= "test" ":" STRING
+
+ client_class_template_test ::= "template-test" ":" STRING
+
+ only_if_required ::= "only-if-required" ":" BOOLEAN
+
+ server_id ::= "server-id" ":" "{" server_id_params "}"
+
+ server_id_params ::= server_id_param
+ | server_id_params "," server_id_param
+ | server_id_params ","
+
+ server_id_param ::= server_id_type
+ | identifier
+ | time
+ | htype
+ | enterprise_id
+ | persist
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ server_id_type ::= "type" ":" duid_type
+
+ duid_type ::= "LLT"
+ | "EN"
+ | "LL"
+
+ htype ::= "htype" ":" INTEGER
+
+ identifier ::= "identifier" ":" STRING
+
+ time ::= "time" ":" INTEGER
+
+ enterprise_id ::= "enterprise-id" ":" INTEGER
+
+ dhcp4o6_port ::= "dhcp4o6-port" ":" INTEGER
+
+ control_socket ::= "control-socket" ":" "{" control_socket_params "}"
+
+ control_socket_params ::= control_socket_param
+ | control_socket_params "," control_socket_param
+ | control_socket_params ","
+
+ control_socket_param ::= socket_type
+ | socket_name
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ socket_type ::= "socket-type" ":" STRING
+
+ socket_name ::= "socket-name" ":" STRING
+
+ dhcp_queue_control ::= "dhcp-queue-control" ":" "{" queue_control_params "}"
+
+ queue_control_params ::= queue_control_param
+ | queue_control_params "," queue_control_param
+ | queue_control_params ","
+
+ queue_control_param ::= enable_queue
+ | queue_type
+ | capacity
+ | user_context
+ | comment
+ | arbitrary_map_entry
+
+ enable_queue ::= "enable-queue" ":" BOOLEAN
+
+ queue_type ::= "queue-type" ":" STRING
+
+ capacity ::= "capacity" ":" INTEGER
+
+ arbitrary_map_entry ::= STRING ":" value
+
+ dhcp_ddns ::= "dhcp-ddns" ":" "{" dhcp_ddns_params "}"
+
+ sub_dhcp_ddns ::= "{" dhcp_ddns_params "}"
+
+ dhcp_ddns_params ::= dhcp_ddns_param
+ | dhcp_ddns_params "," dhcp_ddns_param
+ | dhcp_ddns_params ","
+
+ dhcp_ddns_param ::= enable_updates
+ | server_ip
+ | server_port
+ | sender_ip
+ | sender_port
+ | max_queue_size
+ | ncr_protocol
+ | ncr_format
+ | dep_override_no_update
+ | dep_override_client_update
+ | dep_replace_client_name
+ | dep_generated_prefix
+ | dep_qualifying_suffix
+ | dep_hostname_char_set
+ | dep_hostname_char_replacement
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ enable_updates ::= "enable-updates" ":" BOOLEAN
+
+ dep_qualifying_suffix ::= "qualifying-suffix" ":" STRING
+
+ server_ip ::= "server-ip" ":" STRING
+
+ server_port ::= "server-port" ":" INTEGER
+
+ sender_ip ::= "sender-ip" ":" STRING
+
+ sender_port ::= "sender-port" ":" INTEGER
+
+ max_queue_size ::= "max-queue-size" ":" INTEGER
+
+ ncr_protocol ::= "ncr-protocol" ":" ncr_protocol_value
+
+ ncr_protocol_value ::= "UDP"
+ | "TCP"
+
+ ncr_format ::= "ncr-format" ":" "JSON"
+
+ dep_override_no_update ::= "override-no-update" ":" BOOLEAN
+
+ dep_override_client_update ::= "override-client-update" ":" BOOLEAN
+
+ dep_replace_client_name ::= "replace-client-name" ":" ddns_replace_client_name_value
+
+ dep_generated_prefix ::= "generated-prefix" ":" STRING
+
+ dep_hostname_char_set ::= "hostname-char-set" ":" STRING
+
+ dep_hostname_char_replacement ::= "hostname-char-replacement" ":" STRING
+
+ config_control ::= "config-control" ":" "{" config_control_params "}"
+
+ sub_config_control ::= "{" config_control_params "}"
+
+ config_control_params ::= config_control_param
+ | config_control_params "," config_control_param
+ | config_control_params ","
+
+ config_control_param ::= config_databases
+ | config_fetch_wait_time
+
+ config_databases ::= "config-databases" ":" "[" database_list "]"
+
+ config_fetch_wait_time ::= "config-fetch-wait-time" ":" INTEGER
+
+ loggers ::= "loggers" ":" "[" loggers_entries "]"
+
+ loggers_entries ::= logger_entry
+ | loggers_entries "," logger_entry
+ | loggers_entries ","
+
+ logger_entry ::= "{" logger_params "}"
+
+ logger_params ::= logger_param
+ | logger_params "," logger_param
+ | logger_params ","
+
+ logger_param ::= name
+ | output_options_list
+ | debuglevel
+ | severity
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ debuglevel ::= "debuglevel" ":" INTEGER
+
+ severity ::= "severity" ":" STRING
+
+ output_options_list ::= "output-options" ":" "[" output_options_list_content "]"
+
+ output_options_list_content ::= output_entry
+ | output_options_list_content "," output_entry
+ | output_options_list_content ","
+
+ output_entry ::= "{" output_params_list "}"
+
+ output_params_list ::= output_params
+ | output_params_list "," output_params
+ | output_params_list ","
+
+ output_params ::= output
+ | flush
+ | maxsize
+ | maxver
+ | pattern
+
+ output ::= "output" ":" STRING
+
+ flush ::= "flush" ":" BOOLEAN
+
+ maxsize ::= "maxsize" ":" INTEGER
+
+ maxver ::= "maxver" ":" INTEGER
+
+ pattern ::= "pattern" ":" STRING
+
+ compatibility ::= "compatibility" ":" "{" compatibility_params "}"
+
+ compatibility_params ::= compatibility_param
+ | compatibility_params "," compatibility_param
+ | compatibility_params ","
+
+ compatibility_param ::= lenient_option_parsing
+ | unknown_map_entry
+
+ lenient_option_parsing ::= "lenient-option-parsing" ":" BOOLEAN
+
diff --git a/doc/sphinx/grammar/grammar-netconf-parser.rst b/doc/sphinx/grammar/grammar-netconf-parser.rst
new file mode 100644
index 0000000..e10ac8f
--- /dev/null
+++ b/doc/sphinx/grammar/grammar-netconf-parser.rst
@@ -0,0 +1,221 @@
+This grammar is generated from ``netconf_parser.yy``. See :ref:`netconf` for more details.
+
+.. code-block:: BNF
+ :linenos:
+
+ Grammar
+
+ $accept ::= start EOF
+
+ start ::= START_JSON json
+
+ start ::= START_NETCONF netconf_syntax_map
+
+ start ::= START_SUB_NETCONF sub_netconf
+
+ sub_netconf ::= "{" global_params "}"
+
+ json ::= value
+
+ value ::= INTEGER
+ | FLOAT
+ | BOOLEAN
+ | STRING
+ | NULL
+ | map
+ | list_generic
+
+ map ::= "{" map_content "}"
+
+ map_value ::= map
+
+ map_content ::=
+ | not_empty_map
+
+ not_empty_map ::= STRING ":" value
+ | not_empty_map "," STRING ":" value
+ | not_empty_map ","
+
+ list_generic ::= "[" list_content "]"
+
+ list_content ::=
+ | not_empty_list
+
+ not_empty_list ::= value
+ | not_empty_list "," value
+ | not_empty_list ","
+
+ unknown_map_entry ::= STRING ":"
+
+ netconf_syntax_map ::= "{" global_object "}"
+
+ global_object ::= "Netconf" ":" "{" global_params "}"
+ | global_object_comma
+
+ global_object_comma ::= global_object ","
+
+ global_params ::=
+ | not_empty_global_params
+
+ not_empty_global_params ::= global_param
+ | not_empty_global_params "," global_param
+ | not_empty_global_params ","
+
+ global_param ::= boot_update
+ | subscribe_changes
+ | validate_changes
+ | managed_servers
+ | hooks_libraries
+ | loggers
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ boot_update ::= "boot-update" ":" BOOLEAN
+
+ subscribe_changes ::= "subscribe-changes" ":" BOOLEAN
+
+ validate_changes ::= "validate-changes" ":" BOOLEAN
+
+ user_context ::= "user-context" ":" map_value
+
+ comment ::= "comment" ":" STRING
+
+ hooks_libraries ::= "hooks-libraries" ":" "[" hooks_libraries_list "]"
+
+ hooks_libraries_list ::=
+ | not_empty_hooks_libraries_list
+
+ not_empty_hooks_libraries_list ::= hooks_library
+ | not_empty_hooks_libraries_list "," hooks_library
+ | not_empty_hooks_libraries_list ","
+
+ hooks_library ::= "{" hooks_params "}"
+
+ hooks_params ::= hooks_param
+ | hooks_params "," hooks_param
+ | hooks_params ","
+ | unknown_map_entry
+
+ hooks_param ::= library
+ | parameters
+
+ library ::= "library" ":" STRING
+
+ parameters ::= "parameters" ":" map_value
+
+ managed_servers ::= "managed-servers" ":" "{" servers_entries "}"
+
+ servers_entries ::=
+ | not_empty_servers_entries
+
+ not_empty_servers_entries ::= server_entry
+ | not_empty_servers_entries "," server_entry
+ | not_empty_servers_entries ","
+
+ server_entry ::= dhcp4_server
+ | dhcp6_server
+ | d2_server
+ | ca_server
+ | unknown_map_entry
+
+ dhcp4_server ::= "dhcp4" ":" "{" managed_server_params "}"
+
+ dhcp6_server ::= "dhcp6" ":" "{" managed_server_params "}"
+
+ d2_server ::= "d2" ":" "{" managed_server_params "}"
+
+ ca_server ::= "ca" ":" "{" managed_server_params "}"
+
+ managed_server_params ::= managed_server_param
+ | managed_server_params "," managed_server_param
+ | managed_server_params ","
+
+ managed_server_param ::= model
+ | boot_update
+ | subscribe_changes
+ | validate_changes
+ | control_socket
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ model ::= "model" ":" STRING
+
+ control_socket ::= "control-socket" ":" "{" control_socket_params "}"
+
+ control_socket_params ::= control_socket_param
+ | control_socket_params "," control_socket_param
+ | control_socket_params ","
+
+ control_socket_param ::= socket_type
+ | socket_name
+ | socket_url
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ socket_type ::= "socket-type" ":" socket_type_value
+
+ socket_type_value ::= "unix"
+ | "http"
+ | "stdout"
+
+ socket_name ::= "socket-name" ":" STRING
+
+ socket_url ::= "socket-url" ":" STRING
+
+ loggers ::= "loggers" ":" "[" loggers_entries "]"
+
+ loggers_entries ::= logger_entry
+ | loggers_entries "," logger_entry
+ | loggers_entries ","
+
+ logger_entry ::= "{" logger_params "}"
+
+ logger_params ::= logger_param
+ | logger_params "," logger_param
+ | logger_params ","
+
+ logger_param ::= name
+ | output_options_list
+ | debuglevel
+ | severity
+ | user_context
+ | comment
+ | unknown_map_entry
+
+ name ::= "name" ":" STRING
+
+ debuglevel ::= "debuglevel" ":" INTEGER
+
+ severity ::= "severity" ":" STRING
+
+ output_options_list ::= "output-options" ":" "[" output_options_list_content "]"
+
+ output_options_list_content ::= output_entry
+ | output_options_list_content "," output_entry
+ | output_options_list_content ","
+
+ output_entry ::= "{" output_params_list "}"
+
+ output_params_list ::= output_params
+ | output_params_list "," output_params
+ | output_params_list ","
+
+ output_params ::= output
+ | flush
+ | maxsize
+ | maxver
+ | pattern
+
+ output ::= "output" ":" STRING
+
+ flush ::= "flush" ":" BOOLEAN
+
+ maxsize ::= "maxsize" ":" INTEGER
+
+ maxver ::= "maxver" ":" INTEGER
+
+ pattern ::= "pattern" ":" STRING
+
diff --git a/doc/sphinx/grammar/grammar.rst b/doc/sphinx/grammar/grammar.rst
new file mode 100644
index 0000000..d8f77b0
--- /dev/null
+++ b/doc/sphinx/grammar/grammar.rst
@@ -0,0 +1,44 @@
+..
+ Copyright (C) 2021-2022 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ See the COPYRIGHT file distributed with this work for additional
+ information regarding copyright ownership.
+
+.. _bnf-grammar:
+
+Kea Configuration File Syntax (BNF)
+===================================
+
+Kea consists of several daemons, each with its own configuration syntax. The following sections
+provide a complete syntax of all possible parameters, written in Backus-Naur Form (BNF).
+See this `Wikipedia article on BNF <https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form>`_ for
+more information.
+
+BNF Grammar for DHCPv4
+----------------------
+
+.. include:: grammar-dhcp4-parser.rst
+
+BNF Grammar for DHCPv6
+----------------------
+
+.. include:: grammar-dhcp6-parser.rst
+
+BNF Grammar for Control Agent
+-----------------------------
+
+.. include:: grammar-ca-parser.rst
+
+BNF Grammar for DHCP-DDNS
+-------------------------
+
+.. include:: grammar-d2-parser.rst
+
+BNF Grammar for the Kea NETCONF Agent
+-------------------------------------
+
+.. include:: grammar-netconf-parser.rst
diff --git a/doc/sphinx/index.rst b/doc/sphinx/index.rst
new file mode 100644
index 0000000..438621e
--- /dev/null
+++ b/doc/sphinx/index.rst
@@ -0,0 +1,62 @@
+.. only:: not latex
+
+ .. image:: static/kea-logo-200.png
+ :align: right
+
+.. _introduction:
+
+##################################
+Kea Administrator Reference Manual
+##################################
+
+Kea is an open source implementation of the Dynamic Host Configuration
+Protocol (DHCP) servers, developed and maintained by Internet Systems
+Consortium (ISC).
+
+This is the reference guide for Kea version |release|.
+Links to the most up-to-date version of this document (in PDF, HTML,
+and plain text formats) can be found on `Read the Docs <https://kea.readthedocs.io>`_.
+Other useful Kea information can be found in our
+`Knowledgebase <https://kb.isc.org>`_.
+
+
+.. toctree::
+ :numbered:
+ :maxdepth: 5
+
+ arm/intro
+ arm/quickstart
+ arm/install
+ arm/admin
+ arm/config
+ arm/keactrl
+ arm/agent
+ arm/dhcp4-srv
+ arm/dhcp6-srv
+ arm/database-connectivity
+ arm/lease-expiration
+ arm/congestion-handling
+ arm/ddns
+ arm/lfc
+ arm/classify
+ arm/hooks
+ arm/stats
+ arm/ctrl-channel
+ arm/logging
+ arm/shell
+ arm/integrations
+ arm/stork
+ arm/security
+
+.. toctree::
+ :caption: Appendices
+ :name: appendices
+ :maxdepth: 1
+
+ api
+ manpages
+ kea-messages
+ arm/config-templates.rst
+ umls
+ grammar/grammar
+ arm/acknowledgments
diff --git a/doc/sphinx/man/kea-admin.8.rst b/doc/sphinx/man/kea-admin.8.rst
new file mode 100644
index 0000000..05f5d16
--- /dev/null
+++ b/doc/sphinx/man/kea-admin.8.rst
@@ -0,0 +1,169 @@
+..
+ Copyright (C) 2019-2023 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ See the COPYRIGHT file distributed with this work for additional
+ information regarding copyright ownership.
+
+.. iscman:: kea-admin
+
+``kea-admin`` - Shell script for managing Kea databases
+-------------------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`kea-admin` [command] [backend] [**-h** database_host]
+[**-P** database_port] [**-u** database_username]
+[**-p** [database_password]] [**-n** database_name] [**-d** script_directory]
+[**-v**] [**-x** extra_argument [**-x** extra_argument ...]]
+[**-4** | **-6**] [**-i** input_file] [**-o** output_file] [**-y**]
+
+Description
+~~~~~~~~~~~
+
+``kea-admin`` is a shell script that offers database maintenance. In
+particular, it features database initialization, database version
+checking, and database schema upgrading.
+
+Arguments
+~~~~~~~~~
+
+``command``
+ Specifies the command to be issued to the servers. It can be one of the
+ following:
+
+ ``db-init``
+ Initializes a new database schema. This is useful during a new Kea
+ installation. The database is initialized to the latest version
+ supported by the version of the software being installed.
+
+ ``db-version``
+ Reports the database backend version number. This is not necessarily
+ equal to the Kea version number, as each backend has its own
+ versioning scheme.
+
+ ``db-upgrade``
+ Conducts a database schema upgrade. This is useful when upgrading Kea.
+
+ ``lease-dump``
+ Dumps the contents of the lease database (for MySQL or
+ PostgreSQL backends) to a CSV (comma-separated values) text file. (Support
+ for the Cassandra backend has been deprecated.)
+ The first line of the file contains the column names. This can be used
+ as a way to switch from a database backend to a memfile backend.
+ Alternatively, it can be used as a diagnostic tool, so it provides a
+ portable form of the lease data. There are other mandatory arguments
+ that must be used together with this command. Either ``-4`` or ``-6`` must
+ be specified. Also ``-o`` or ``--output`` must be provided.
+
+ ``lease-upload``
+ Uploads leases from a CSV (comma-separated values) text file to a MySQL or
+ a PostgreSQL lease database. The CSV file needs to be in memfile format.
+ There are other mandatory arguments that must be used together with this
+ command. Either ``-4`` or ``-6`` must be specified.
+ Also ``-i`` or ``--input`` must be provided.
+
+ ``stats-recount``
+ Recounts lease statistics for a MySQL or PostgreSQL database.
+
+``backend``
+ Specifies the backend type. Currently allowed backends are: memfile,
+ mysql, and pgsql; cql has been deprecated.
+
+``-h|--host hostname``
+ Specifies the hostname when connecting to a database.
+ The default value is ``localhost``.
+
+``-i|--input input_file``
+ Specifies the CSV (comma-separated values) text file with leases to be uploaded.
+ Required for ``lease-upload``.
+
+``-P|--port port``
+ Specifies the port when connecting to a database. If not specified,
+ the default value chosen by the database client is used.
+
+``-u|--user username``
+ Specifies the username when connecting to a database.
+ The default value is ``keatest``.
+
+``-p|--password password``
+ Specifies the password when connecting to a database.
+ If only ``-p`` or ``--password`` is given, the user is prompted for a password.
+ If not specified at all, the ``KEA_ADMIN_DB_PASSWORD`` environment variable
+ is checked for a value and used if it exists.
+ Otherwise the default value of ``keatest`` is used.
+
+``-n|--name database-name``
+ Specifies the name of the database to connect to. The
+ default value is ``keatest``.
+
+``-d|--directory script-directory``
+ Specifies the override scripts directory. That script is used during
+ upgrades, database initialization, and possibly other operations.
+ The default value is ``(prefix)/share/kea/scripts/``.
+
+``-o|--output output_file``
+ Specifies the file to which the lease data will be dumped. Required for
+ ``lease-dump``.
+
+``-v|--version``
+ Prints the ``kea-admin`` version and quits.
+
+``-4``
+ Directs ``kea-admin`` to lease-dump the DHCPv4 leases. Incompatible with
+ the -6 option.
+
+``-6``
+ Directs ``kea-admin`` to lease-dump the DHCPv6 leases. Incompatible with
+ the -4 option.
+
+``-x|--extra``
+ Specifies an extra argument to pass to the database command tool e.g.
+ to invoke ``mysql`` with the ``--ssl`` argument. This can be repeated
+ to pass more than one argument. Quotes are not preserved. Avoid commands
+ containing spaces.
+
+``-y|--yes``
+ Assume yes on overwriting temporary files.
+
+Documentation
+~~~~~~~~~~~~~
+
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software - compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+https://kea.readthedocs.io.
+
+Kea source code is documented in the Kea Developer's Guide,
+available at https://reports.kea.isc.org/dev_guide/.
+
+The Kea project website is available at https://kea.isc.org.
+
+Mailing Lists and Support
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are two public mailing lists available for the Kea project. **kea-users**
+(kea-users at lists.isc.org) is intended for Kea users, while **kea-dev**
+(kea-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+https://lists.isc.org. The community provides best-effort support
+on both of those lists.
+
+ISC provides professional support for Kea services. See
+https://www.isc.org/kea/ for details.
+
+See Also
+~~~~~~~~
+
+:manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`,
+:manpage:`kea-dhcp-ddns(8)`, :manpage:`kea-ctrl-agent(8)`,
+:manpage:`keactrl(8)`, :manpage:`perfdhcp(8)`, :manpage:`kea-netconf(8)`,
+Kea Administrator Reference Manual.
diff --git a/doc/sphinx/man/kea-ctrl-agent.8.rst b/doc/sphinx/man/kea-ctrl-agent.8.rst
new file mode 100644
index 0000000..815c0e3
--- /dev/null
+++ b/doc/sphinx/man/kea-ctrl-agent.8.rst
@@ -0,0 +1,101 @@
+..
+ Copyright (C) 2019-2023 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ See the COPYRIGHT file distributed with this work for additional
+ information regarding copyright ownership.
+
+.. iscman:: kea-ctrl-agent
+
+``kea-ctrl-agent`` - Control Agent process in Kea
+-------------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`kea-ctrl-agent` [**-v**] [**-V**] [**-W**] [**-d**] [**-c** config-file] [**-t** config-file]
+
+Description
+~~~~~~~~~~~
+
+The ``kea-ctrl-agent`` provides a REST service for controlling Kea
+services. The received HTTP requests are decapsulated and forwarded to
+the respective Kea services in JSON format. Received JSON responses are
+encapsulated within HTTP responses and returned to the controlling
+entity. Some commands may be handled by the Control Agent directly, and
+not forwarded to any Kea service.
+
+Arguments
+~~~~~~~~~
+
+The arguments are as follows:
+
+``-v``
+ Displays the version.
+
+``-V``
+ Displays the extended version.
+
+``-W``
+ Displays the configuration report.
+
+``-d``
+ Sets the logging level to debug with extra verbosity. This is primarily for
+ development purposes in stand-alone mode.
+
+``-c config-file``
+ Specifies the file with the configuration for the Control Agent
+ server. It may also contain configuration entries for other Kea
+ services.
+
+``-t config-file``
+ Checks the syntax of the configuration file and reports the first error,
+ if any. Note that not all parameters are completely checked; in
+ particular, service and client sockets are not opened, and hook
+ libraries are not loaded.
+
+Documentation
+~~~~~~~~~~~~~
+
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software - compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+https://kea.readthedocs.io.
+
+Kea source code is documented in the Kea Developer's Guide,
+available at https://reports.kea.isc.org/dev_guide/.
+
+The Kea project website is available at https://kea.isc.org.
+
+Mailing Lists and Support
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are two public mailing lists available for the Kea project. **kea-users**
+(kea-users at lists.isc.org) is intended for Kea users, while **kea-dev**
+(kea-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+https://lists.isc.org. The community provides best-effort support
+on both of those lists.
+
+ISC provides professional support for Kea services. See
+https://www.isc.org/kea/ for details.
+
+History
+~~~~~~~
+
+The ``kea-ctrl-agent`` was first coded in December 2016 by Marcin
+Siodelski.
+
+See Also
+~~~~~~~~
+
+:manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`,
+:manpage:`kea-dhcp-ddns(8)`, :manpage:`kea-admin(8)`, :manpage:`keactrl(8)`,
+:manpage:`perfdhcp(8)`, :manpage:`kea-lfc(8)`, Kea Administrator Reference Manual.
diff --git a/doc/sphinx/man/kea-dhcp-ddns.8.rst b/doc/sphinx/man/kea-dhcp-ddns.8.rst
new file mode 100644
index 0000000..ecaf3fd
--- /dev/null
+++ b/doc/sphinx/man/kea-dhcp-ddns.8.rst
@@ -0,0 +1,101 @@
+..
+ Copyright (C) 2019-2023 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ See the COPYRIGHT file distributed with this work for additional
+ information regarding copyright ownership.
+
+.. iscman:: kea-dhcp-ddns
+
+``kea-dhcp-ddns`` - DHCP-DDNS process in Kea
+--------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`kea-dhcp-ddns` [**-v**] [**-V**] [**-W**] [**-d**] [**-c** config-file] [**-t** config-file]
+
+Description
+~~~~~~~~~~~
+
+The ``kea-dhcp-ddns`` service process requests an update of DNS mapping
+based on DHCP lease-change events. It runs as a separate process that
+expects to receive Name Change Requests from Kea DHCP servers.
+
+Arguments
+~~~~~~~~~
+
+The arguments are as follows:
+
+``-v``
+ Displays the version.
+
+``-V``
+ Displays the extended version.
+
+``-W``
+ Displays the configuration report.
+
+``-d``
+ Sets the logging level to debug with extra verbosity. This is primarily for
+ development purposes in stand-alone mode.
+
+``-c config-file``
+ Specifies the configuration file with the configuration for the DHCP-DDNS server. It
+ may also contain configuration entries for other Kea services.
+
+``-t config-file``
+ Checks the syntax of the configuration file and reports the first error,
+ if any. Note that not all parameters are completely checked; in
+ particular, a service socket is not opened.
+
+Documentation
+~~~~~~~~~~~~~
+
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software - compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+https://kea.readthedocs.io.
+
+Kea source code is documented in the Kea Developer's Guide,
+available at https://reports.kea.isc.org/dev_guide/.
+
+The Kea project website is available at https://kea.isc.org.
+
+Mailing Lists and Support
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are two public mailing lists available for the Kea project. **kea-users**
+(kea-users at lists.isc.org) is intended for Kea users, while **kea-dev**
+(kea-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+https://lists.isc.org. The community provides best-effort support
+on both of those lists.
+
+ISC provides professional support for Kea services. See
+https://www.isc.org/kea/ for details.
+
+History
+~~~~~~~
+
+The ``b10-dhcp-ddns`` process was first coded in May 2013 by Thomas
+Markwalder.
+
+Kea became a standalone server and the BIND 10 framework was removed. The
+DHCP-DDNS server binary was renamed to kea-dhcp-ddns in July 2014. Kea
+1.0.0 was released in December 2015.
+
+See Also
+~~~~~~~~
+
+:manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`,
+:manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`keactrl(8)`,
+:manpage:`perfdhcp(8)`, :manpage:`kea-netconf(8)`, :manpage:`kea-lfc(8)`,
+Kea Administrator Reference Manual.
diff --git a/doc/sphinx/man/kea-dhcp4.8.rst b/doc/sphinx/man/kea-dhcp4.8.rst
new file mode 100644
index 0000000..6bc42f4
--- /dev/null
+++ b/doc/sphinx/man/kea-dhcp4.8.rst
@@ -0,0 +1,116 @@
+..
+ Copyright (C) 2019-2023 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ See the COPYRIGHT file distributed with this work for additional
+ information regarding copyright ownership.
+
+.. iscman:: kea-dhcp4
+
+``kea-dhcp4`` - DHCPv4 server in Kea
+------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`kea-dhcp4` [**-v**] [**-V**] [**-W**] [**-d**] [**-c** config-file] [**-t** config-file] [**-p** server-port-number] [**-P** client-port-number]
+
+Description
+~~~~~~~~~~~
+
+The ``kea-dhcp4`` daemon provides the DHCPv4 server implementation.
+
+Arguments
+~~~~~~~~~
+
+The arguments are as follows:
+
+``-v``
+ Displays the version.
+
+``-V``
+ Displays the extended version.
+
+``-W``
+ Displays the configuration report.
+
+``-d``
+ Enables the debug mode with extra verbosity.
+
+``-c config-file``
+ Specifies the configuration file with the configuration for the DHCPv4 server. It
+ may also contain configuration entries for other Kea services.
+
+``-t config-file``
+ Checks the configuration file and reports the first error, if any. Note
+ that not all parameters are completely checked; in particular,
+ service and control channel sockets are not opened, and hook
+ libraries are not loaded.
+
+``-T config-file``
+ Checks the configuration file and reports the first error, if any.
+ It performs extra checks beyond what -t offers, such as establishing
+ database connections (for the lease backend, host reservations backend,
+ configuration backend, and forensic logging backend), loading hook libraries,
+ parsing hook-library configurations, etc. It does not open UNIX or TCP/UDP sockets,
+ nor does it open or rotate files, as any of these actions could interfere
+ with a running process on the same machine.
+
+``-p server-port-number``
+ Specifies the server port number (1-65535) on which the server listens. This is
+ useful for testing purposes only.
+
+``-P client-port-number``
+ Specifies the client port number (1-65535) to which the server responds. This is
+ useful for testing purposes only.
+
+Documentation
+~~~~~~~~~~~~~
+
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software - compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+https://kea.readthedocs.io.
+
+Kea source code is documented in the Kea Developer's Guide,
+available at https://reports.kea.isc.org/dev_guide/.
+
+The Kea project website is available at https://kea.isc.org.
+
+Mailing Lists and Support
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are two public mailing lists available for the Kea project. **kea-users**
+(kea-users at lists.isc.org) is intended for Kea users, while **kea-dev**
+(kea-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+https://lists.isc.org. The community provides best-effort support
+on both of those lists.
+
+ISC provides professional support for Kea services. See
+https://www.isc.org/kea/ for details.
+
+History
+~~~~~~~
+
+The ``b10-dhcp4`` daemon was first coded in November 2011 by Tomek
+Mrugalski.
+
+In mid-2014, Kea was decoupled from the BIND 10 framework and became a
+standalone DHCP server. The DHCPv4 server binary was renamed to
+``kea-dhcp4``. Kea 1.0.0 was released in December 2015.
+
+See Also
+~~~~~~~~
+
+:manpage:`kea-dhcp6(8)`, :manpage:`kea-dhcp-ddns(8)`,
+:manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`keactrl(8)`,
+:manpage:`perfdhcp(8)`, :manpage:`kea-netconf(8)`, :manpage:`kea-lfc(8)`,
+Kea Administrator Reference Manual.
diff --git a/doc/sphinx/man/kea-dhcp6.8.rst b/doc/sphinx/man/kea-dhcp6.8.rst
new file mode 100644
index 0000000..d18e7b6
--- /dev/null
+++ b/doc/sphinx/man/kea-dhcp6.8.rst
@@ -0,0 +1,116 @@
+..
+ Copyright (C) 2019-2023 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ See the COPYRIGHT file distributed with this work for additional
+ information regarding copyright ownership.
+
+.. iscman:: kea-dhcp6
+
+``kea-dhcp6`` - DHCPv6 server in Kea
+------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`kea-dhcp6` [**-v**] [**-V**] [**-W**] [**-d**] [**-c** config-file] [**-t** config-file] [**-p** server-port-number] [**-P** client-port-number]
+
+Description
+~~~~~~~~~~~
+
+The ``kea-dhcp6`` daemon provides the DHCPv6 server implementation.
+
+Arguments
+~~~~~~~~~
+
+The arguments are as follows:
+
+``-v``
+ Displays the version.
+
+``-V``
+ Displays the extended version.
+
+``-W``
+ Displays the configuration report.
+
+``-d``
+ Enables the debug mode with extra verbosity.
+
+``-c config-file``
+ Specifies the configuration file with the configuration for the DHCPv6 server. It
+ may also contain configuration entries for other Kea services.
+
+``-t config-file``
+ Checks the configuration file and reports the first error, if any. Note
+ that not all parameters are completely checked; in particular,
+ service and control channel sockets are not opened, and hook
+ libraries are not loaded.
+
+``-T config-file``
+ Checks the configuration file and reports the first error, if any.
+ It performs extra checks beyond what -t offers, such as establishing
+ database connections (for the lease backend, host reservations backend,
+ configuration backend, and forensic logging backend), loading hook libraries,
+ parsing hook-library configurations, etc. It does not open UNIX or TCP/UDP sockets, nor
+ does it open or rotate files, as any of these actions could interfere with
+ a running process on the same machine.
+
+``-p server-port-number``
+ Specifies the server port number (1-65535) on which the server listens. This is
+ useful for testing purposes only.
+
+``-P client-port-number``
+ Specifies the client port number (1-65535) to which the server responds. This is
+ useful for testing purposes only.
+
+Documentation
+~~~~~~~~~~~~~
+
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software - compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+https://kea.readthedocs.io.
+
+Kea source code is documented in the Kea Developer's Guide,
+available at https://reports.kea.isc.org/dev_guide/.
+
+The Kea project website is available at https://kea.isc.org.
+
+Mailing Lists and Support
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are two public mailing lists available for the Kea project. **kea-users**
+(kea-users at lists.isc.org) is intended for Kea users, while **kea-dev**
+(kea-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+https://lists.isc.org. The community provides best-effort support
+on both of those lists.
+
+ISC provides professional support for Kea services. See
+https://www.isc.org/kea/ for details.
+
+History
+~~~~~~~
+
+The ``b10-dhcp6`` daemon was first coded in June 2011 by Tomek
+Mrugalski.
+
+In mid-2014, Kea was decoupled from the BIND 10 framework and became a
+standalone DHCP server. The DHCPv6 server binary was renamed to
+``kea-dhcp6``. Kea 1.0.0 was released in December 2015.
+
+See Also
+~~~~~~~~
+
+:manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp-ddns(8)`,
+:manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`keactrl(8)`,
+:manpage:`perfdhcp(8)`, :manpage:`kea-netconf(8)`, :manpage:`kea-lfc(8)`,
+Kea Administrator Reference Manual.
diff --git a/doc/sphinx/man/kea-lfc.8.rst b/doc/sphinx/man/kea-lfc.8.rst
new file mode 100644
index 0000000..d00e97d
--- /dev/null
+++ b/doc/sphinx/man/kea-lfc.8.rst
@@ -0,0 +1,132 @@
+..
+ Copyright (C) 2019-2023 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ See the COPYRIGHT file distributed with this work for additional
+ information regarding copyright ownership.
+
+.. iscman:: kea-lfc
+
+``kea-lfc`` - Lease File Cleanup process in Kea
+-----------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`kea-lfc` [**-4**|**-6**] [**-c** config-file] [**-p** pid-file] [**-x** previous-file] [**-i** copy-file] [**-o** output-file] [**-f** finish-file] [**-v**] [**-V**] [**-W**] [**-d**] [**-h**]
+
+Description
+~~~~~~~~~~~
+
+The ``kea-lfc`` service process removes redundant information from the
+files used to provide persistent storage for the memfile database
+backend. The service is written to run as a stand-alone process. While
+it can be started externally, there is usually no need to do this. It
+is run periodically by the Kea DHCP servers.
+
+Arguments
+~~~~~~~~~
+
+The arguments are as follows:
+
+``-4 | -6``
+ Indicates the protocol version of the lease files; must be either 4 or 6.
+
+``-c config-file``
+ Specifies the file with the configuration for the ``kea-lfc``
+ process. It may also contain configuration entries for other Kea
+ services. Currently ``kea-lfc`` gets all of its arguments from the
+ command line.
+
+``-p pid-file``
+ Specifies the PID file. When the ``kea-lfc`` process starts, it attempts to
+ determine if another instance of the process is already running, by
+ examining the PID file. If one is already running, the new process is
+ terminated. If one is not running, Kea writes its PID into the PID file.
+
+``-x previous-file``
+ Specifies the previous or ex-lease file. When ``kea-lfc`` starts, this is the
+ result of any previous run of ``kea-lfc``; when ``kea-lfc`` finishes,
+ it is the result of the current run. If ``kea-lfc`` is interrupted before
+ completing, this file may not exist.
+
+``-i copy-file``
+ Specifies the input or copy of lease file. Before the DHCP server invokes
+ ``kea-lfc``, it moves the current lease file here and then calls
+ ``kea-lfc`` with this file.
+
+``-o output-file``
+ Specifies the output lease file, which is the temporary file ``kea-lfc`` should use to
+ write the leases. Once this file is finished writing, it is
+ moved to the finish file (see below).
+
+``-f finish-file``
+ Specifies the finish or completion file, another temporary file ``kea-lfc`` uses
+ for bookkeeping. When ``kea-lfc`` finishes writing the output file,
+ it moves it to this file name. After ``kea-lfc`` finishes deleting
+ the other files (previous and input), it moves this file to the previous
+ lease file. By moving the files in this fashion, the ``kea-lfc`` and
+ the DHCP server processes can determine the correct file to use even
+ if one of the processes was interrupted before completing its task.
+
+``-v``
+ Causes the version stamp to be printed.
+
+``-V``
+ Causes a longer form of the version stamp to be printed.
+
+``-W``
+ Displays the configuration report.
+
+``-d``
+ Sets the logging level to debug with extra verbosity. This is primarily for
+ development purposes in stand-alone mode.
+
+``-h``
+ Causes the usage string to be printed.
+
+Documentation
+~~~~~~~~~~~~~
+
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software - compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+https://kea.readthedocs.io.
+
+Kea source code is documented in the Kea Developer's Guide,
+available at https://reports.kea.isc.org/dev_guide/.
+
+The Kea project website is available at https://kea.isc.org.
+
+Mailing Lists and Support
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are two public mailing lists available for the Kea project. **kea-users**
+(kea-users at lists.isc.org) is intended for Kea users, while **kea-dev**
+(kea-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+https://lists.isc.org. The community provides best-effort support
+on both of those lists.
+
+ISC provides professional support for Kea services. See
+https://www.isc.org/kea/ for details.
+
+History
+~~~~~~~
+
+The ``kea-lfc`` process was first coded in January 2015 by the ISC
+Kea/DHCP team.
+
+See Also
+~~~~~~~~
+
+:manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`, :manpage:`kea-dhcp-ddns(8)`,
+:manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`keactrl(8)`,
+:manpage:`perfdhcp(8)`, :manpage:`kea-netconf(8)`, Kea Administrator Reference Manual.
diff --git a/doc/sphinx/man/kea-netconf.8.rst b/doc/sphinx/man/kea-netconf.8.rst
new file mode 100644
index 0000000..d9cf92e
--- /dev/null
+++ b/doc/sphinx/man/kea-netconf.8.rst
@@ -0,0 +1,96 @@
+..
+ Copyright (C) 2019-2023 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ See the COPYRIGHT file distributed with this work for additional
+ information regarding copyright ownership.
+
+.. iscman:: kea-netconf
+
+``kea-netconf`` - NETCONF agent for configuring Kea
+---------------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`kea-netconf` [**-v**] [**-V**] [**-W**] [**-d**] [**-c** config-file] [**-t** config-file]
+
+Description
+~~~~~~~~~~~
+
+The ``kea-netconf`` agent provides a YANG/NETCONF interface for the Kea
+environment.
+
+Arguments
+~~~~~~~~~
+
+The arguments are as follows:
+
+``-v``
+ Displays the version.
+
+``-V``
+ Displays the extended version.
+
+``-W``
+ Displays the configuration report.
+
+``-d``
+ Enables the debug mode with extra verbosity.
+
+``-c config-file``
+ Specifies the file with the configuration for the NETCONF agent.
+
+``-t config-file``
+ Checks the syntax of the configuration file and reports the first error,
+ if any. Note that not all parameters are completely checked; in
+ particular, service and client sockets are not opened, and hook
+ libraries are not loaded.
+
+Documentation
+~~~~~~~~~~~~~
+
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software - compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+https://kea.readthedocs.io.
+
+Kea source code is documented in the Kea Developer's Guide,
+available at https://reports.kea.isc.org/dev_guide/.
+
+The Kea project website is available at https://kea.isc.org.
+
+Mailing Lists and Support
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are two public mailing lists available for the Kea project. **kea-users**
+(kea-users at lists.isc.org) is intended for Kea users, while **kea-dev**
+(kea-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+https://lists.isc.org. The community provides best-effort support
+on both of those lists.
+
+ISC provides professional support for Kea services. See
+https://www.isc.org/kea/ for details.
+
+History
+~~~~~~~
+
+Early prototypes of ``kea-netconf`` implementation were written during IETF
+Hackathons in Berlin, London, and Montreal. An actual production-ready
+implementation was started in August 2018 by Tomek Mrugalski and Francis
+Dupont.
+
+See Also
+~~~~~~~~
+
+:manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`, :manpage:`kea-dhcp-ddns(8)`,
+:manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`keactrl(8)`,
+:manpage:`perfdhcp(8)`, :manpage:`kea-lfc(8)`, Kea Administrator Reference Manual.
diff --git a/doc/sphinx/man/kea-shell.8.rst b/doc/sphinx/man/kea-shell.8.rst
new file mode 100644
index 0000000..d46af78
--- /dev/null
+++ b/doc/sphinx/man/kea-shell.8.rst
@@ -0,0 +1,129 @@
+..
+ Copyright (C) 2019-2023 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ See the COPYRIGHT file distributed with this work for additional
+ information regarding copyright ownership.
+
+.. iscman:: kea-shell
+
+``kea-shell`` - Text client for Control Agent process
+-----------------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`kea-shell` [**-h**] [**-v**] [**--host**] [**--port**] [**--path**] [**--ca**] [**--cert**] [**--key**] [**--auth-user**] [**--auth-password**] [**--timeout**] [**--service**] [command]
+
+Description
+~~~~~~~~~~~
+
+The ``kea-shell`` provides a REST client for the Kea Control Agent (CA).
+It takes commands as a command-line parameter that is sent to the CA
+with proper JSON encapsulation. Optional arguments may be specified on
+the standard input. The request is sent via HTTP and a response is
+retrieved, displayed on the standard output. Basic HTTP authentication
+and HTTPS, i.e. TLS transport, are supported.
+
+
+Arguments
+~~~~~~~~~
+
+The arguments are as follows:
+
+``-h``
+ Displays help regarding command-line parameters.
+
+``-v``
+ Displays the version.
+
+``--host``
+ Specifies the host to connect to. The Control Agent must be running at the
+ specified host. If not specified, 127.0.0.1 is used.
+
+``--port``
+ Specifies the TCP port to connect to. Control Agent must be listening
+ at the specified port. If not specified, 8000 is used.
+
+``--path``
+ Specifies the path in the URL to connect to. If not specified, an empty
+ path is used. As Control Agent listens at the empty path, this
+ parameter is useful only with a reverse proxy.
+
+``--ca``
+ Specifies the file or directory name of the Certification Authority.
+ If not specified, HTTPS is not used.
+
+``--cert``
+ Specifies the file name of the user end-entity public key certificate.
+ If specified, the file name of the user key must also be specified.
+
+``--key``
+ Specifies the file name of the user key file. If specified, the file
+ name of the user certificate must also be specified.
+ Encrypted key files are not supported.
+
+``--auth-user``
+ Specifies the user ID for basic HTTP authentication. If not specified,
+ or specified as the empty string, authentication is not used.
+
+``--auth-password``
+ Specifies the password for basic HTTP authentication. If not specified
+ but the user ID is specified, an empty password is used.
+
+``--timeout``
+ Specifies the connection timeout, in seconds. The default is 10.
+
+``--service``
+ Specifies the service that is the target of a command. If not
+ specified, the Control Agent itself is targeted. May be used more than once
+ to specify multiple targets.
+
+``command``
+ Specifies the command to be sent to the CA. If not specified,
+ ``list-commands`` is used.
+
+Documentation
+~~~~~~~~~~~~~
+
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software - compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+https://kea.readthedocs.io.
+
+Kea source code is documented in the Kea Developer's Guide,
+available at https://reports.kea.isc.org/dev_guide/.
+
+The Kea project website is available at https://kea.isc.org.
+
+Mailing Lists and Support
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are two public mailing lists available for the Kea project. **kea-users**
+(kea-users at lists.isc.org) is intended for Kea users, while **kea-dev**
+(kea-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+https://lists.isc.org. The community provides best-effort support
+on both of those lists.
+
+ISC provides professional support for Kea services. See
+https://www.isc.org/kea/ for details.
+
+History
+~~~~~~~
+
+The ``kea-shell`` was first coded in March 2017 by Tomek Mrugalski.
+
+See Also
+~~~~~~~~
+
+:manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`, :manpage:`kea-dhcp-ddns(8)`,
+:manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`keactrl(8)`,
+:manpage:`perfdhcp(8)`, :manpage:`kea-lfc(8)`, Kea Administrator Reference Manual.
diff --git a/doc/sphinx/man/keactrl.8.rst b/doc/sphinx/man/keactrl.8.rst
new file mode 100644
index 0000000..ca188e6
--- /dev/null
+++ b/doc/sphinx/man/keactrl.8.rst
@@ -0,0 +1,127 @@
+..
+ Copyright (C) 2019-2023 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ See the COPYRIGHT file distributed with this work for additional
+ information regarding copyright ownership.
+
+.. iscman:: keactrl
+
+``keactrl`` - Shell script for managing Kea
+-------------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`keactrl` [**command**] [**-c** keactrl-config-file] [**-s** server[,server,...]] [**-v**]
+
+Description
+~~~~~~~~~~~
+
+``keactrl`` is a shell script which controls the startup, shutdown, and
+reconfiguration of the Kea servers (``kea-dhcp4``, ``kea-dhcp6``,
+``kea-dhcp-ddns``, ``kea-ctrl-agent``, and ``kea-netconf``). It also
+provides a way to check the current status of the servers and
+determine the configuration files in use.
+
+Configuration File
+~~~~~~~~~~~~~~~~~~
+
+Depending on the user's requirements, not all of the available servers need be run.
+The ``keactrl`` configuration file specifies which servers are enabled and which
+are disabled. By default the configuration file is
+``[kea-install-dir]/etc/kea/keactrl.conf``.
+
+See the Kea Administrator Reference Manual for documentation of the
+parameters in the ``keactrl`` configuration file.
+
+Options
+~~~~~~~
+
+``command``
+ Specifies the command to be issued to the servers. It can be one of the following:
+
+ ``start``
+ Starts the servers.
+
+ ``stop``
+ Stops the servers.
+
+ ``reload``
+ Instructs the servers to re-read the Kea configuration file. This
+ command is not supported by the NETCONF agent.
+
+ ``status``
+ Prints the status of the servers.
+
+``-c|--ctrl-config keactrl-config-file``
+ Specifies the ``keactrl`` configuration file. Without this switch,
+ ``keactrl`` uses the file
+ ``[kea-install-dir]/etc/kea/keactrl.conf``.
+
+``-s|--server server[,server,...]``
+ Specifies a subset of the enabled servers to which the command should
+ be issued. The list of servers should be separated by commas, with no
+ intervening spaces. Acceptable values are:
+
+ ``dhcp4``
+ DHCPv4 server (``kea-dhcp4``).
+
+ ``dhcp6``
+ DHCPv6 server (``kea-dhcp6``).
+
+ ``dhcp_ddns``
+ DHCP DDNS server (``kea-dhcp-ddns``).
+
+ ``ctrl_agent``
+ Control Agent (``kea-ctrl-agent``).
+
+ ``netconf``
+ NETCONF agent (``kea-netconf``).
+
+ ``all``
+ All servers, including NETCONF if it was configured to be
+ built. This is the default.
+
+``-v|--version``
+ Prints the ``keactrl`` version and quits.
+
+Documentation
+~~~~~~~~~~~~~
+
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software - compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+https://kea.readthedocs.io.
+
+Kea source code is documented in the Kea Developer's Guide,
+available at https://reports.kea.isc.org/dev_guide/.
+
+The Kea project website is available at https://kea.isc.org.
+
+Mailing Lists and Support
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are two public mailing lists available for the Kea project. **kea-users**
+(kea-users at lists.isc.org) is intended for Kea users, while **kea-dev**
+(kea-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+https://lists.isc.org. The community provides best-effort support
+on both of those lists.
+
+ISC provides professional support for Kea services. See
+https://www.isc.org/kea/ for details.
+
+See Also
+~~~~~~~~
+
+:manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`, :manpage:`kea-dhcp-ddns(8)`,
+:manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`kea-netconf(8)`,
+:manpage:`perfdhcp(8)`, :manpage:`kea-lfc(8)`, Kea Administrator Reference Manual.
diff --git a/doc/sphinx/man/man8s.mk b/doc/sphinx/man/man8s.mk
new file mode 100644
index 0000000..7af7c1f
--- /dev/null
+++ b/doc/sphinx/man/man8s.mk
@@ -0,0 +1,10 @@
+man8s += $(sphinxbuilddir)/man/kea-admin.8
+man8s += $(sphinxbuilddir)/man/kea-ctrl-agent.8
+man8s += $(sphinxbuilddir)/man/kea-dhcp4.8
+man8s += $(sphinxbuilddir)/man/kea-dhcp6.8
+man8s += $(sphinxbuilddir)/man/kea-dhcp-ddns.8
+man8s += $(sphinxbuilddir)/man/kea-lfc.8
+man8s += $(sphinxbuilddir)/man/kea-netconf.8
+man8s += $(sphinxbuilddir)/man/kea-shell.8
+man8s += $(sphinxbuilddir)/man/keactrl.8
+man8s += $(sphinxbuilddir)/man/perfdhcp.8
diff --git a/doc/sphinx/man/perfdhcp.8.rst b/doc/sphinx/man/perfdhcp.8.rst
new file mode 100644
index 0000000..465d6f0
--- /dev/null
+++ b/doc/sphinx/man/perfdhcp.8.rst
@@ -0,0 +1,564 @@
+..
+ Copyright (C) 2019-2023 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ See the COPYRIGHT file distributed with this work for additional
+ information regarding copyright ownership.
+
+.. iscman:: perfdhcp
+
+``perfdhcp`` - DHCP benchmarking tool
+-------------------------------------
+
+Synopsis
+~~~~~~~~
+
+:program:`perfdhcp` [**-1**] [**-4** | **-6**] [**-A** encapsulation-level] [**-b** base] [**-B**] [**-c**] [**-C** separator] [**-d** drop-time] [**-D** max-drop] [-e lease-type] [**-E** time-offset] [**-f** renew-rate] [**-F** release-rate] [**-g** thread-mode] [**-h**] [**-i**] [**-I** ip-offset] [**-J** remote-address-list-file] [**-l** local-address|interface] [**-L** local-port] [**-M** mac-list-file] [**-n** num-request] [**-N** remote-port] [**-O** random-offset] [**-o** code,hexstring] [**--or** encapsulation-level:code,hexstring] [**-p** test-period] [**-P** preload] [**-r** rate] [**-R** num-clients] [**-s** seed] [**-S** srvid-offset] [**--scenario** name] [**-t** report] [**-T** template-file] [**-u**] [**-v**] [**-W** exit-wait-time] [**-w** script_name] [**-x** diagnostic-selector] [**-X** xid-offset] [server]
+
+Description
+~~~~~~~~~~~
+
+``perfdhcp`` is a DHCP benchmarking tool. It provides a way to measure
+the performance of DHCP servers by generating large amounts of traffic
+from multiple simulated clients. It is able to test both IPv4 and IPv6
+servers, and provides statistics concerning response times and the
+number of requests that are dropped.
+
+The tool supports two different scenarios, which offer certain behaviors to be tested.
+By default (the basic scenario), tests are run using the full four-packet exchange sequence
+(DORA for DHCPv4, SARR for DHCPv6). An option is provided to run tests
+using the initial two-packet exchange (DO and SA) instead. It is also
+possible to configure ``perfdhcp`` to send DHCPv6 RENEW and RELEASE messages
+at a specified rate, in parallel with the DHCPv6 four-way exchanges. By
+default, if there is no response received with one second, a response is
+considered lost and ``perfdhcp`` continues with other transactions.
+
+A second scenario, called avalanche, is selected via ``--scenario avalanche``.
+It first sends the number of Discovery or Solicit messages specified by the ``-R`` option; then
+a retransmission (with an exponential back-off mechanism) is used for each simulated client, until all requests are
+answered. It generates a report when all clients receive their addresses, or when
+it is manually stopped. This scenario attempts to replicate a
+case where the server is not able to handle the traffic swiftly
+enough. Real clients will assume the packet or response was lost
+and will retransmit, further increasing DHCP traffic. This is
+sometimes called an avalanche effect, thus the scenario name.
+Option ``-p`` is ignored in the avalanche scenario.
+
+When running a performance test, ``perfdhcp`` exchanges packets with
+the server under test as quickly as possible, unless the ``-r`` parameter is used to
+limit the request rate. The length of the test can be limited by setting
+a threshold on any or all of the number of requests made by
+``perfdhcp``, the elapsed time, or the number of requests dropped by the
+server.
+
+Templates
+~~~~~~~~~
+
+To allow the contents of packets sent to the server to be customized,
+``perfdhcp`` allows the specification of template files that determine
+the contents of the packets. For example, the customized packet may
+contain a DHCPv6 ORO to request a set of options to be returned by the
+server, or it may contain the Client FQDN option to request that the server
+perform DNS updates. This may be used to discover performance
+bottlenecks for different server configurations (e.g. DDNS enabled or
+disabled).
+
+Up to two template files can be specified on the command line, with each file
+representing the contents of a particular type of packet, and the type being
+determined by the test being carried out. For example, if testing
+DHCPv6:
+
+- With no template files specified on the command line, ``perfdhcp``
+ generates both Solicit and Request packets.
+
+- With one template file specified, that file is used as the
+ pattern for Solicit packets: ``perfdhcp`` generates the Request
+ packets.
+
+- With two template files given on the command line, the first is
+ used as the pattern for Solicit packets, and the second as the pattern
+ for Request packets.
+
+(A similar determination applies to DHCPv4's DHCPDISCOVER and DHCPREQUEST
+packets.)
+
+The template file holds the DHCP packet, represented as a stream of ASCII
+hexadecimal digits; it excludes any IP/UDP stack headers. The
+template file must not contain any characters other than hexadecimal
+digits and spaces. Spaces are discarded when the template file is parsed;
+in the file, ``12B4`` is the same as ``12 B4``, which is the same as
+``1 2 B 4``.
+
+The template files should be used in conjunction with the command-line
+parameters which specify offsets of the data fields being modified in
+outbound packets. For example, the ``-E time-offset`` switch specifies
+the offset of the DHCPv6 Elapsed Time option in the packet template.
+If the offset is specified, ``perfdhcp`` injects the current elapsed-time
+value into this field before sending the packet to the server.
+
+In many scenarios, ``perfdhcp`` needs to simulate multiple clients,
+each having a unique client identifier. Since packets for each client are
+generated from the same template file, it is necessary to randomize the
+client identifier (or HW address in DHCPv4) in the packet created from
+it. The ``-O random-offset`` option allows specification of the offset in
+the template where randomization should be performed. It is important to
+note that this offset points to the end (not the beginning) of the
+client identifier (or HW address field). The number of bytes being
+randomized depends on the number of simulated clients. If the number of
+simulated clients is between 1 and 255, only one byte (to which the
+randomization offset points) is randomized. If the number of
+simulated clients is between 256 and 65535, two bytes are
+randomized. Note that the last two bytes of the client identifier are
+randomized in this case: the byte which the randomization offset parameter
+points to, and the one which precedes it (random-offset - 1). If the
+number of simulated clients exceeds 65535, three bytes are
+randomized, and so on.
+
+``perfdhcp`` can simulate traffic from multiple subnets by enabling option
+``-J`` and passing a path to a file that contains v4 or v6 addresses to be
+used as relays in generated messages. That enables testing of vast numbers
+of Kea shared networks. While testing DHCPv4, Kea should be started with the
+``KEA_TEST_SEND_RESPONSES_TO_SOURCE`` environment variable, to force Kea
+to send generated messages to the source address of the incoming packet.
+
+Templates may currently be used to generate packets being sent to the
+server in 4-way exchanges, i.e. Solicit, Request (DHCPv6) and DHCPDISCOVER,
+DHCPREQUEST (DHCPv4). They cannot be used when Renew or DHCPRELEASE packets are
+being sent.
+
+Options
+~~~~~~~
+
+``-1``
+ Takes the ``server-id`` option from the first received message.
+
+``-4``
+ Establishes DHCPv4 operation; this is the default. It is incompatible with the
+ ``-6`` option.
+
+``-6``
+ Establishes DHCPv6 operation. It is incompatible with the ``-4`` option.
+
+``-b basetype=value``
+ Indicates the base MAC or DUID used to simulate different clients. The basetype
+ may be "mac" or "duid". (The keyword "ether" may alternatively used
+ for MAC.) The ``-b`` option can be specified multiple times. The MAC
+ address must consist of six octets separated by single (:) or double
+ (::) colons; for example: mac=00:0c:01:02:03:04. The DUID value is a
+ hexadecimal string; it must be at least six octets long and not
+ longer than 64 bytes, and the length must be less than 128
+ hexadecimal digits. For example: duid=0101010101010101010110111F14.
+
+``-d drop-time``
+ Specifies the time after which a request is treated as having been
+ lost. The value is given in seconds and may contain a fractional
+ component. The default is 1.
+
+``-e lease-type``
+ Specifies the type of lease being requested from the server. It may
+ be one of the following:
+
+ ``address-only``
+ Only regular addresses (v4 or v6) are requested.
+
+ ``prefix-only``
+ Only IPv6 prefixes are requested.
+
+ ``address-and-prefix``
+ Both IPv6 addresses and prefixes are requested.
+
+ The ``-e prefix-only`` and ``-e address-and-prefix`` forms may not be used
+ with the ``-4`` option.
+
+``-F release-rate``
+ Specifies the rate at which DHCPv4 DHCPRELEASE or DHCPv6 Release requests are sent to a server. This value
+ is only valid when used in conjunction with the exchange rate (given
+ by ``-r rate``). Furthermore, the sum of this value and the renew-rate
+ (given by ``-f rate``) must be equal to or less than the exchange
+ rate value.
+
+``-f renew-rate``
+ Specifies the rate at which DHCPv4 DHCPREQUEST or DHCPv6 Renew requests are sent to a server.
+ This value is only valid when used in conjunction with the exchange
+ rate (given by ``-r rate``). Furthermore, the sum of this value and
+ the release-rate (given by ``-F rate``) must be equal to or less than the
+ exchange rate.
+
+``-g thread-mode``
+ Allows selection of thread-mode, which can be either ``single`` or ``multi``. In multi-thread mode,
+ packets are received in a separate thread, which allows better
+ utilisation of CPUs. In a single-CPU system it is better to run in one
+ thread, to avoid threads blocking each other. If more than one CPU is
+ present in the system, multi-thread mode is the default; otherwise
+ single-thread is the default.
+
+``-h``
+ Prints help and exits.
+
+``-i``
+ Performs only the initial part of the exchange: DISCOVER-OFFER if ``-4`` is
+ selected, Solicit-Advertise if ``-6`` is chosen.
+
+ ``-i`` is incompatible with the following options: ``-1``, ``-d``,
+ ``-D``, ``-E``, ``-S``, ``-I`` and ``-F``. In addition, it cannot be
+ used with multiple instances of ``-O``, ``-T``, and ``-X``.
+
+``-J remote-address-list-file``
+ Specifies a text file that includes multiple addresses, and is
+ designed to test shared networks. If provided, ``perfdhcp``
+ randomly chooses one of the addresses for each exchange, to generate traffic
+ from multiple subnets. When testing DHCPv4, it
+ should be started with the ``KEA_TEST_SEND_RESPONSES_TO_SOURCE=ENABLE``
+ environment variable; otherwise, ``perfdhcp`` will not be able to receive responses.
+
+``-l local-addr|interface``
+ For DHCPv4 operation, specifies the local hostname/address to use when
+ communicating with the server. By default, the interface address
+ through which traffic would normally be routed to the server is used.
+ For DHCPv6 operation, specifies the name of the network interface
+ through which exchanges are initiated.
+
+``-L local-port``
+ Specifies the local port to use. This must be zero or a positive
+ integer up to 65535. A value of 0 (the default) allows ``perfdhcp``
+ to choose its own port.
+
+``-M mac-list-file``
+ Specifies a text file containing a list of MAC addresses, one per line. If
+ provided, a MAC address is chosen randomly from this list for
+ every new exchange. In DHCPv6, MAC addresses are used to
+ generate DUID-LLs. This parameter must not be used in conjunction
+ with the ``-b`` parameter.
+
+``-N remote-port``
+ Specifies the remote port to use. This must be zero or a positive
+ integer up to 65535. A value of 0 (the default) allows ``perfdhcp``
+ to choose the standard service port.
+
+``-o code,hexstring``
+ Forces ``perfdhcp`` to insert the specified extra option (or options if
+ used several times) into packets being transmitted. The code
+ specifies the option code and the hexstring is a hexadecimal string that
+ defines the content of the option. Care should be taken as ``perfdhcp``
+ does not offer any kind of logic behind those options; they are simply
+ inserted into packets and sent as is. Be careful not to duplicate
+ options that are already inserted. For example, to insert client
+ class identifier (option code 60) with a string "docsis", use
+ "-o 60,646f63736973". The ``-o`` may be used multiple times. It is
+ necessary to specify the protocol family (either ``-4`` or ``-6``) before
+ using ``-o``.
+
+``-P preload``
+ Initiates preload exchanges back-to-back at startup. Must be 0
+ (the default) or a positive integer.
+
+``-r rate``
+ Initiates the rate of DORA/SARR (or if ``-i`` is given, DO/SA) exchanges per
+ second. A periodic report is generated showing the number of
+ exchanges which were not completed, as well as the average response
+ latency. The program continues until interrupted, at which point a
+ final report is generated.
+
+``-R num-clients``
+ Specifies how many different clients are used. With a value of 1 (the
+ default), all requests appear to come from the same client.
+ Must be a positive number.
+
+``-s seed``
+ Specifies the seed for randomization, making runs of ``perfdhcp``
+ repeatable. This must be 0 or a positive integer. The value 0 means that a
+ seed is not used; this is the default.
+
+``--scenario name``
+ Specifies the type of scenario, and can be ``basic`` (the default) or ``avalanche``.
+
+``-T template-file``
+ Specifies a file containing the template to use as a stream of
+ hexadecimal digits. This may be specified up to two times and
+ controls the contents of the packets sent (see the "Templates"
+ section above).
+
+``-u``
+ Enables checks for address uniqueness. The lease valid-lifetime should not be shorter
+ than the test duration, and clients should not request an address more than once without
+ releasing it.
+
+``-v``
+ Prints the version of this program.
+
+``-W exit-wait-time``
+ Specifies the exit-wait-time parameter, which causes ``perfdhcp`` to wait for
+ a certain amount of time after an exit condition has been met, to receive all
+ packets without sending any new packets. Expressed in microseconds.
+ If not specified, 0 is used (i.e. exit immediately after exit
+ conditions are met).
+
+``-w script_name``
+ Specifies the name of the script to be run before/after ``perfdhcp``.
+ When called, the script is passed a single parameter, either "start" or
+ "stop", indicating whether it is being called before or after ``perfdhcp``.
+
+``-x diagnostic-selector``
+ Includes extended diagnostics in the output. This is a
+ string of single keywords specifying the operations for which verbose
+ output is desired. The selector key letters are:
+
+ ``a``
+ Prints the decoded command-line arguments.
+
+ ``e``
+ Prints the exit reason.
+
+ ``i``
+ Prints the rate-processing details.
+
+ ``l``
+ Prints the received leases.
+
+ ``s``
+ Prints the first server ID.
+
+ ``t``
+ When finished, prints timers of all successful exchanges.
+
+ ``T``
+ When finished, prints templates.
+
+``-Y seconds``
+ Time in seconds after which ``perfdhcp`` starts simulating the client waiting longer for server responses. This increases the
+ ``secs`` field in DHCPv4 and sends increased values in the ``Elapsed Time`` option in DHCPv6. Must be used with ``-y``.
+
+``-y seconds``
+ Time in seconds during which ``perfdhcp`` simulates the client waiting longer for server responses. This increases
+ the ``secs`` field in DHCPv4 and sends increased values in the ``Elapsed Time`` option in DHCPv6. Must be used with ``-Y``.
+
+DHCPv4-Only Options
+~~~~~~~~~~~~~~~~~~~
+
+The following options only apply for DHCPv4 (i.e. when ``-4`` is given).
+
+``-B``
+ Forces broadcast handling.
+
+DHCPv6-Only Options
+~~~~~~~~~~~~~~~~~~~
+
+The following options only apply for DHCPv6 (i.e. when ``-6`` is given).
+
+``-c``
+ Adds a rapid-commit option (exchanges are Solicit-Advertise).
+
+``-A encapsulation-level``
+ Specifies that relayed traffic must be generated. The argument
+ specifies the level of encapsulation, i.e. how many relay agents are
+ simulated. Currently the only supported encapsulation-level value is
+ 1, which means that the generated traffic is equivalent to the amount of
+ traffic passing through a single relay agent.
+
+``--or encapsulation-level:code,hexstring``
+ This option is very similar to ``-o``, only that it forces ``perfdhcp``
+ to insert the specified extra option (or options if used several times)
+ into relayed DHCPv6 message at given level of encapsulation (currently
+ the only supported encapsulation-level value is 1). The code
+ specifies the option code and the hexstring is a hexadecimal string that
+ defines the content of the option. Care should be taken as ``perfdhcp``
+ does not offer any kind of logic behind those options; they are simply
+ inserted into packets and sent as is. Please notice that ``encapsulation-level:``
+ is optional and if omitted, default encapsulation-level value 1 is used.
+ For example, to insert Subscriber identifier (option code 38) with a
+ value 1234 at first level of encapsulation, use ``--or 38,31323334``
+ or ``--or 1:38,31323334``. The ``--or`` may be used multiple times.
+ It must be used together with ``-A``.
+
+Template-Related Options
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+The following options may only be used in conjunction with ``-T`` and
+control how ``perfdhcp`` modifies the template. The options may be
+specified multiple times on the command line; each occurrence affects
+the corresponding template file (see "Templates" above).
+
+``-E time-offset``
+ Specifies the offset of the ``secs`` field (DHCPv4) or ``Elapsed Time`` option (DHCPv6) in the
+ second (i.e. Request) template; must be 0 or a positive integer. A
+ value of 0 disables this.
+
+``-I ip-offset``
+ Specifies the offset of the IP address (DHCPv4) in the ``requested-ip``
+ option or ``IA_NA`` option (DHCPv6) in the second (Request) template.
+
+``-O random-offset``
+ Specifies the offset of the last octet to randomize in the template. This
+ must be an integer greater than 3. The ``-T`` switch must be given to
+ use this option.
+
+``-S srvid-offset``
+ Specifies the offset of the ``server-id`` option in the second (Request) template.
+ This must be a positive integer, and the switch can only be used
+ when the template option (``-T``) is also given.
+
+``-X xid-offset``
+ Specifies the offset of the transaction ID (xid) in the template. This must be a
+ positive integer, and the switch can only be used when the template
+ option (``-T``) is also given.
+
+Options Controlling a Test
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``-D max-drop``
+ Aborts the test immediately if "max-drop" requests have been dropped.
+ Use ``-D 1`` to abort if even a single request has
+ been dropped. "max-drop" must be a positive integer. If "max-drop"
+ includes the suffix ``%``, it specifies the maximum percentage of
+ requests that may be dropped before aborting. In this case, testing of
+ the threshold begins after 10 requests are expected to have been
+ received.
+
+``-n num-requests``
+ Initiates "num-request" transactions. No report is generated until all
+ transactions have been initiated/waited-for, after which a report is
+ generated and the program terminates.
+
+``-p test-period``
+ Sends requests for "test-period", which is specified in the same manner
+ as ``-d``. This can be used as an alternative to ``-n``, or both
+ options can be given, in which case the testing is completed when
+ either limit is reached.
+
+``-t interval``
+ Sets the delay (in seconds) between two successive reports.
+
+``-C separator``
+ Suppresses the preliminary output and causes the interim data to
+ only contain the values delimited by ``separator``. Used in
+ conjunction with ``-t`` to produce easily parsable
+ reports at ``-t`` intervals.
+
+Arguments
+~~~~~~~~~
+
+``server``
+ Indicates the server to test, specified as an IP address. In the DHCPv6 case, the
+ special name ``all`` can be used to refer to
+ ``All_DHCP_Relay_Agents_and_Servers`` (the multicast address FF02::1:2),
+ or the special name ``servers`` to refer to ``All_DHCP_Servers`` (the
+ multicast address FF05::1:3). The server is mandatory except where
+ the ``-l`` option is given to specify an interface, in which case it
+ defaults to ``all``.
+
+Errors
+~~~~~~
+
+``perfdhcp`` can report the following errors in the packet exchange:
+
+tooshort
+ A message was received that was too short.
+
+orphans
+ A message was received which does not match one sent to the server (i.e.
+ it is a duplicate message, a message that has arrived after an
+ excessive delay, or one that is just not recognized).
+
+locallimit
+ Local system limits have been reached when sending a message.
+
+Exit Status
+~~~~~~~~~~~
+
+``perfdhcp`` exits with one of the following status codes:
+
+0
+ Success.
+
+1
+ General error.
+
+2
+ Error in command-line arguments.
+
+3
+ No general failures in operation, but one or more exchanges were
+ unsuccessful.
+
+Usage Examples
+~~~~~~~~~~~~~~
+
+Here is an example that simulates regular DHCPv4 traffic of 100 DHCPv4 devices (-R 100),
+10 packets per second (-r 10), shows the query/response rate details (-xi),
+shows a report every 2 seconds (-t 2), and sends the packets to the IP 192.0.2.1:
+
+.. code-block:: console
+
+ sudo perfdhcp -xi -t 2 -r 10 -R 100 192.0.2.1
+
+Here's a similar case, but for DHCPv6. Note that the DHCPv6 protocol uses link-local
+addresses, so the interface (eth0 in this example) must be specified on which to send the
+traffic. ``all`` is a convenience alias for ``All_DHCP_Relay_Agents_and_Servers``
+(the multicast address FF02::1:2). It is also possible to use the ``servers`` alias
+to refer to ``All_DHCP_Servers`` (the multicast address FF05::1:3). The default is ``all``.
+
+.. code-block:: console
+
+ sudo perfdhcp -6 -xi -t 1 -r 1 -R 10 -l eth0 all
+
+The following examples simulate normal DHCPv4 and DHCPv6 traffic that, after 3 seconds,
+starts pretending not to receive any responses from the server for 10 seconds. The
+DHCPv4 protocol signals this by an increased ``secs`` field, while DHCPv6 uses the
+``Elapsed Time`` option. In real networks, this indicates that clients are not getting
+responses in a timely matter. This can be used to simulate some HA scenarios, as Kea
+uses the ``secs`` field and ``Elapsed Time`` option value as one of the indicators
+that the HA partner is not responding. When enabled with ``-y`` and ``-Y``, the ``secs``
+and ``Elapsed Time`` values increase steadily.
+
+.. code-block:: console
+
+ sudo perfdhcp -xi -t 1 -r 1 -y 10 -Y 3 192.0.2.1
+
+ sudo perfdhcp -6 -xi -t 1 -r 1 -y 10 -Y 3 2001:db8::1
+
+Documentation
+~~~~~~~~~~~~~
+
+Kea comes with an extensive Kea Administrator Reference Manual that covers
+all aspects of running the Kea software - compilation, installation,
+configuration, configuration examples, and much more. Kea also features a
+Kea Messages Manual, which lists all possible messages Kea can print
+with a brief description for each of them. Both documents are
+available in various formats (.txt, .html, .pdf) with the Kea
+distribution. The Kea documentation is available at
+https://kea.readthedocs.io.
+
+Kea source code is documented in the Kea Developer's Guide,
+available at https://reports.kea.isc.org/dev_guide/.
+
+The Kea project website is available at https://kea.isc.org.
+
+Mailing Lists and Support
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are two public mailing lists available for the Kea project. **kea-users**
+(kea-users at lists.isc.org) is intended for Kea users, while **kea-dev**
+(kea-dev at lists.isc.org) is intended for Kea developers, prospective
+contributors, and other advanced users. Both lists are available at
+https://lists.isc.org. The community provides best-effort support
+on both of those lists.
+
+ISC provides professional support for Kea services. See
+https://www.isc.org/kea/ for details.
+
+History
+~~~~~~~
+
+The ``perfdhcp`` tool was initially coded in October 2011 by John
+DuBois, Francis Dupont, and Marcin Siodelski of ISC. Kea 1.0.0, which
+included ``perfdhcp``, was released in December 2015.
+
+See Also
+~~~~~~~~
+
+:manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`, :manpage:`kea-dhcp-ddns(8)`,
+:manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`kea-netconf(8)`,
+:manpage:`keactrl(8)`, :manpage:`kea-lfc(8)`, Kea Administrator Reference Manual.
diff --git a/doc/sphinx/man/rst_man_sources.mk b/doc/sphinx/man/rst_man_sources.mk
new file mode 100644
index 0000000..933fafe
--- /dev/null
+++ b/doc/sphinx/man/rst_man_sources.mk
@@ -0,0 +1,10 @@
+rst_man_sources += man/kea-admin.8.rst
+rst_man_sources += man/kea-ctrl-agent.8.rst
+rst_man_sources += man/kea-dhcp4.8.rst
+rst_man_sources += man/kea-dhcp6.8.rst
+rst_man_sources += man/kea-dhcp-ddns.8.rst
+rst_man_sources += man/kea-lfc.8.rst
+rst_man_sources += man/kea-netconf.8.rst
+rst_man_sources += man/kea-shell.8.rst
+rst_man_sources += man/keactrl.8.rst
+rst_man_sources += man/perfdhcp.8.rst
diff --git a/doc/sphinx/manpages.rst b/doc/sphinx/manpages.rst
new file mode 100644
index 0000000..a428bfa
--- /dev/null
+++ b/doc/sphinx/manpages.rst
@@ -0,0 +1,28 @@
+..
+ Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ See the COPYRIGHT file distributed with this work for additional
+ information regarding copyright ownership.
+
+.. _manpages:
+
+Manual Pages
+============
+
+.. toctree::
+ :maxdepth: 1
+
+ man/kea-dhcp4.8
+ man/kea-dhcp6.8
+ man/kea-ctrl-agent.8
+ man/keactrl.8
+ man/kea-admin.8
+ man/kea-dhcp-ddns.8
+ man/kea-lfc.8
+ man/kea-shell.8
+ man/kea-netconf.8
+ man/perfdhcp.8
diff --git a/doc/sphinx/mes-files.txt b/doc/sphinx/mes-files.txt
new file mode 100644
index 0000000..58c4676
--- /dev/null
+++ b/doc/sphinx/mes-files.txt
@@ -0,0 +1,29 @@
+src/hooks/dhcp/flex_option/flex_option_messages.mes
+src/hooks/dhcp/bootp/bootp_messages.mes
+src/hooks/dhcp/mysql_cb/mysql_cb_messages.mes
+src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes
+src/hooks/dhcp/high_availability/ha_messages.mes
+src/hooks/dhcp/stat_cmds/stat_cmds_messages.mes
+src/hooks/dhcp/user_chk/user_chk_messages.mes
+src/lib/config/config_messages.mes
+src/lib/hooks/hooks_messages.mes
+src/lib/dhcpsrv/dhcpsrv_messages.mes
+src/lib/dhcpsrv/alloc_engine_messages.mes
+src/lib/dhcpsrv/hosts_messages.mes
+src/lib/http/auth_messages.mes
+src/lib/http/http_messages.mes
+src/lib/dhcp_ddns/dhcp_ddns_messages.mes
+src/lib/database/db_messages.mes
+src/lib/log/log_messages.mes
+src/lib/log/logimpl_messages.mes
+src/lib/log/tests/log_test_messages.mes
+src/lib/process/process_messages.mes
+src/lib/asiodns/asiodns_messages.mes
+src/lib/eval/eval_messages.mes
+src/lib/d2srv/d2_messages.mes
+src/lib/tcp/tcp_messages.mes
+src/bin/dhcp4/dhcp4_messages.mes
+src/bin/agent/ca_messages.mes
+src/bin/dhcp6/dhcp6_messages.mes
+src/bin/lfc/lfc_messages.mes
+src/bin/netconf/netconf_messages.mes
diff --git a/doc/sphinx/mes2doc.py b/doc/sphinx/mes2doc.py
new file mode 100755
index 0000000..91d13cb
--- /dev/null
+++ b/doc/sphinx/mes2doc.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http:#mozilla.org/MPL/2.0/.
+
+# Produce System Messages Manual
+#
+# This tool reads all the message files given on the command line.
+# It pulls all the messages and description out, sorts them by
+# message ID, and writes them out as a single (formatted) file.
+#
+# Invocation:
+# The code is invoked using the command line:
+#
+# system_messages.py [-o <output-file>] <files>
+#
+# If no output file is specified, output is written to stdout.
+# The produced format is ReStructuredText.
+
+import re
+import argparse
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(description='Convert set of *.mes files to .rst documentation format')
+ parser.add_argument('-o', '--output', help='Output file name (default to stdout).')
+ parser.add_argument('files', help='Input .mes files.', nargs='+')
+
+ args = parser.parse_args()
+ return args
+
+
+def read_input_files(files):
+ messages = {}
+ for f in files:
+ with open(f) as fp:
+ print("Processing %s" % f)
+ namespace = None
+ msg_descr = None
+ msg_id = None
+ msg_text = None
+ for line in fp.readlines():
+ line = line.strip()
+
+ if not line or line.startswith('#'):
+ pass
+
+ elif line.startswith('$'):
+ pass
+
+ elif line.startswith('%'):
+ # end previous message
+ if msg_id is not None:
+ section = msg_id.split('_')[0]
+ messages[msg_id] = (section, msg_id, msg_text, msg_descr)
+
+ # start next message
+ m = re.search('^%\s?([A-Z0-9_]+)\s+(.*)', line);
+ msg_id, msg_text = m.groups()
+ msg_descr = []
+
+ else:
+ msg_descr.append(line)
+
+ return messages
+
+
+def generate_rst(messages):
+ rst = '''.. _kea-messages:
+
+###################
+Kea Messages Manual
+###################
+
+Kea is an open source implementation of the Dynamic Host Configuration
+Protocol (DHCP) servers, developed and maintained by Internet Systems
+Consortium (ISC).
+
+This is the reference guide for Kea version |release|.
+Links to the most up-to-date version of this document (in PDF, HTML,
+and plain text formats), along with other useful information about
+Kea, can be found in ISC's `Knowledgebase <https://kea.readthedocs.io>`_.
+
+Please note that in the messages below, the percent sign ("%") followed by a number is
+used to indicate a placeholder for data that is provided by the Kea code during its operation.
+
+
+.. toctree::
+ :numbered:
+ :maxdepth: 5
+
+'''
+
+ prev_section = None
+ for _, msg in sorted(messages.items()):
+ section, msg_id, msg_text, msg_descr = msg
+
+ if section != prev_section:
+ prev_section = section
+ rst += section + '\n'
+ rst += '~' * len(section) + '\n\n'
+
+ rst += '**' + msg_id + '**\n\n'
+
+ rst += msg_text + '\n\n'
+
+ rst += ''.join([' ' + l + '\n' for l in msg_descr])
+ rst += '\n'
+
+
+ return rst
+
+def generate(in_files, out_file):
+ messages = read_input_files(in_files)
+
+ rst = generate_rst(messages)
+
+ if out_file:
+ with open(out_file, 'w') as f:
+ f.write(rst)
+ print('Wrote generated RST content to: %s' % out_file)
+ else:
+ print(rst)
+
+
+def main():
+ args = parse_args()
+ generate(args.files, args.output)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/sphinx/mes_files.mk b/doc/sphinx/mes_files.mk
new file mode 100644
index 0000000..29517b2
--- /dev/null
+++ b/doc/sphinx/mes_files.mk
@@ -0,0 +1,29 @@
+mes_files += $(top_srcdir)/src/hooks/dhcp/flex_option/flex_option_messages.mes
+mes_files += $(top_srcdir)/src/hooks/dhcp/bootp/bootp_messages.mes
+mes_files += $(top_srcdir)/src/hooks/dhcp/mysql_cb/mysql_cb_messages.mes
+mes_files += $(top_srcdir)/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes
+mes_files += $(top_srcdir)/src/hooks/dhcp/high_availability/ha_messages.mes
+mes_files += $(top_srcdir)/src/hooks/dhcp/stat_cmds/stat_cmds_messages.mes
+mes_files += $(top_srcdir)/src/hooks/dhcp/user_chk/user_chk_messages.mes
+mes_files += $(top_srcdir)/src/lib/config/config_messages.mes
+mes_files += $(top_srcdir)/src/lib/hooks/hooks_messages.mes
+mes_files += $(top_srcdir)/src/lib/dhcpsrv/dhcpsrv_messages.mes
+mes_files += $(top_srcdir)/src/lib/dhcpsrv/alloc_engine_messages.mes
+mes_files += $(top_srcdir)/src/lib/dhcpsrv/hosts_messages.mes
+mes_files += $(top_srcdir)/src/lib/http/auth_messages.mes
+mes_files += $(top_srcdir)/src/lib/http/http_messages.mes
+mes_files += $(top_srcdir)/src/lib/dhcp_ddns/dhcp_ddns_messages.mes
+mes_files += $(top_srcdir)/src/lib/database/db_messages.mes
+mes_files += $(top_srcdir)/src/lib/log/log_messages.mes
+mes_files += $(top_srcdir)/src/lib/log/logimpl_messages.mes
+mes_files += $(top_srcdir)/src/lib/log/tests/log_test_messages.mes
+mes_files += $(top_srcdir)/src/lib/process/process_messages.mes
+mes_files += $(top_srcdir)/src/lib/asiodns/asiodns_messages.mes
+mes_files += $(top_srcdir)/src/lib/eval/eval_messages.mes
+mes_files += $(top_srcdir)/src/lib/d2srv/d2_messages.mes
+mes_files += $(top_srcdir)/src/lib/tcp/tcp_messages.mes
+mes_files += $(top_srcdir)/src/bin/dhcp4/dhcp4_messages.mes
+mes_files += $(top_srcdir)/src/bin/agent/ca_messages.mes
+mes_files += $(top_srcdir)/src/bin/dhcp6/dhcp6_messages.mes
+mes_files += $(top_srcdir)/src/bin/lfc/lfc_messages.mes
+mes_files += $(top_srcdir)/src/bin/netconf/netconf_messages.mes
diff --git a/doc/sphinx/static/kea-imageonly-100bw.png b/doc/sphinx/static/kea-imageonly-100bw.png
new file mode 100644
index 0000000..fdbaf40
--- /dev/null
+++ b/doc/sphinx/static/kea-imageonly-100bw.png
Binary files differ
diff --git a/doc/sphinx/static/kea-logo-100x70.png b/doc/sphinx/static/kea-logo-100x70.png
new file mode 100644
index 0000000..1de8411
--- /dev/null
+++ b/doc/sphinx/static/kea-logo-100x70.png
Binary files differ
diff --git a/doc/sphinx/static/kea-logo-200.png b/doc/sphinx/static/kea-logo-200.png
new file mode 100644
index 0000000..7dbd993
--- /dev/null
+++ b/doc/sphinx/static/kea-logo-200.png
Binary files differ
diff --git a/doc/sphinx/static/kea.css b/doc/sphinx/static/kea.css
new file mode 100644
index 0000000..55d7758
--- /dev/null
+++ b/doc/sphinx/static/kea.css
@@ -0,0 +1,26 @@
+/* give more screen width to the content as by default it is too narrow
+ and many tables and boxes are squeezed */
+.wy-nav-content {
+ max-width: 1100px;
+}
+
+/* by default table content is not wrapped and then the tables
+ are pretty wide so removing that
+ */
+.wy-table-responsive table td, .wy-table-responsive table th {
+ white-space: normal;
+}
+
+/* Paint literal text black. */
+.rst-content code.literal {
+ color: black;
+}
+
+/* Have literal text that references other sections underlined.
+ * It helps the reader figure out that it's a clickable link.
+ * This type of referencing can be achieved using text roles.
+ * https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html
+ */
+.rst-content code.xref {
+ text-decoration: underline dotted gray;
+}
diff --git a/doc/sphinx/uml/appendRequestedOptions.png b/doc/sphinx/uml/appendRequestedOptions.png
new file mode 100644
index 0000000..8b217f8
--- /dev/null
+++ b/doc/sphinx/uml/appendRequestedOptions.png
Binary files differ
diff --git a/doc/sphinx/uml/appendRequestedOptions.svg b/doc/sphinx/uml/appendRequestedOptions.svg
new file mode 100644
index 0000000..c888943
--- /dev/null
+++ b/doc/sphinx/uml/appendRequestedOptions.svg
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="832px" preserveAspectRatio="none" style="width:376px;height:832px;background:#FFFFFF;" version="1.1" viewBox="0 0 376 832" width="376px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="333" x="20" y="34.9659">Append requested options algorithm (Kea 1.8.0)</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="158" x="103.5" y="51.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="138" x="113.5" y="73.896">get configured option list</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="262" x="51.5" y="107.412"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="242" x="61.5" y="130.2401">get parameter request list (PRL) from query</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="231" x="67" y="207.7561"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="211" x="77" y="230.5842">get configured options in dhcp4 space</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="181" x="92" y="308.1003"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="161" x="102" y="330.9283">push back option code to PRL</text>
+ <polygon fill="#F1F1F1" points="114.5,264.1003,250.5,264.1003,262.5,276.1003,250.5,288.1003,114.5,288.1003,102.5,276.1003,114.5,264.1003" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="136" x="114.5" y="280.3682">for each persistent option</text>
+ <polygon fill="#F1F1F1" points="75,163.7561,290,163.7561,302,175.7561,290,187.7561,75,187.7561,63,175.7561,75,163.7561" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="215" x="75" y="180.0241">for each item from configured option list</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="231" x="67" y="545.9353"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="211" x="77" y="568.7634">get configured options in dhcp4 space</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="148" x="108.5" y="651.7704"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="128" x="118.5" y="674.5985">add option to response</text>
+ <polygon fill="#F1F1F1" points="166.5,602.2794,198.5,602.2794,210.5,614.2794,198.5,626.2794,166.5,626.2794,154.5,614.2794,166.5,602.2794" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="21" x="186.5" y="638.0383">first</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="32" x="166.5" y="618.5474">found</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="145" x="210.5" y="611.0565">not found or already found</text>
+ <polygon fill="#F1F1F1" points="182.5,708.1145,194.5,720.1145,182.5,732.1145,170.5,720.1145,182.5,708.1145" style="stroke:#181818;stroke-width:0.5;"/>
+ <polygon fill="#F1F1F1" points="75,501.9353,290,501.9353,302,513.9353,290,525.9353,75,525.9353,63,513.9353,75,501.9353" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="215" x="75" y="518.2033">for each item from configured option list</text>
+ <polygon fill="#F1F1F1" points="108,452.4444,257,452.4444,269,464.4444,257,476.4444,108,476.4444,96,464.4444,108,452.4444" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="186.5" y="488.2033">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="149" x="108" y="468.7123">option is not set in response</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="269" y="461.2214">no</text>
+ <polygon fill="#F1F1F1" points="182.5,774.1145,194.5,786.1145,182.5,798.1145,170.5,786.1145,182.5,774.1145" style="stroke:#181818;stroke-width:0.5;"/>
+ <polygon fill="#F1F1F1" points="129,408.4444,236,408.4444,248,420.4444,236,432.4444,129,432.4444,117,420.4444,129,408.4444" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="107" x="129" y="424.7123">for each code in PRL</text>
+ <ellipse cx="26" cy="463.4444" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="26" cy="463.4444" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="87.412" y2="107.412"/>
+ <polygon fill="#181818" points="178.5,97.412,182.5,107.412,186.5,97.412,182.5,101.412" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="288.1003" y2="308.1003"/>
+ <polygon fill="#181818" points="178.5,298.1003,182.5,308.1003,186.5,298.1003,182.5,302.1003" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="344.4444" y2="354.4444"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="285" y1="354.4444" y2="354.4444"/>
+ <polygon fill="#181818" points="281,324.2723,285,314.2723,289,324.2723,285,320.2723" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="285" x2="285" y1="276.1003" y2="354.4444"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="285" x2="262.5" y1="276.1003" y2="276.1003"/>
+ <polygon fill="#181818" points="272.5,272.1003,262.5,276.1003,272.5,280.1003,268.5,276.1003" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="102.5" x2="80" y1="276.1003" y2="276.1003"/>
+ <polygon fill="#181818" points="76,310.2723,80,320.2723,84,310.2723,80,314.2723" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="80" x2="80" y1="276.1003" y2="366.4444"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="80" x2="314" y1="366.4444" y2="366.4444"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="314" x2="314" y1="175.7561" y2="366.4444"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="314" x2="302" y1="175.7561" y2="175.7561"/>
+ <polygon fill="#181818" points="312,171.7561,302,175.7561,312,179.7561,308,175.7561" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="244.1003" y2="264.1003"/>
+ <polygon fill="#181818" points="178.5,254.1003,182.5,264.1003,186.5,254.1003,182.5,258.1003" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="187.7561" y2="207.7561"/>
+ <polygon fill="#181818" points="178.5,197.7561,182.5,207.7561,186.5,197.7561,182.5,201.7561" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="63" x2="51" y1="175.7561" y2="175.7561"/>
+ <polygon fill="#181818" points="47,266.6003,51,276.6003,55,266.6003,51,270.6003" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="51" x2="51" y1="175.7561" y2="388.4444"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="51" x2="182.5" y1="388.4444" y2="388.4444"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="388.4444" y2="408.4444"/>
+ <polygon fill="#181818" points="178.5,398.4444,182.5,408.4444,186.5,398.4444,182.5,402.4444" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="143.7561" y2="163.7561"/>
+ <polygon fill="#181818" points="178.5,153.7561,182.5,163.7561,186.5,153.7561,182.5,157.7561" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="626.2794" y2="651.7704"/>
+ <polygon fill="#181818" points="178.5,641.7704,182.5,651.7704,186.5,641.7704,182.5,645.7704" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="210.5" x2="266.5" y1="614.2794" y2="614.2794"/>
+ <polygon fill="#181818" points="262.5,659.9424,266.5,669.9424,270.5,659.9424,266.5,663.9424" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="266.5" x2="266.5" y1="614.2794" y2="720.1145"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="266.5" x2="194.5" y1="720.1145" y2="720.1145"/>
+ <polygon fill="#181818" points="204.5,716.1145,194.5,720.1145,204.5,724.1145,200.5,720.1145" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="688.1145" y2="708.1145"/>
+ <polygon fill="#181818" points="178.5,698.1145,182.5,708.1145,186.5,698.1145,182.5,702.1145" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="582.2794" y2="602.2794"/>
+ <polygon fill="#181818" points="178.5,592.2794,182.5,602.2794,186.5,592.2794,182.5,596.2794" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="525.9353" y2="545.9353"/>
+ <polygon fill="#181818" points="178.5,535.9353,182.5,545.9353,186.5,535.9353,182.5,539.9353" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="732.1145" y2="742.1145"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="314" y1="742.1145" y2="742.1145"/>
+ <polygon fill="#181818" points="310,632.2704,314,622.2704,318,632.2704,314,628.2704" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="314" x2="314" y1="513.9353" y2="742.1145"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="314" x2="302" y1="513.9353" y2="513.9353"/>
+ <polygon fill="#181818" points="312,509.9353,302,513.9353,312,517.9353,308,513.9353" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="63" x2="51" y1="513.9353" y2="513.9353"/>
+ <polygon fill="#181818" points="47,618.2704,51,628.2704,55,618.2704,51,622.2704" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="51" x2="51" y1="513.9353" y2="754.1145"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="51" x2="182.5" y1="754.1145" y2="754.1145"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="754.1145" y2="774.1145"/>
+ <polygon fill="#181818" points="178.5,764.1145,182.5,774.1145,186.5,764.1145,182.5,768.1145" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="476.4444" y2="501.9353"/>
+ <polygon fill="#181818" points="178.5,491.9353,182.5,501.9353,186.5,491.9353,182.5,495.9353" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="269" x2="324" y1="464.4444" y2="464.4444"/>
+ <polygon fill="#181818" points="320,612.2704,324,622.2704,328,612.2704,324,616.2704" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="324" x2="324" y1="464.4444" y2="786.1145"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="324" x2="194.5" y1="786.1145" y2="786.1145"/>
+ <polygon fill="#181818" points="204.5,782.1145,194.5,786.1145,204.5,790.1145,200.5,786.1145" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="432.4444" y2="452.4444"/>
+ <polygon fill="#181818" points="178.5,442.4444,182.5,452.4444,186.5,442.4444,182.5,446.4444" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="182.5" y1="798.1145" y2="808.1145"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="182.5" x2="348" y1="808.1145" y2="808.1145"/>
+ <polygon fill="#181818" points="344,620.2704,348,610.2704,352,620.2704,348,616.2704" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="348" x2="348" y1="420.4444" y2="808.1145"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="348" x2="248" y1="420.4444" y2="420.4444"/>
+ <polygon fill="#181818" points="258,416.4444,248,420.4444,258,424.4444,254,420.4444" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="117" x2="26" y1="420.4444" y2="420.4444"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="26" x2="26" y1="420.4444" y2="452.4444"/>
+ <polygon fill="#181818" points="22,442.4444,26,452.4444,30,442.4444,26,446.4444" style="stroke:#181818;stroke-width:1.0;"/>
+ <!--SRC=[dP2nJiGm38RtFCK_jWCda1WG9o4nmu0GBn0QvnhH9Y5nXVhsE6tJa-06H2OhjZz_zflZWV0O8tc3H9yJPIQ34Da5dw67KqYE-n7D4sdS7EuErwrIzoTYTC5RTvhINex1PJvksQYJ7eafLUgIHVFo-jp2fZ12VjCivL-z4uN6XWJING_7DAuLboUq2hWiaE4yJD_5M-IeEpfAvKzwf9G5Gfwtfe8JO9noZtVTVTHHNJ043X3Xmf7xB2Yr1x_4bmunA2jAXxDeDfRBy86HHLhoYN8KMMh1FNaqC-LLuZ-NMEJ_V8toYfSDapTrldNfp1AQDliKsR_QrdOQiZYMTHO8H5OFYRIPruyAaZivgxPelTYvkLMrO4zUFPZWPKhc4DKt]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/appendRequestedOptions.uml b/doc/sphinx/uml/appendRequestedOptions.uml
new file mode 100644
index 0000000..80a21d9
--- /dev/null
+++ b/doc/sphinx/uml/appendRequestedOptions.uml
@@ -0,0 +1,31 @@
+@startuml
+
+Title Append requested options algorithm (Kea 1.8.0)
+
+:get configured option list;
+
+:get parameter request list (PRL) from query;
+
+while (for each item from configured option list)
+ :get configured options in dhcp4 space;
+ while (for each persistent option)
+ :push back option code to PRL;
+ endwhile
+endwhile
+
+while (for each code in PRL)
+ if (option is not set in response) then (yes)
+ while (for each item from configured option list)
+ :get configured options in dhcp4 space;
+ if (found) then (first)
+ :add option to response;
+ else (not found or already found)
+ endif
+ endwhile
+ else (no)
+ endif
+endwhile
+->done;
+stop
+
+@enduml
diff --git a/doc/sphinx/uml/appendRequestedVendorOptions.png b/doc/sphinx/uml/appendRequestedVendorOptions.png
new file mode 100644
index 0000000..c00fcce
--- /dev/null
+++ b/doc/sphinx/uml/appendRequestedVendorOptions.png
Binary files differ
diff --git a/doc/sphinx/uml/appendRequestedVendorOptions.svg b/doc/sphinx/uml/appendRequestedVendorOptions.svg
new file mode 100644
index 0000000..94685f9
--- /dev/null
+++ b/doc/sphinx/uml/appendRequestedVendorOptions.svg
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="1642px" preserveAspectRatio="none" style="width:429px;height:1642px;background:#FFFFFF;" version="1.1" viewBox="0 0 429 1642" width="429px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="386" x="20" y="34.9659">Append vendor requested options algorithm (Kea 1.8.0)</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="227" x="105.5" y="51.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="207" x="115.5" y="73.896">get vendor id from query vivso option</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="247" x="95.5" y="107.412"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="227" x="105.5" y="130.2401">get vendor id from response vivso option</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="55" x="191.5" y="213.2471"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="35" x="201.5" y="236.0752">return</text>
+ <ellipse cx="219" cy="287.9191" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="219" cy="287.9191" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="193.5,163.7561,244.5,163.7561,256.5,175.7561,244.5,187.7561,193.5,187.7561,181.5,175.7561,193.5,163.7561" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="223" y="199.5151">no</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="51" x="193.5" y="180.0241">vendor id</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="256.5" y="172.5332">yes</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="158" x="140" y="340.9191"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="138" x="150" y="363.7472">get configured option list</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="369" x="34.5" y="397.2633"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="349" x="44.5" y="420.0914">get option request list (ORO) from query DOCSIS vendor option</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="249" x="94.5" y="497.6074"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="229" x="104.5" y="520.4355">get configured options in vendor id space</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="186" x="126" y="597.9515"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="166" x="136" y="620.7796">push back option code to ORO</text>
+ <polygon fill="#F1F1F1" points="151,553.9515,287,553.9515,299,565.9515,287,577.9515,151,577.9515,139,565.9515,151,553.9515" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="136" x="151" y="570.2195">for each persistent option</text>
+ <polygon fill="#F1F1F1" points="111.5,453.6074,326.5,453.6074,338.5,465.6074,326.5,477.6074,111.5,477.6074,99.5,465.6074,111.5,453.6074" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="215" x="111.5" y="469.8753">for each item from configured option list</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="199" x="119.5" y="747.7865"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="179" x="129.5" y="770.6146">create vivso option for vendor id</text>
+ <polygon fill="#F1F1F1" points="161,698.2956,277,698.2956,289,710.2956,277,722.2956,161,722.2956,149,710.2956,161,698.2956" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="223" y="734.0545">no</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="116" x="161" y="714.5636">response vivso option</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="289" y="707.0726">yes</text>
+ <polygon fill="#F1F1F1" points="219,804.1307,231,816.1307,219,828.1307,207,816.1307,219,804.1307" style="stroke:#181818;stroke-width:0.5;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="95" x="171.5" y="848.1307"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="75" x="181.5" y="870.9588">added = false</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="249" x="94.5" y="1041.9657"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="229" x="104.5" y="1064.7938">get configured options in vendor id space</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="186" x="126" y="1147.8008"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="166" x="136" y="1170.6289">add sub-option to vivso option</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="91" x="173.5" y="1219.1449"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="71" x="183.5" y="1241.973">added = true</text>
+ <polygon fill="#F1F1F1" points="203,1098.3098,235,1098.3098,247,1110.3098,235,1122.3098,203,1122.3098,191,1110.3098,203,1098.3098" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="21" x="223" y="1134.0687">first</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="32" x="203" y="1114.5778">found</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="145" x="247" y="1107.0869">not found or already found</text>
+ <polygon fill="#F1F1F1" points="219,1275.489,231,1287.489,219,1299.489,207,1287.489,219,1275.489" style="stroke:#181818;stroke-width:0.5;"/>
+ <polygon fill="#F1F1F1" points="111.5,997.9657,326.5,997.9657,338.5,1009.9657,326.5,1021.9657,111.5,1021.9657,99.5,1009.9657,111.5,997.9657" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="215" x="111.5" y="1014.2337">for each item from configured option list</text>
+ <polygon fill="#F1F1F1" points="124.5,948.4748,313.5,948.4748,325.5,960.4748,313.5,972.4748,124.5,972.4748,112.5,960.4748,124.5,948.4748" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="223" y="984.2337">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="189" x="124.5" y="964.7427">sub-option is not set in vivso option</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="325.5" y="957.2518">no</text>
+ <polygon fill="#F1F1F1" points="219,1341.489,231,1353.489,219,1365.489,207,1353.489,219,1341.489" style="stroke:#181818;stroke-width:0.5;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="178" x="130" y="1484.4709"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="158" x="140" y="1507.299">add vivso option in response</text>
+ <polygon fill="#F1F1F1" points="154.5,1434.98,283.5,1434.98,295.5,1446.98,283.5,1458.98,154.5,1458.98,142.5,1446.98,154.5,1434.98" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="223" y="1470.7389">no</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="129" x="154.5" y="1451.2479">vivso option in response</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="295.5" y="1443.757">yes</text>
+ <polygon fill="#F1F1F1" points="219,1540.815,231,1552.815,219,1564.815,207,1552.815,219,1540.815" style="stroke:#181818;stroke-width:0.5;"/>
+ <polygon fill="#F1F1F1" points="202.5,1385.489,235.5,1385.489,247.5,1397.489,235.5,1409.489,202.5,1409.489,190.5,1397.489,202.5,1385.489" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="223" y="1421.2479">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="33" x="202.5" y="1401.757">added</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="247.5" y="1394.266">no</text>
+ <polygon fill="#F1F1F1" points="219,1584.815,231,1596.815,219,1608.815,207,1596.815,219,1584.815" style="stroke:#181818;stroke-width:0.5;"/>
+ <polygon fill="#F1F1F1" points="163,904.4748,275,904.4748,287,916.4748,275,928.4748,163,928.4748,151,916.4748,163,904.4748" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="112" x="163" y="920.7427">for each code in ORO</text>
+ <ellipse cx="37.5" cy="959.4748" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="37.5" cy="959.4748" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="87.412" y2="107.412"/>
+ <polygon fill="#181818" points="215,97.412,219,107.412,223,97.412,219,101.412" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="249.5912" y2="276.9191"/>
+ <polygon fill="#181818" points="215,266.9191,219,276.9191,223,266.9191,219,270.9191" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="187.7561" y2="213.2471"/>
+ <polygon fill="#181818" points="215,203.2471,219,213.2471,223,203.2471,219,207.2471" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="256.5" x2="268.5" y1="175.7561" y2="175.7561"/>
+ <polygon fill="#181818" points="264.5,246.9191,268.5,256.9191,272.5,246.9191,268.5,250.9191" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="268.5" x2="268.5" y1="175.7561" y2="320.9191"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="268.5" x2="219" y1="320.9191" y2="320.9191"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="320.9191" y2="340.9191"/>
+ <polygon fill="#181818" points="215,330.9191,219,340.9191,223,330.9191,219,334.9191" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="143.7561" y2="163.7561"/>
+ <polygon fill="#181818" points="215,153.7561,219,163.7561,223,153.7561,219,157.7561" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="377.2633" y2="397.2633"/>
+ <polygon fill="#181818" points="215,387.2633,219,397.2633,223,387.2633,219,391.2633" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="577.9515" y2="597.9515"/>
+ <polygon fill="#181818" points="215,587.9515,219,597.9515,223,587.9515,219,591.9515" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="634.2956" y2="644.2956"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="324" y1="644.2956" y2="644.2956"/>
+ <polygon fill="#181818" points="320,614.1235,324,604.1235,328,614.1235,324,610.1235" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="324" x2="324" y1="565.9515" y2="644.2956"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="324" x2="299" y1="565.9515" y2="565.9515"/>
+ <polygon fill="#181818" points="309,561.9515,299,565.9515,309,569.9515,305,565.9515" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="139" x2="114" y1="565.9515" y2="565.9515"/>
+ <polygon fill="#181818" points="110,600.1235,114,610.1235,118,600.1235,114,604.1235" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="114" x2="114" y1="565.9515" y2="656.2956"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="114" x2="355.5" y1="656.2956" y2="656.2956"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="355.5" x2="355.5" y1="465.6074" y2="656.2956"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="355.5" x2="338.5" y1="465.6074" y2="465.6074"/>
+ <polygon fill="#181818" points="348.5,461.6074,338.5,465.6074,348.5,469.6074,344.5,465.6074" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="533.9515" y2="553.9515"/>
+ <polygon fill="#181818" points="215,543.9515,219,553.9515,223,543.9515,219,547.9515" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="477.6074" y2="497.6074"/>
+ <polygon fill="#181818" points="215,487.6074,219,497.6074,223,487.6074,219,491.6074" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="99.5" x2="82.5" y1="465.6074" y2="465.6074"/>
+ <polygon fill="#181818" points="78.5,556.4515,82.5,566.4515,86.5,556.4515,82.5,560.4515" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="82.5" x2="82.5" y1="465.6074" y2="678.2956"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="82.5" x2="219" y1="678.2956" y2="678.2956"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="678.2956" y2="698.2956"/>
+ <polygon fill="#181818" points="215,688.2956,219,698.2956,223,688.2956,219,692.2956" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="433.6074" y2="453.6074"/>
+ <polygon fill="#181818" points="215,443.6074,219,453.6074,223,443.6074,219,447.6074" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="722.2956" y2="747.7865"/>
+ <polygon fill="#181818" points="215,737.7865,219,747.7865,223,737.7865,219,741.7865" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="289" x2="328.5" y1="710.2956" y2="710.2956"/>
+ <polygon fill="#181818" points="324.5,755.9586,328.5,765.9586,332.5,755.9586,328.5,759.9586" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="328.5" x2="328.5" y1="710.2956" y2="816.1307"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="328.5" x2="231" y1="816.1307" y2="816.1307"/>
+ <polygon fill="#181818" points="241,812.1307,231,816.1307,241,820.1307,237,816.1307" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="784.1307" y2="804.1307"/>
+ <polygon fill="#181818" points="215,794.1307,219,804.1307,223,794.1307,219,798.1307" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="828.1307" y2="848.1307"/>
+ <polygon fill="#181818" points="215,838.1307,219,848.1307,223,838.1307,219,842.1307" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="1184.1449" y2="1219.1449"/>
+ <polygon fill="#181818" points="215,1209.1449,219,1219.1449,223,1209.1449,219,1213.1449" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="1122.3098" y2="1147.8008"/>
+ <polygon fill="#181818" points="215,1137.8008,219,1147.8008,223,1137.8008,219,1141.8008" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="247" x2="322" y1="1110.3098" y2="1110.3098"/>
+ <polygon fill="#181818" points="318,1191.6449,322,1201.6449,326,1191.6449,322,1195.6449" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="322" x2="322" y1="1110.3098" y2="1287.489"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="322" x2="231" y1="1287.489" y2="1287.489"/>
+ <polygon fill="#181818" points="241,1283.489,231,1287.489,241,1291.489,237,1287.489" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="1255.489" y2="1275.489"/>
+ <polygon fill="#181818" points="215,1265.489,219,1275.489,223,1265.489,219,1269.489" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="1078.3098" y2="1098.3098"/>
+ <polygon fill="#181818" points="215,1088.3098,219,1098.3098,223,1088.3098,219,1092.3098" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="1021.9657" y2="1041.9657"/>
+ <polygon fill="#181818" points="215,1031.9657,219,1041.9657,223,1031.9657,219,1035.9657" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="1299.489" y2="1309.489"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="355.5" y1="1309.489" y2="1309.489"/>
+ <polygon fill="#181818" points="351.5,1163.9728,355.5,1153.9728,359.5,1163.9728,355.5,1159.9728" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="355.5" x2="355.5" y1="1009.9657" y2="1309.489"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="355.5" x2="338.5" y1="1009.9657" y2="1009.9657"/>
+ <polygon fill="#181818" points="348.5,1005.9657,338.5,1009.9657,348.5,1013.9657,344.5,1009.9657" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="99.5" x2="82.5" y1="1009.9657" y2="1009.9657"/>
+ <polygon fill="#181818" points="78.5,1149.9728,82.5,1159.9728,86.5,1149.9728,82.5,1153.9728" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="82.5" x2="82.5" y1="1009.9657" y2="1321.489"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="82.5" x2="219" y1="1321.489" y2="1321.489"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="1321.489" y2="1341.489"/>
+ <polygon fill="#181818" points="215,1331.489,219,1341.489,223,1331.489,219,1335.489" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="972.4748" y2="997.9657"/>
+ <polygon fill="#181818" points="215,987.9657,219,997.9657,223,987.9657,219,991.9657" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="325.5" x2="365.5" y1="960.4748" y2="960.4748"/>
+ <polygon fill="#181818" points="361.5,1143.9728,365.5,1153.9728,369.5,1143.9728,365.5,1147.9728" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="365.5" x2="365.5" y1="960.4748" y2="1353.489"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="365.5" x2="231" y1="1353.489" y2="1353.489"/>
+ <polygon fill="#181818" points="241,1349.489,231,1353.489,241,1357.489,237,1353.489" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="1458.98" y2="1484.4709"/>
+ <polygon fill="#181818" points="215,1474.4709,219,1484.4709,223,1474.4709,219,1478.4709" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="295.5" x2="318" y1="1446.98" y2="1446.98"/>
+ <polygon fill="#181818" points="314,1492.643,318,1502.643,322,1492.643,318,1496.643" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="318" x2="318" y1="1446.98" y2="1552.815"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="318" x2="231" y1="1552.815" y2="1552.815"/>
+ <polygon fill="#181818" points="241,1548.815,231,1552.815,241,1556.815,237,1552.815" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="1520.815" y2="1540.815"/>
+ <polygon fill="#181818" points="215,1530.815,219,1540.815,223,1530.815,219,1534.815" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="1409.489" y2="1434.98"/>
+ <polygon fill="#181818" points="215,1424.98,219,1434.98,223,1424.98,219,1428.98" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="247.5" x2="340" y1="1397.489" y2="1397.489"/>
+ <polygon fill="#181818" points="336,1492.643,340,1502.643,344,1492.643,340,1496.643" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="340" x2="340" y1="1397.489" y2="1596.815"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="340" x2="231" y1="1596.815" y2="1596.815"/>
+ <polygon fill="#181818" points="241,1592.815,231,1596.815,241,1600.815,237,1596.815" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="1564.815" y2="1584.815"/>
+ <polygon fill="#181818" points="215,1574.815,219,1584.815,223,1574.815,219,1578.815" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="1365.489" y2="1385.489"/>
+ <polygon fill="#181818" points="215,1375.489,219,1385.489,223,1375.489,219,1379.489" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="928.4748" y2="948.4748"/>
+ <polygon fill="#181818" points="215,938.4748,219,948.4748,223,938.4748,219,942.4748" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="1608.815" y2="1618.815"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="389.5" y1="1618.815" y2="1618.815"/>
+ <polygon fill="#181818" points="385.5,1281.1358,389.5,1271.1358,393.5,1281.1358,389.5,1277.1358" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="389.5" x2="389.5" y1="916.4748" y2="1618.815"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="389.5" x2="287" y1="916.4748" y2="916.4748"/>
+ <polygon fill="#181818" points="297,912.4748,287,916.4748,297,920.4748,293,916.4748" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="151" x2="37.5" y1="916.4748" y2="916.4748"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="37.5" x2="37.5" y1="916.4748" y2="948.4748"/>
+ <polygon fill="#181818" points="33.5,938.4748,37.5,948.4748,41.5,938.4748,37.5,942.4748" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="219" x2="219" y1="884.4748" y2="904.4748"/>
+ <polygon fill="#181818" points="215,894.4748,219,904.4748,223,894.4748,219,898.4748" style="stroke:#181818;stroke-width:1.0;"/>
+ <!--SRC=[dPEnJiCm48PtFyMDoT0ADjG890GBOgX4UG4tFZTMKzlOJb7VdhEJk6aR1kXGHVRTVz__N_AfGelmR2rg0KVwCmuSVdNe0memDYYZFV1sPvmApG6AT-HmltnOtfMChNOOnYObG3fp06fr9pYgepT3UpLRvz1RaiQhKYMXo9KbX0Or5DgK363bC7IEQW1yC9PXIzt52Nt9g479WQSsMgfTvp8-jCg7QhWTZWQBwGgAzSUwdDA_hbysRvkHU4HZtusYh0f9PyZh1bJ0GzysFpD1po9vK7eIYBUynkZhUe95vqa8zGWU9KdKThw1BQ_tuxZQ28HWW9n47L9EKYm_f61dCxyAkNR8msK5H9vCMyq5puKWSuyWETrLDqabFB8SWwOXaSLtsyKWhpne4y1JKZ6M6ROqAph_pmfIOdzQG_n5HcawdLz2gTnPZnJ9CanCKFYNR_AqC6KJN9VL-mYZwpG3282tbBmuzGVZd3xVuQdVvBcvPEU2Y9i6tKIMlgZfDfNE7z_bxhEjtyfx-idwfuYtL1bvyIICfkxqsVu0]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/appendRequestedVendorOptions.uml b/doc/sphinx/uml/appendRequestedVendorOptions.uml
new file mode 100644
index 0000000..65d3d54
--- /dev/null
+++ b/doc/sphinx/uml/appendRequestedVendorOptions.uml
@@ -0,0 +1,53 @@
+@startuml
+
+Title Append vendor requested options algorithm (Kea 1.8.0)
+
+:get vendor id from query vivso option;
+:get vendor id from response vivso option;
+if (vendor id) then (no)
+ :return;
+ stop
+else (yes)
+endif
+
+:get configured option list;
+
+:get option request list (ORO) from query DOCSIS vendor option;
+
+while (for each item from configured option list)
+ :get configured options in vendor id space;
+ while (for each persistent option)
+ :push back option code to ORO;
+ endwhile
+endwhile
+
+if (response vivso option) then (no)
+ :create vivso option for vendor id;
+else (yes)
+endif
+
+:added = false;
+while (for each code in ORO)
+ if (sub-option is not set in vivso option) then (yes)
+ while (for each item from configured option list)
+ :get configured options in vendor id space;
+ if (found) then (first)
+ :add sub-option to vivso option;
+ :added = true;
+ else (not found or already found)
+ endif
+ endwhile
+ else (no)
+ endif
+ if (added) then (yes)
+ if (vivso option in response) then (no)
+ :add vivso option in response;
+ else (yes)
+ endif
+ else (no)
+ endif
+endwhile
+->done;
+stop
+
+@enduml
diff --git a/doc/sphinx/uml/assign-lease4.png b/doc/sphinx/uml/assign-lease4.png
new file mode 100644
index 0000000..29e099b
--- /dev/null
+++ b/doc/sphinx/uml/assign-lease4.png
Binary files differ
diff --git a/doc/sphinx/uml/assign-lease4.svg b/doc/sphinx/uml/assign-lease4.svg
new file mode 100644
index 0000000..12f09a2
--- /dev/null
+++ b/doc/sphinx/uml/assign-lease4.svg
@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="1433px" preserveAspectRatio="none" style="width:1340px;height:1433px;background:#FFFFFF;" version="1.1" viewBox="0 0 1340 1433" width="1340px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="223" x="551.5" y="24.9659">DHCPv4 Assign Lease (Kea 1.8.0)</text>
+ <!--cluster init_reboot-->
+ <g id="cluster_init_reboot">
+ <rect fill="none" height="391.06" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="442" x="487" y="477.3379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="129" x="643.5" y="494.3039">INIT-REBOOT state</text>
+ </g>
+ <!--cluster allocated-->
+ <g id="cluster_allocated">
+ <rect fill="none" height="274.03" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="160" x="7" y="1153.5379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="106" x="34" y="1170.5039">Lease allocated</text>
+ </g>
+ <!--entity by_client_id-->
+ <g id="elem_by_client_id">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="217" x="511.5" y="587.1979"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="197" x="521.5" y="612.1639">Get existing lease by client id</text>
+ </g>
+ <!--entity by_hw_addr-->
+ <g id="elem_by_hw_addr">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="286" x="619" y="705.2679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="266" x="629" y="730.2339">Get existing lease by hardware address</text>
+ </g>
+ <!--entity authoritative-->
+ <g id="elem_authoritative">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="133" x="519.5" y="805.3379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="113" x="529.5" y="830.3039">Get authoritative</text>
+ </g>
+ <!--entity ddns-->
+ <g id="elem_ddns">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="111" x="31.5" y="1264.4279"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="91" x="41.5" y="1289.3939">update DDNS</text>
+ </g>
+ <!--entity ack-->
+ <g id="elem_ack">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="85" x="44.5" y="1364.4979"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="65" x="54.5" y="1389.4639">Send ACK</text>
+ </g>
+ <!--entity subnet-->
+ <g id="elem_subnet">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="112" x="1177" y="47.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="92" x="1187" y="72.0339">Check Subnet</text>
+ </g>
+ <g id="elem_GMN3">
+ <path d="M1054,52.7479 L1054,80.4539 A0,0 0 0 0 1054,80.4539 L1142,80.4539 A0,0 0 0 0 1142,80.4539 L1142,70.7479 L1176.66,66.5979 L1142,62.7479 L1142,62.7479 L1132,52.7479 L1054,52.7479 A0,0 0 0 0 1054,52.7479 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M1132,52.7479 L1132,62.7479 L1142,62.7479 L1132,52.7479 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="67" x="1060" y="71.6449">entry point</text>
+ </g>
+ <!--entity server_id-->
+ <g id="elem_server_id">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="106" x="1020" y="147.1379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="86" x="1030" y="172.1039">Get server id</text>
+ </g>
+ <!--entity hint-->
+ <g id="elem_hint">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="74" x="832" y="247.1979"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="54" x="842" y="272.1639">Get hint</text>
+ </g>
+ <!--entity ident-->
+ <g id="elem_ident">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="258" x="556" y="365.2679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="238" x="566" y="390.2339">Get hardware address and client id</text>
+ </g>
+ <!--entity hostname-->
+ <g id="elem_hostname">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="142" x="277" y="941.3979"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="122" x="287" y="966.3639">Process hostname</text>
+ </g>
+ <!--entity allocate-->
+ <g id="elem_allocate">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="114" x="205" y="1041.4679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="94" x="215" y="1066.4339">Request lease</text>
+ </g>
+ <!--entity failed-->
+ <g id="elem_failed">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="143" x="190.5" y="1164.3679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="123" x="200.5" y="1189.3339">No lease allocated</text>
+ </g>
+ <g id="elem_GMN19">
+ <path d="M368,1170.0479 L368,1179.8979 L333.93,1183.8979 L368,1187.8979 L368,1197.7539 A0,0 0 0 0 368,1197.7539 L446,1197.7539 A0,0 0 0 0 446,1197.7539 L446,1180.0479 L436,1170.0479 L368,1170.0479 A0,0 0 0 0 368,1170.0479 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M436,1170.0479 L436,1180.0479 L446,1180.0479 L436,1170.0479 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="57" x="374" y="1188.9449">exit point</text>
+ </g>
+ <!--entity nak-->
+ <g id="elem_nak">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="87" x="591.5" y="1264.4279"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="67" x="601.5" y="1289.3939">Send NAK</text>
+ </g>
+ <g id="elem_GMN23">
+ <path d="M713,1270.1179 L713,1279.9679 L678.88,1283.9679 L713,1287.9679 L713,1297.8239 A0,0 0 0 0 713,1297.8239 L791,1297.8239 A0,0 0 0 0 791,1297.8239 L791,1280.1179 L781,1270.1179 L713,1270.1179 A0,0 0 0 0 713,1270.1179 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M781,1270.1179 L781,1280.1179 L791,1280.1179 L781,1270.1179 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="57" x="719" y="1289.0149">exit point</text>
+ </g>
+ <!--entity no_response-->
+ <g id="elem_no_response">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="105" x="999.5" y="1041.4679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="85" x="1009.5" y="1066.4339">No response</text>
+ </g>
+ <g id="elem_GMN27">
+ <path d="M1139,1047.1479 L1139,1057.0079 L1104.66,1061.0079 L1139,1065.0079 L1139,1074.8539 A0,0 0 0 0 1139,1074.8539 L1217,1074.8539 A0,0 0 0 0 1217,1074.8539 L1217,1057.1479 L1207,1047.1479 L1139,1047.1479 A0,0 0 0 0 1139,1047.1479 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M1207,1047.1479 L1207,1057.1479 L1217,1057.1479 L1207,1047.1479 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="57" x="1145" y="1066.0449">exit point</text>
+ </g>
+ <!--link subnet to server_id-->
+ <g id="link_subnet_server_id">
+ <path d="M1202.15,86.5079 C1175.67,102.7379 1137.47,126.1579 1109.48,143.3079 " fill="none" id="subnet-to-server_id" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1105.32,145.8579,1115.0825,144.5568,1109.5807,143.2414,1110.8961,137.7396,1105.32,145.8579" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link subnet to nak-->
+ <g id="link_subnet_nak">
+ <path d="M1244.71,86.3479 C1255.55,105.5279 1270,136.5579 1270,165.6679 C1270,165.6679 1270,165.6679 1270,1184.8979 C1270,1249.0679 757.06,1228.6979 696,1248.4279 C686.6,1251.4679 677.03,1256.0479 668.34,1260.8979 " fill="none" id="subnet-to-nak" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="664.37,1263.1779,674.1655,1262.1545,668.7033,1260.6834,670.1743,1255.2212,664.37,1263.1779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="61" x="1271" y="671.1649">no subnet</text>
+ </g>
+ <!--link server_id to hint-->
+ <g id="link_server_id_hint">
+ <path d="M1033.67,186.5779 C998.83,203.3279 948.06,227.7279 912.13,245.0079 " fill="none" id="server_id-to-hint" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="907.86,247.0579,917.704,246.7491,912.3631,244.885,914.2273,239.5441,907.86,247.0579" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link hint to ident-->
+ <g id="link_hint_ident">
+ <path d="M831.51,268.0879 C759.25,269.6479 604.9,277.5579 572,316.2679 C555.28,335.9479 575.32,351.4079 602.93,362.5379 " fill="none" id="hint-to-ident" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="607.52,364.3079,600.5673,357.3323,602.8562,362.5054,597.6832,364.7943,607.52,364.3079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="178" x="573" y="331.1649">use requested address option</text>
+ </g>
+ <!--link hint to ident-->
+ <g id="link_hint_ident">
+ <path d="M839.01,286.6279 C824.77,295.6079 807.5,306.4879 792,316.2679 C768.12,331.3279 741.25,348.2879 720.41,361.4479 " fill="none" id="hint-to-ident-1" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="716.26,364.0679,726.0052,362.6427,720.4871,361.3974,721.7324,355.8793,716.26,364.0679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="106" x="793" y="331.1649">use client address</text>
+ </g>
+ <!--link hint to ident-->
+ <g id="link_hint_ident">
+ <path d="M890.07,286.6879 C903.34,300.9279 915.9,320.4379 904,335.2679 C892.23,349.9379 858.17,360.4479 820.31,367.8479 " fill="none" id="hint-to-ident-2" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="815.69,368.7279,825.2762,370.9874,820.6031,367.7996,823.7908,363.1265,815.69,368.7279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="43" x="909" y="331.1649">no hint</text>
+ </g>
+ <!--link ident to init_reboot-->
+ <g id="link_ident_init_reboot">
+ <path d="M685,404.6979 C685,421.4329 685,446.4029 685,467.4479 C685,470.0785 685,472.6478 685,475.1321 C685,475.7531 685,476.3688 685,476.9789 " fill="none" id="ident-to-init_reboot" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="685,476.9789,689,467.9789,685,471.9789,681,467.9789,685,476.9789" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="210" x="686" y="449.2349">requested address and no server id</text>
+ </g>
+ <!--link ident to hostname-->
+ <g id="link_ident_hostname">
+ <path d="M569.5,404.8179 C471.94,424.2579 348,458.4479 348,506.6979 C348,506.6979 348,506.6979 348,825.8679 C348,863.8479 348,907.8779 348,934.8179 " fill="none" id="ident-to-hostname" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="348,939.7679,352,930.7679,348,934.7679,344,930.7679,348,939.7679" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link init_reboot to by_client_id-->
+ <g id="link_init_reboot_by_client_id">
+ <path d="M684.81,508.4779 C682.27,512.2879 654.33,554.2279 635.97,581.7679 " fill="none" id="init_reboot-to-by_client_id" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="633.28,585.8079,641.5922,580.5251,636.0469,581.6433,634.9287,576.098,633.28,585.8079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="80" x="664" y="553.0949">has a client id</text>
+ </g>
+ <!--link init_reboot to by_hw_addr-->
+ <g id="link_init_reboot_by_hw_addr">
+ <path d="M685.56,508.2379 C691.04,508.6379 735.47,512.6679 753,538.1979 C786.32,586.7179 776.72,660.3479 768.45,698.6579 " fill="none" id="init_reboot-to-by_hw_addr" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="767.37,703.4579,773.2491,695.5563,768.4682,698.58,765.4445,693.7991,767.37,703.4579" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="65" x="777" y="612.1249">no client id</text>
+ </g>
+ <!--link by_client_id to authoritative-->
+ <g id="link_by_client_id_authoritative">
+ <path d="M605.16,626.5679 C591.42,645.2879 571.95,675.4579 564,705.2679 C555.44,737.3479 565.93,775.1879 575.15,799.3479 " fill="none" id="by_client_id-to-authoritative" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="576.89,803.7779,577.2979,793.9375,575.0503,799.1287,569.8591,796.8811,576.89,803.7779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="36" x="565" y="730.1949">found</text>
+ </g>
+ <!--link by_client_id to by_hw_addr-->
+ <g id="link_by_client_id_by_hw_addr">
+ <path d="M615.57,626.5079 C613.24,641.2079 612.7,661.4279 623,675.2679 C631.4,686.5579 642.62,695.2379 655.06,701.9079 " fill="none" id="by_client_id-to-by_hw_addr" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="659.5,704.1479,653.2585,696.5293,655.0336,701.9004,649.6625,703.6755,659.5,704.1479" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="60" x="624" y="671.1649">not found</text>
+ </g>
+ <!--link by_hw_addr to authoritative-->
+ <g id="link_by_hw_addr_authoritative">
+ <path d="M728.07,744.7079 C698.82,761.0079 656.57,784.5479 625.75,801.7179 " fill="none" id="by_hw_addr-to-authoritative" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="621.45,804.1179,631.26,803.2438,625.8208,801.6897,627.3748,796.2505,621.45,804.1179" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link authoritative to no_response-->
+ <g id="link_authoritative_no_response">
+ <path d="M652.89,841.8779 C695.29,853.2479 750.68,870.3979 797,892.3979 C884.78,934.0979 977.68,1002.0879 1023.3,1037.2879 " fill="none" id="authoritative-to-no_response" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1027.17,1040.2779,1022.5281,1031.5915,1023.2255,1037.2052,1017.6119,1037.9027,1027.17,1040.2779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="224" x="944" y="966.3349">not authoritative and no owned lease</text>
+ </g>
+ <!--link authoritative to nak-->
+ <g id="link_authoritative_nak">
+ <path d="M652.79,844.4179 C705.78,863.6879 771,899.6479 771,959.9379 C771,959.9379 771,959.9379 771,1184.8979 C771,1197.1579 712.12,1235.7379 671.85,1260.6979 " fill="none" id="authoritative-to-nak" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="667.83,1263.1779,677.5925,1261.8768,672.0907,1260.5614,673.4061,1255.0596,667.83,1263.1779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="192" x="772" y="1066.4049">owned lease with hint mismatch</text>
+ </g>
+ <!--link authoritative to nak-->
+ <g id="link_authoritative_nak">
+ <path d="M563.6,844.6979 C537.82,868.6079 499,912.4879 499,959.9379 C499,959.9379 499,959.9379 499,1184.8979 C499,1229.0079 546.66,1255.2679 585.55,1269.3379 " fill="none" id="authoritative-to-nak-1" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="589.79,1270.8279,582.6258,1264.0697,585.0729,1269.1698,579.9728,1271.617,589.79,1270.8279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="200" x="500" y="1066.4049">authoritative and no owned lease</text>
+ </g>
+ <!--link authoritative to hostname-->
+ <g id="link_authoritative_hostname">
+ <path d="M519.21,840.8179 C482.59,851.2179 437.72,867.7179 403,892.3979 C386.76,903.9479 372.66,921.5079 362.8,935.8279 " fill="none" id="authoritative-to-hostname" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="360.21,939.6879,368.5513,934.4513,363,935.5387,361.9126,929.9873,360.21,939.6879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="68" x="404" y="907.2949">other cases</text>
+ </g>
+ <!--link hostname to allocate-->
+ <g id="link_hostname_allocate">
+ <path d="M331.42,980.8479 C317.5,996.7179 297.55,1019.4679 282.6,1036.5079 " fill="none" id="hostname-to-allocate" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="279.53,1040.0179,288.4746,1035.8956,282.8296,1036.2612,282.4639,1030.6162,279.53,1040.0179" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link allocate to allocated-->
+ <g id="link_allocate_allocated">
+ <path d="M214.81,1080.8979 C198.19,1088.7379 179.94,1098.7679 165,1110.5379 C156.9975,1116.8404 149.2931,1124.3342 142.2119,1132.0726 C138.6713,1135.9418 135.2864,1139.8722 132.098,1143.7455 C130.5038,1145.6821 128.9586,1147.6044 127.4677,1149.4977 C126.7222,1150.4443 125.9903,1151.3837 125.2726,1152.3139 C125.0931,1152.5465 124.9146,1152.7784 124.7369,1153.0098 C124.6481,1153.1255 124.5595,1153.2411 124.4711,1153.3565 " fill="none" id="allocate-to-allocated" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="124.4711,1153.3565,133.1191,1148.6435,127.5113,1149.3869,126.7678,1143.7792,124.4711,1153.3565" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="87" x="166" y="1125.4349">lease allocated</text>
+ </g>
+ <!--link allocated to ddns-->
+ <g id="link_allocated_ddns">
+ <path d="M103.99,1184.9679 C103.65,1186.9379 96.26,1229.5779 91.34,1257.9479 " fill="none" id="allocated-to-ddns" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="90.52,1262.6279,96.0029,1254.4464,91.3767,1257.7019,88.1212,1253.0756,90.52,1262.6279" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link ddns to ack-->
+ <g id="link_ddns_ack">
+ <path d="M87,1303.8779 C87,1319.2079 87,1340.9479 87,1357.7679 " fill="none" id="ddns-to-ack" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="87,1362.6679,91,1353.6679,87,1357.6679,83,1353.6679,87,1362.6679" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link allocate to failed-->
+ <g id="link_allocate_failed">
+ <path d="M262,1080.8979 C262,1101.5679 262,1134.8079 262,1157.6879 " fill="none" id="allocate-to-failed" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="262,1162.6279,266,1153.6279,262,1157.6279,258,1153.6279,262,1162.6279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="106" x="263" y="1125.4349">no lease allocated</text>
+ </g>
+ <!--link failed to nak-->
+ <g id="link_failed_nak">
+ <path d="M333.92,1203.8079 C407.65,1223.1879 520.38,1252.8279 584.94,1269.8079 " fill="none" id="failed-to-nak" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="589.57,1271.0179,581.8807,1264.8636,584.7339,1269.7483,579.8493,1272.6014,589.57,1271.0179" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--SRC=[dLF1Rjim3BtxAuYSjaEA3jXXw650bWnRqI4jcju3nM8Y8OwKMNJIODY_Zv8intBI3TZ5iAZt7YdoaJHL2BFlqwV3HvWufpS6Vg1m2EyUKC27cqytj- -BGcpG48oc2iijB9gLGHg1SE32Rs4i8LIu9hW31jOds5jjgEDzGmA7zG5hq38ImscfPOPHJ0hNQiXMefP7KIC8AMjq3eIHK5RQ0rf9BT6JQYn9c0s_QdG_ltyPFt_zylZu0eu4OSGPJSiQLzOI_2gWbmHVjIDjDlmKtu7LQP1XTLh6GAZxxynXmKb07PS-ajD5GyhMcalKXrXa5Yb-Txrugctfdw-i8oDs4PmE7UePVpReA5OJzQhAbjo1h3znqEbAPa3Pwqspbno1sMo-21WfZUik5yYJc4mV8hlSzegTsxOVUOgrq1NAQ9fQRrHmpQkcWMc2y7mIXOtO_fF0wRZJUslQDnkxJEUtkT71C1v_xdco2tAKCpF5sEJr3XKumQZ-4qx1Wenk-Crrd04tCbdMxabRSntT6gs5Nc8uVqWLp-6cP-ExAzdyWlYgqzfrL5zrCanVn5_q7Swgoe_ql0JNuAsVOubdP2OKScQEPlZQDkO2To59PrmANuWLkNISsddshKuEQhjZZuPR5anweP96tyF0KPCAWu2TTZj1fNgJzX_vqbXyKb8yjP9nR8OK3YzFYvn-igWVsc3bYlETHuRTzP_8uttDXE9kncaEXTgxzed57m00]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/assign-lease4.uml b/doc/sphinx/uml/assign-lease4.uml
new file mode 100644
index 0000000..de19ed7
--- /dev/null
+++ b/doc/sphinx/uml/assign-lease4.uml
@@ -0,0 +1,64 @@
+@startuml
+
+title DHCPv4 Assign Lease (Kea 1.8.0)
+
+agent "Check Subnet" as subnet
+note left : entry point
+
+agent "Get server id" as server_id
+
+agent "Get hint" as hint
+
+agent "Get hardware address and client id" as ident
+
+rectangle "INIT-REBOOT state" as init_reboot {
+ agent "Get existing lease by client id" as by_client_id
+
+ agent "Get existing lease by hardware address" as by_hw_addr
+
+ agent "Get authoritative" as authoritative
+}
+
+agent "Process hostname" as hostname
+
+agent "Request lease" as allocate
+
+rectangle "Lease allocated" as allocated {
+ agent "update DDNS" as ddns
+ agent "Send ACK" as ack
+}
+
+agent "No lease allocated" as failed
+note right : exit point
+
+agent "Send NAK" as nak
+note right : exit point
+
+agent "No response" as no_response
+note right : exit point
+
+subnet --> server_id
+subnet ---> nak : no subnet
+server_id --> hint
+hint --> ident : use requested address option
+hint --> ident : use client address
+hint --> ident : no hint
+ident --> init_reboot : requested address and no server id
+ident ---> hostname
+init_reboot --> by_client_id : has a client id
+init_reboot --> by_hw_addr : no client id
+by_client_id ---> authoritative : found
+by_client_id --> by_hw_addr : not found
+by_hw_addr --> authoritative
+authoritative ---> no_response : not authoritative and no owned lease
+authoritative --> nak : owned lease with hint mismatch
+authoritative --> nak : authoritative and no owned lease
+authoritative --> hostname : other cases
+hostname --> allocate
+allocate --> allocated : lease allocated
+allocated --> ddns
+ddns --> ack
+allocate --> failed : no lease allocated
+failed --> nak
+
+@enduml
diff --git a/doc/sphinx/uml/buildCfgOptionList.png b/doc/sphinx/uml/buildCfgOptionList.png
new file mode 100644
index 0000000..262b10e
--- /dev/null
+++ b/doc/sphinx/uml/buildCfgOptionList.png
Binary files differ
diff --git a/doc/sphinx/uml/buildCfgOptionList.svg b/doc/sphinx/uml/buildCfgOptionList.svg
new file mode 100644
index 0000000..e1d7470
--- /dev/null
+++ b/doc/sphinx/uml/buildCfgOptionList.svg
@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="1517px" preserveAspectRatio="none" style="width:615px;height:1517px;background:#FFFFFF;" version="1.1" viewBox="0 0 615 1517" width="615px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="476" x="68" y="34.9659">buildCfgOptionList: build configured option list algorithm (Kea 1.8.0)</text>
+ <ellipse cx="279.25" cy="60.0679" fill="#222222" rx="10" ry="10" style="stroke:#222222;stroke-width:1.0;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="206" x="176.25" y="90.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="186" x="186.25" y="112.896">Get (empty) configured option list</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="55" x="251.75" y="195.903"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="35" x="261.75" y="218.7311">return</text>
+ <ellipse cx="279.25" cy="270.575" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="279.25" cy="270.575" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="252.75,146.412,305.75,146.412,317.75,158.412,305.75,170.412,252.75,170.412,240.75,158.412,252.75,146.412" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="283.25" y="182.1709">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="53" x="252.75" y="162.68">no subnet</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="317.75" y="155.1891">no</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="210" x="174.25" y="373.066"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="190" x="184.25" y="395.8941">push back host configured options</text>
+ <polygon fill="#F1F1F1" points="215.25,323.575,343.25,323.575,355.25,335.575,343.25,347.575,215.25,347.575,203.25,335.575,215.25,323.575" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="283.25" y="359.3339">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="128" x="215.25" y="339.843">current host reservation</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="355.25" y="332.3521">no</text>
+ <polygon fill="#F1F1F1" points="279.25,429.4101,291.25,441.4101,279.25,453.4101,267.25,441.4101,279.25,429.4101" style="stroke:#181818;stroke-width:0.5;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="180" x="189.25" y="522.901"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="160" x="199.25" y="545.7291">get pool of assigned address</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="210" x="174.25" y="628.7361"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="190" x="184.25" y="651.5642">push back pool configured options</text>
+ <polygon fill="#F1F1F1" points="267.25,579.2451,291.25,579.2451,303.25,591.2451,291.25,603.2451,267.25,603.2451,255.25,591.2451,267.25,579.2451" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="283.25" y="615.0041">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="24" x="267.25" y="595.5131">pool</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="303.25" y="588.0222">no</text>
+ <polygon fill="#F1F1F1" points="279.25,685.0802,291.25,697.0802,279.25,709.0802,267.25,697.0802,279.25,685.0802" style="stroke:#181818;stroke-width:0.5;"/>
+ <polygon fill="#F1F1F1" points="234.25,473.4101,324.25,473.4101,336.25,485.4101,324.25,497.4101,234.25,497.4101,222.25,485.4101,234.25,473.4101" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="283.25" y="509.169">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="90" x="234.25" y="489.6781">assigned address</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="336.25" y="482.1871">no</text>
+ <polygon fill="#F1F1F1" points="279.25,729.0802,291.25,741.0802,279.25,753.0802,267.25,741.0802,279.25,729.0802" style="stroke:#181818;stroke-width:0.5;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="224" x="167.25" y="773.0802"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="204" x="177.25" y="795.9083">push back subnet configured options</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="199" x="179.75" y="829.4243"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="179" x="189.75" y="852.2524">get shared network from subnet</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="273" x="142.75" y="935.2594"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="253" x="152.75" y="958.0875">push back shared network configured options</text>
+ <polygon fill="#F1F1F1" points="237.75,885.7684,320.75,885.7684,332.75,897.7684,320.75,909.7684,237.75,909.7684,225.75,897.7684,237.75,885.7684" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="283.25" y="921.5273">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="83" x="237.75" y="902.0364">shared network</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="332.75" y="894.5455">no</text>
+ <polygon fill="#F1F1F1" points="279.25,991.6035,291.25,1003.6035,279.25,1015.6035,267.25,1003.6035,279.25,991.6035" style="stroke:#181818;stroke-width:0.5;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="308" x="125.25" y="1079.6035"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="288" x="135.25" y="1102.4316">get client class definition from current configuration</text>
+ <polygon fill="#F1F1F1" points="263.25,1135.9476,295.25,1135.9476,307.25,1147.9476,295.25,1159.9476,263.25,1159.9476,251.25,1147.9476,263.25,1135.9476" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="32" x="263.25" y="1152.2156">found</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="237.25" y="1144.7246">no</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="307.25" y="1144.7246">yes</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="193" x="38" y="1219.4386"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="173" x="48" y="1242.2666">log debug "class unconfigured"</text>
+ <polygon fill="#F1F1F1" points="86.5,1169.9476,182.5,1169.9476,194.5,1181.9476,182.5,1193.9476,86.5,1193.9476,74.5,1181.9476,86.5,1169.9476" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="138.5" y="1205.7065">no</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="96" x="86.5" y="1186.2156">built-in client class</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="194.5" y="1178.7246">yes</text>
+ <polygon fill="#F1F1F1" points="134.5,1275.7827,146.5,1287.7827,134.5,1299.7827,122.5,1287.7827,134.5,1275.7827" style="stroke:#181818;stroke-width:0.5;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="302" x="273" y="1169.9476"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="282" x="283" y="1192.7757">push back client class definition configured options</text>
+ <polygon fill="#F1F1F1" points="279.25,1305.7827,291.25,1317.7827,279.25,1329.7827,267.25,1317.7827,279.25,1305.7827" style="stroke:#181818;stroke-width:0.5;"/>
+ <polygon fill="#F1F1F1" points="211.75,1035.6035,346.75,1035.6035,358.75,1047.6035,346.75,1059.6035,211.75,1059.6035,199.75,1047.6035,211.75,1035.6035" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="135" x="211.75" y="1051.8715">for each query client class</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="220" x="169.25" y="1371.7827"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="200" x="179.25" y="1394.6108">push back global configured options</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="55" x="251.75" y="1428.1268"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="35" x="261.75" y="1450.9549">return</text>
+ <ellipse cx="279.25" cy="1495.4709" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="279.25" cy="1495.4709" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="70.0679" y2="90.0679"/>
+ <polygon fill="#181818" points="275.25,80.0679,279.25,90.0679,283.25,80.0679,279.25,84.0679" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="232.2471" y2="259.575"/>
+ <polygon fill="#181818" points="275.25,249.575,279.25,259.575,283.25,249.575,279.25,253.575" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="170.412" y2="195.903"/>
+ <polygon fill="#181818" points="275.25,185.903,279.25,195.903,283.25,185.903,279.25,189.903" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="317.75" x2="329.75" y1="158.412" y2="158.412"/>
+ <polygon fill="#181818" points="325.75,229.575,329.75,239.575,333.75,229.575,329.75,233.575" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="329.75" x2="329.75" y1="158.412" y2="303.575"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="329.75" x2="279.25" y1="303.575" y2="303.575"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="303.575" y2="323.575"/>
+ <polygon fill="#181818" points="275.25,313.575,279.25,323.575,283.25,313.575,279.25,317.575" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="126.412" y2="146.412"/>
+ <polygon fill="#181818" points="275.25,136.412,279.25,146.412,283.25,136.412,279.25,140.412" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="347.575" y2="373.066"/>
+ <polygon fill="#181818" points="275.25,363.066,279.25,373.066,283.25,363.066,279.25,367.066" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="355.25" x2="394.25" y1="335.575" y2="335.575"/>
+ <polygon fill="#181818" points="390.25,381.238,394.25,391.238,398.25,381.238,394.25,385.238" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="394.25" x2="394.25" y1="335.575" y2="441.4101"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="394.25" x2="291.25" y1="441.4101" y2="441.4101"/>
+ <polygon fill="#181818" points="301.25,437.4101,291.25,441.4101,301.25,445.4101,297.25,441.4101" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="409.4101" y2="429.4101"/>
+ <polygon fill="#181818" points="275.25,419.4101,279.25,429.4101,283.25,419.4101,279.25,423.4101" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="603.2451" y2="628.7361"/>
+ <polygon fill="#181818" points="275.25,618.7361,279.25,628.7361,283.25,618.7361,279.25,622.7361" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="303.25" x2="394.25" y1="591.2451" y2="591.2451"/>
+ <polygon fill="#181818" points="390.25,636.9081,394.25,646.9081,398.25,636.9081,394.25,640.9081" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="394.25" x2="394.25" y1="591.2451" y2="697.0802"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="394.25" x2="291.25" y1="697.0802" y2="697.0802"/>
+ <polygon fill="#181818" points="301.25,693.0802,291.25,697.0802,301.25,701.0802,297.25,697.0802" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="665.0802" y2="685.0802"/>
+ <polygon fill="#181818" points="275.25,675.0802,279.25,685.0802,283.25,675.0802,279.25,679.0802" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="559.2451" y2="579.2451"/>
+ <polygon fill="#181818" points="275.25,569.2451,279.25,579.2451,283.25,569.2451,279.25,573.2451" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="497.4101" y2="522.901"/>
+ <polygon fill="#181818" points="275.25,512.901,279.25,522.901,283.25,512.901,279.25,516.901" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="336.25" x2="416.25" y1="485.4101" y2="485.4101"/>
+ <polygon fill="#181818" points="412.25,601.2361,416.25,611.2361,420.25,601.2361,416.25,605.2361" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="416.25" x2="416.25" y1="485.4101" y2="741.0802"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="416.25" x2="291.25" y1="741.0802" y2="741.0802"/>
+ <polygon fill="#181818" points="301.25,737.0802,291.25,741.0802,301.25,745.0802,297.25,741.0802" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="709.0802" y2="729.0802"/>
+ <polygon fill="#181818" points="275.25,719.0802,279.25,729.0802,283.25,719.0802,279.25,723.0802" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="453.4101" y2="473.4101"/>
+ <polygon fill="#181818" points="275.25,463.4101,279.25,473.4101,283.25,463.4101,279.25,467.4101" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="753.0802" y2="773.0802"/>
+ <polygon fill="#181818" points="275.25,763.0802,279.25,773.0802,283.25,763.0802,279.25,767.0802" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="809.4243" y2="829.4243"/>
+ <polygon fill="#181818" points="275.25,819.4243,279.25,829.4243,283.25,819.4243,279.25,823.4243" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="909.7684" y2="935.2594"/>
+ <polygon fill="#181818" points="275.25,925.2594,279.25,935.2594,283.25,925.2594,279.25,929.2594" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="332.75" x2="425.75" y1="897.7684" y2="897.7684"/>
+ <polygon fill="#181818" points="421.75,943.4314,425.75,953.4314,429.75,943.4314,425.75,947.4314" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="425.75" x2="425.75" y1="897.7684" y2="1003.6035"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="425.75" x2="291.25" y1="1003.6035" y2="1003.6035"/>
+ <polygon fill="#181818" points="301.25,999.6035,291.25,1003.6035,301.25,1007.6035,297.25,1003.6035" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="971.6035" y2="991.6035"/>
+ <polygon fill="#181818" points="275.25,981.6035,279.25,991.6035,283.25,981.6035,279.25,985.6035" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="865.7684" y2="885.7684"/>
+ <polygon fill="#181818" points="275.25,875.7684,279.25,885.7684,283.25,875.7684,279.25,879.7684" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="134.5" x2="134.5" y1="1193.9476" y2="1219.4386"/>
+ <polygon fill="#181818" points="130.5,1209.4386,134.5,1219.4386,138.5,1209.4386,134.5,1213.4386" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="194.5" x2="241" y1="1181.9476" y2="1181.9476"/>
+ <polygon fill="#181818" points="237,1227.6106,241,1237.6106,245,1227.6106,241,1231.6106" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="241" x2="241" y1="1181.9476" y2="1287.7827"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="241" x2="146.5" y1="1287.7827" y2="1287.7827"/>
+ <polygon fill="#181818" points="156.5,1283.7827,146.5,1287.7827,156.5,1291.7827,152.5,1287.7827" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="134.5" x2="134.5" y1="1255.7827" y2="1275.7827"/>
+ <polygon fill="#181818" points="130.5,1265.7827,134.5,1275.7827,138.5,1265.7827,134.5,1269.7827" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="251.25" x2="134.5" y1="1147.9476" y2="1147.9476"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="134.5" x2="134.5" y1="1147.9476" y2="1169.9476"/>
+ <polygon fill="#181818" points="130.5,1159.9476,134.5,1169.9476,138.5,1159.9476,134.5,1163.9476" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="307.25" x2="424" y1="1147.9476" y2="1147.9476"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="424" x2="424" y1="1147.9476" y2="1169.9476"/>
+ <polygon fill="#181818" points="420,1159.9476,424,1169.9476,428,1159.9476,424,1163.9476" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="134.5" x2="134.5" y1="1299.7827" y2="1317.7827"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="134.5" x2="267.25" y1="1317.7827" y2="1317.7827"/>
+ <polygon fill="#181818" points="257.25,1313.7827,267.25,1317.7827,257.25,1321.7827,261.25,1317.7827" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="424" x2="424" y1="1206.2917" y2="1317.7827"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="424" x2="291.25" y1="1317.7827" y2="1317.7827"/>
+ <polygon fill="#181818" points="301.25,1313.7827,291.25,1317.7827,301.25,1321.7827,297.25,1317.7827" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="1115.9476" y2="1135.9476"/>
+ <polygon fill="#181818" points="275.25,1125.9476,279.25,1135.9476,283.25,1125.9476,279.25,1129.9476" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="1059.6035" y2="1079.6035"/>
+ <polygon fill="#181818" points="275.25,1069.6035,279.25,1079.6035,283.25,1069.6035,279.25,1073.6035" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="1329.7827" y2="1339.7827"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="589" y1="1339.7827" y2="1339.7827"/>
+ <polygon fill="#181818" points="585,1197.9386,589,1187.9386,593,1197.9386,589,1193.9386" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="589" x2="589" y1="1047.6035" y2="1339.7827"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="589" x2="358.75" y1="1047.6035" y2="1047.6035"/>
+ <polygon fill="#181818" points="368.75,1043.6035,358.75,1047.6035,368.75,1051.6035,364.75,1047.6035" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="199.75" x2="24" y1="1047.6035" y2="1047.6035"/>
+ <polygon fill="#181818" points="20,1183.9386,24,1193.9386,28,1183.9386,24,1187.9386" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="24" x2="24" y1="1047.6035" y2="1351.7827"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="24" x2="279.25" y1="1351.7827" y2="1351.7827"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="1351.7827" y2="1371.7827"/>
+ <polygon fill="#181818" points="275.25,1361.7827,279.25,1371.7827,283.25,1361.7827,279.25,1365.7827" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="1015.6035" y2="1035.6035"/>
+ <polygon fill="#181818" points="275.25,1025.6035,279.25,1035.6035,283.25,1025.6035,279.25,1029.6035" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="1408.1268" y2="1428.1268"/>
+ <polygon fill="#181818" points="275.25,1418.1268,279.25,1428.1268,283.25,1418.1268,279.25,1422.1268" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="279.25" x2="279.25" y1="1464.4709" y2="1484.4709"/>
+ <polygon fill="#181818" points="275.25,1474.4709,279.25,1484.4709,283.25,1474.4709,279.25,1478.4709" style="stroke:#181818;stroke-width:1.0;"/>
+ <!--SRC=[ZLFBJiCm4BpxArOz9GSGt51pvC019Bx1YTUELTSR_A3gt-C7eKsQIjoiVSpEpgo33WQXZzg8Lwa-fw39VcWVzZK60rcfLNGeW4eIJCe2DugS3kCHcdVayFpuylZKCkO3Tu7jtp10WySfdDix01rZMa9Z2NpiBOOMmeWMcZFwbW7i7OReR9UUFj34q7ZCrIr3AxIipKDq3cs0aH8XXnxTDyy3rbXJz2FqV3ZKoXj2ljl6vzvhPLCP5oB1-pMkIbjEH0P8mhesCyyOER_gMp0g_LkC02wSqhkmkc5v1LHbt8HYXQaVUOwceXEv0qX7nz-chcoxpD_NS8NpBpLFeqv7raXoW7mOuIkYEyDWTBPlC4cyDAIml8w1GAcjBZTJwCw6ppEBsqcgJ5zIj69cNKMh3kGp3W_QBiUjBRaMkrXaIANvVLImgsIYlQowwsfJsMvkthBttZft7AykMr7qMjYh3FLy-rB-_ahvAJy0]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/buildCfgOptionList.uml b/doc/sphinx/uml/buildCfgOptionList.uml
new file mode 100644
index 0000000..1df19f8
--- /dev/null
+++ b/doc/sphinx/uml/buildCfgOptionList.uml
@@ -0,0 +1,52 @@
+@startuml
+
+title buildCfgOptionList: build configured option list algorithm (Kea 1.8.0)
+
+start
+:Get (empty) configured option list;
+
+if (no subnet) then (yes)
+ :return;
+ stop
+else (no)
+endif
+
+if (current host reservation) then (yes)
+ :push back host configured options;
+else (no)
+endif
+
+if (assigned address) then (yes)
+ :get pool of assigned address;
+ if (pool) then (yes)
+ :push back pool configured options;
+ else (no)
+ endif
+else (no)
+endif
+
+:push back subnet configured options;
+
+:get shared network from subnet;
+if (shared network) then (yes)
+ :push back shared network configured options;
+else (no)
+endif
+
+while (for each query client class)
+ :get client class definition from current configuration;
+ if (found) then (no)
+ if (built-in client class) then (yes)
+ else (no)
+ :log debug "class unconfigured";
+ endif
+ else (yes)
+ :push back client class definition configured options;
+ endif
+endwhile
+
+:push back global configured options;
+:return;
+stop
+
+@enduml
diff --git a/doc/sphinx/uml/currentHost4.png b/doc/sphinx/uml/currentHost4.png
new file mode 100644
index 0000000..e8aa59e
--- /dev/null
+++ b/doc/sphinx/uml/currentHost4.png
Binary files differ
diff --git a/doc/sphinx/uml/currentHost4.svg b/doc/sphinx/uml/currentHost4.svg
new file mode 100644
index 0000000..58ed82e
--- /dev/null
+++ b/doc/sphinx/uml/currentHost4.svg
@@ -0,0 +1,301 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="2256px" preserveAspectRatio="none" style="width:944px;height:2256px;background:#FFFFFF;" version="1.1" viewBox="0 0 944 2256" width="944px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="218" x="356.25" y="24.9659">currentHost DHCPv4 (Kea 1.8.0)</text>
+ <!--entity entry-->
+ <g id="elem_entry">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="133" x="301" y="47.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="113" x="311" y="72.0339">Subnet Selection</text>
+ </g>
+ <!--entity setSelected-->
+ <g id="elem_setSelected">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="246" x="244.5" y="147.1379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="226" x="254.5" y="172.1039">Set subnet to the selected subnet</text>
+ </g>
+ <!--entity clientid_lookup-->
+ <g id="elem_clientid_lookup">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="244" x="245.5" y="247.2079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="224" x="255.5" y="272.1739">Has client a lease for its client id?</text>
+ </g>
+ <!--entity clientid_iterate-->
+ <g id="elem_clientid_iterate">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="281" x="7" y="347.2779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="261" x="17" y="372.2439">Iterate on allowed subnets for client id</text>
+ </g>
+ <!--entity found_clientid-->
+ <g id="elem_found_clientid">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="252" x="21.5" y="534.4079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="232" x="31.5" y="559.3739">Set subnet to the by client id lease</text>
+ </g>
+ <!--entity hwaddr_lookup-->
+ <g id="elem_hwaddr_lookup">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="211" x="429" y="465.3379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="191" x="439" y="490.3039">Has client a matching lease?</text>
+ </g>
+ <!--entity hwaddr_iterate-->
+ <g id="elem_hwaddr_iterate">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="350" x="200.5" y="603.4779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="330" x="210.5" y="628.4439">Iterate on allowed subnets for hardware address</text>
+ </g>
+ <!--entity found_hwaddr-->
+ <g id="elem_found_hwaddr">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="242" x="227.5" y="721.5479"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="222" x="237.5" y="746.5139">Set subnet to the matching lease</text>
+ </g>
+ <!--entity hasAddressReservation-->
+ <g id="elem_hasAddressReservation">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="210" x="373.5" y="981.6079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="190" x="383.5" y="1006.5739">Has an address reservation?</text>
+ </g>
+ <!--entity setAddressReservation-->
+ <g id="elem_setAddressReservation">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="298" x="220.5" y="1099.6779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="278" x="230.5" y="1124.6439">Set subnet to address reservation subnet</text>
+ </g>
+ <!--entity pool-->
+ <g id="elem_pool">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="249" x="354" y="1199.7479"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="229" x="364" y="1224.7139">Is the address in an allowed pool?</text>
+ </g>
+ <!--entity pool_iterate-->
+ <g id="elem_pool_iterate">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="266" x="146.5" y="1299.8179"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="246" x="156.5" y="1324.7839">Iterate on allowed subnets with pool</text>
+ </g>
+ <!--entity inAllowedPool-->
+ <g id="elem_inAllowedPool">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="251" x="154" y="1417.8879"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="231" x="164" y="1442.8539">Set subnet to address pool subnet</text>
+ </g>
+ <!--entity allocate-->
+ <g id="elem_allocate">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="156" x="396.5" y="1517.9479"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="136" x="406.5" y="1542.9139">Allocate a new lease</text>
+ </g>
+ <!--entity allocate_iterate-->
+ <g id="elem_allocate_iterate">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="200" x="237.5" y="1636.0179"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="180" x="247.5" y="1660.9839">Iterate on allowed subnets</text>
+ </g>
+ <!--entity allocated-->
+ <g id="elem_allocated">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="264" x="273.5" y="1754.0879"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="244" x="283.5" y="1779.0539">Set subnet to allocated lease subnet</text>
+ </g>
+ <!--entity getHRmode-->
+ <g id="elem_getHRmode">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="249" x="395" y="1854.1579"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="229" x="405" y="1879.1239">Get subnet host reservation mode</text>
+ </g>
+ <!--entity checkHRmode-->
+ <g id="elem_checkHRmode">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="290" x="374.5" y="1954.2279"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="270" x="384.5" y="1979.1939">Is subnet host reservation mode global?</text>
+ </g>
+ <!--entity global-->
+ <g id="elem_global">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="202" x="298.5" y="2072.2879"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="182" x="308.5" y="2097.2539">Get global host reservation</text>
+ </g>
+ <!--entity bySubnet-->
+ <g id="elem_bySubnet">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="207" x="536" y="2072.2879"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="187" x="546" y="2097.2539">Get subnet host reservation</text>
+ </g>
+ <!--entity return-->
+ <g id="elem_return">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="233" x="402" y="2190.3579"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="213" x="412" y="2215.3239">Return current host reservation</text>
+ </g>
+ <!--link entry to setSelected-->
+ <g id="link_entry_setSelected">
+ <path d="M367.5,86.5079 C367.5,101.8379 367.5,123.5879 367.5,140.4079 " fill="none" id="entry-to-setSelected" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="367.5,145.3079,371.5,136.3079,367.5,140.3079,363.5,136.3079,367.5,145.3079" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link setSelected to clientid_lookup-->
+ <g id="link_setSelected_clientid_lookup">
+ <path d="M367.5,186.5779 C367.5,201.9079 367.5,223.6579 367.5,240.4779 " fill="none" id="setSelected-to-clientid_lookup" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="367.5,245.3779,371.5,236.3779,367.5,240.3779,363.5,236.3779,367.5,245.3779" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link clientid_lookup to hwaddr_lookup-->
+ <g id="link_clientid_lookup_hwaddr_lookup">
+ <path d="M432.71,286.6579 C466.61,299.2479 506.66,318.7979 534.5,347.2779 C563.51,376.9479 577.14,395.5079 565.5,435.3379 C562.95,444.0779 558.29,452.7279 553.35,460.2679 " fill="none" id="clientid_lookup-to-hwaddr_lookup" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="550.77,464.0379,559.1444,458.8543,553.5862,459.9065,552.5341,454.3483,550.77,464.0379" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="108" x="564.5" y="372.2049">no client id option</text>
+ </g>
+ <!--link clientid_lookup to clientid_iterate-->
+ <g id="link_clientid_lookup_clientid_iterate">
+ <path d="M324.82,286.7679 C287.78,303.2779 234.2,327.1579 195.64,344.3479 " fill="none" id="clientid_lookup-to-clientid_iterate" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="191.44,346.2179,201.2889,346.2122,196.0079,344.1846,198.0355,338.9036,191.44,346.2179" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link clientid_iterate to clientid_iterate-->
+ <g id="link_clientid_iterate_clientid_iterate">
+ <path d="M288.3,358.3179 C309.02,359.6579 323,362.4879 323,366.8079 C323,370.6879 311.71,373.3679 294.4,374.8379 " fill="none" id="clientid_iterate-to-clientid_iterate" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="289.81,375.1879,299.0823,378.5082,294.7962,374.8165,298.488,370.5303,289.81,375.1879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="199" x="329" y="372.2049">match-client-id is false or no lease</text>
+ </g>
+ <!--link clientid_iterate to found_clientid-->
+ <g id="link_clientid_iterate_found_clientid">
+ <path d="M147.5,386.5779 C147.5,420.1179 147.5,490.3479 147.5,527.9379 " fill="none" id="clientid_iterate-to-found_clientid" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="147.5,532.6079,151.5,523.6079,147.5,527.6079,143.5,523.6079,147.5,532.6079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="79" x="148.5" y="431.2349">found a lease</text>
+ </g>
+ <!--link found_clientid to hasAddressReservation-->
+ <g id="link_found_clientid_hasAddressReservation">
+ <path d="M147.5,573.7479 C147.5,598.3879 147.5,642.9479 147.5,681.0479 C147.5,681.0479 147.5,681.0479 147.5,920.6079 C147.5,966.8079 270.91,986.2379 367.03,994.3679 " fill="none" id="found_clientid-to-hasAddressReservation" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="371.81,994.7679,363.1721,990.0364,366.8272,994.3539,362.5097,998.0089,371.81,994.7679" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link clientid_iterate to hwaddr_lookup-->
+ <g id="link_clientid_iterate_hwaddr_lookup">
+ <path d="M182.5,386.8379 C215.25,404.3879 261.96,428.6879 281.5,435.3379 C326.59,450.6879 377.94,461.6679 422.55,469.2579 " fill="none" id="clientid_iterate-to-hwaddr_lookup" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="427.3,470.0579,419.0851,464.625,422.3688,469.2313,417.7626,472.515,427.3,470.0579" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="278" x="282.5" y="431.2349">not found by client id, try by hardware address</text>
+ </g>
+ <!--link hwaddr_lookup to hasAddressReservation-->
+ <g id="link_hwaddr_lookup_hasAddressReservation">
+ <path d="M640.43,496.0879 C717.9,510.0979 809.5,543.2979 809.5,622.0079 C809.5,622.0079 809.5,622.0079 809.5,920.6079 C809.5,966.8079 686.09,986.2379 589.97,994.3679 " fill="none" id="hwaddr_lookup-to-hasAddressReservation" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="585.19,994.7679,594.4903,998.0089,590.1728,994.3539,593.8279,990.0364,585.19,994.7679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="126" x="810.5" y="805.5049">no hardware address</text>
+ </g>
+ <!--link hwaddr_lookup to hwaddr_iterate-->
+ <g id="link_hwaddr_lookup_hwaddr_iterate">
+ <path d="M512.39,504.8079 C483.64,529.4279 433.44,572.4079 402.47,598.9179 " fill="none" id="hwaddr_lookup-to-hwaddr_iterate" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="398.83,602.0379,408.2726,599.2383,402.6333,598.7922,403.0794,593.153,398.83,602.0379" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link hwaddr_iterate to hwaddr_iterate-->
+ <g id="link_hwaddr_iterate_hwaddr_iterate">
+ <path d="M550.89,613.4979 C571.83,615.2279 585.5,618.3979 585.5,623.0079 C585.5,627.1579 574.46,630.1379 557.04,631.9479 " fill="none" id="hwaddr_iterate-to-hwaddr_iterate" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="552.4,632.3879,561.7285,635.547,557.379,631.9301,560.9959,627.5806,552.4,632.3879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="176" x="591.5" y="628.4049">no lease or client id mismatch</text>
+ </g>
+ <!--link hwaddr_iterate to found_hwaddr-->
+ <g id="link_hwaddr_iterate_found_hwaddr">
+ <path d="M371.1,642.9179 C366.53,662.5579 359.36,693.3979 354.32,715.0579 " fill="none" id="hwaddr_iterate-to-found_hwaddr" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="353.24,719.7079,359.168,711.8429,354.3685,714.8369,351.3745,710.0374,353.24,719.7079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="79" x="365.5" y="687.4449">found a lease</text>
+ </g>
+ <!--link found_hwaddr to hasAddressReservation-->
+ <g id="link_found_hwaddr_hasAddressReservation">
+ <path d="M358.06,761.0579 C381.21,807.0079 440.27,924.2479 466.09,975.5179 " fill="none" id="found_hwaddr-to-hasAddressReservation" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="468.25,979.7879,467.7902,969.9498,466.0082,975.3186,460.6394,973.5366,468.25,979.7879" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link hwaddr_iterate to hasAddressReservation-->
+ <g id="link_hwaddr_iterate_hasAddressReservation">
+ <path d="M432.33,643.0379 C473.7,660.8879 522.5,692.1179 522.5,740.0779 C522.5,740.0779 522.5,740.0779 522.5,920.6079 C522.5,941.1679 511.11,961.3979 499.91,976.3179 " fill="none" id="hwaddr_iterate-to-hasAddressReservation" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="497.07,979.9679,505.7564,975.326,500.1427,976.0235,499.4452,970.4098,497.07,979.9679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="60" x="523.5" y="854.5049">not found</text>
+ </g>
+ <!--link hasAddressReservation to setAddressReservation-->
+ <g id="link_hasAddressReservation_setAddressReservation">
+ <path d="M460.74,1021.0579 C441.9,1041.1179 412.09,1072.8679 391.7,1094.5779 " fill="none" id="hasAddressReservation-to-setAddressReservation" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="388.28,1098.2079,397.3611,1094.3956,391.7068,1094.5669,391.5355,1088.9127,388.28,1098.2079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="20" x="431.5" y="1065.5749">yes</text>
+ </g>
+ <!--link hasAddressReservation to pool-->
+ <g id="link_hasAddressReservation_pool">
+ <path d="M495.7,1020.7579 C517.49,1046.6879 550.63,1095.3879 536.5,1138.7479 C529.65,1159.7679 514.91,1179.8579 502,1194.5879 " fill="none" id="hasAddressReservation-to-pool" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="498.72,1198.2379,507.7062,1194.207,502.0577,1194.5151,501.7496,1188.8666,498.72,1198.2379" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="212" x="540.5" y="1124.6149">no or not check in the taken branch</text>
+ </g>
+ <!--link setAddressReservation to pool-->
+ <g id="link_setAddressReservation_pool">
+ <path d="M390.52,1139.1279 C408.31,1155.1279 433.88,1178.1379 452.87,1195.2279 " fill="none" id="setAddressReservation-to-pool" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="456.42,1198.4179,452.4062,1189.4241,452.7035,1195.0731,447.0545,1195.3704,456.42,1198.4179" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link pool to pool_iterate-->
+ <g id="link_pool_pool_iterate">
+ <path d="M440.13,1239.1879 C406.79,1255.6179 358.5,1279.4179 323.58,1296.6279 " fill="none" id="pool-to-pool_iterate" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="319.45,1298.6679,329.2909,1298.271,323.9335,1296.4547,325.7497,1291.0974,319.45,1298.6679" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link pool to allocate-->
+ <g id="link_pool_allocate">
+ <path d="M557.89,1239.2379 C589.58,1251.1179 622.9,1270.0379 641.5,1299.8179 C650.7,1314.5379 645.43,1321.9679 641.5,1338.8879 C628.27,1395.8279 621.1,1412.1779 583.5,1456.9479 C564.11,1480.0379 536.68,1500.0079 514.18,1514.1679 " fill="none" id="pool-to-allocate" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="510.16,1516.6579,519.9161,1515.3094,514.408,1514.0207,515.6966,1508.5126,510.16,1516.6579" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="188" x="634.5" y="1383.7849">pool check is not in all branches</text>
+ </g>
+ <!--link pool_iterate to pool_iterate-->
+ <g id="link_pool_iterate_pool_iterate">
+ <path d="M412.93,1310.7779 C433.5,1312.0679 447.5,1314.9279 447.5,1319.3479 C447.5,1323.3279 436.2,1326.0379 418.99,1327.4879 " fill="none" id="pool_iterate-to-pool_iterate" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="414.44,1327.8179,423.7123,1331.1382,419.4262,1327.4465,423.118,1323.1603,414.44,1327.8179" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="182" x="453.5" y="1324.7449">address not in an allowed pool</text>
+ </g>
+ <!--link pool_iterate to inAllowedPool-->
+ <g id="link_pool_iterate_inAllowedPool">
+ <path d="M279.5,1339.2579 C279.5,1358.8179 279.5,1389.4779 279.5,1411.1179 " fill="none" id="pool_iterate-to-inAllowedPool" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="279.5,1416.0079,283.5,1407.0079,279.5,1411.0079,275.5,1407.0079,279.5,1416.0079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="158" x="280.5" y="1383.7849">address in an allowed pool</text>
+ </g>
+ <!--link pool_iterate to allocate-->
+ <g id="link_pool_iterate_allocate">
+ <path d="M405.43,1339.2479 C421.34,1346.3379 436.03,1355.9279 447.5,1368.8879 C482.63,1408.5679 481.73,1475.1879 478.08,1511.3279 " fill="none" id="pool_iterate-to-allocate" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="477.55,1516.2079,482.5033,1507.6953,478.0928,1511.2375,474.5506,1506.8269,477.55,1516.2079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="95" x="479.5" y="1442.8149">no allowed pool</text>
+ </g>
+ <!--link inAllowedPool to allocate-->
+ <g id="link_inAllowedPool_allocate">
+ <path d="M317.1,1457.3279 C349.64,1473.6879 396.7,1497.3579 430.89,1514.5579 " fill="none" id="inAllowedPool-to-allocate" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="435.33,1516.7879,429.0787,1509.1773,430.8607,1514.5461,425.4919,1516.3281,435.33,1516.7879" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link allocate to allocate_iterate-->
+ <g id="link_allocate_allocate_iterate">
+ <path d="M399.66,1557.3979 C383.63,1564.4479 368.06,1574.0279 356.5,1587.0179 C346.18,1598.6179 341.42,1615.4079 339.25,1629.3479 " fill="none" id="allocate-to-allocate_iterate" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="338.63,1634.0279,343.778,1625.6316,339.2871,1629.0713,335.8474,1624.5803,338.63,1634.0279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="228" x="357.5" y="1601.9149">start from preferred (last used) subnet</text>
+ </g>
+ <!--link allocate to getHRmode-->
+ <g id="link_allocate_getHRmode">
+ <path d="M552.86,1556.5279 C567.72,1563.5779 581.43,1573.3879 590.5,1587.0179 C646.76,1671.5379 572.93,1796.9279 537.05,1848.8479 " fill="none" id="allocate-to-getHRmode" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="534.36,1852.6879,542.7993,1847.6107,537.2283,1848.5924,536.2465,1843.0214,534.36,1852.6879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="182" x="605.5" y="1719.9849">allocation is not in all branches</text>
+ </g>
+ <!--link allocate_iterate to allocate_iterate-->
+ <g id="link_allocate_iterate_allocate_iterate">
+ <path d="M437.88,1644.9279 C458.07,1646.0979 472.5,1649.6479 472.5,1655.5579 C472.5,1660.8179 461.06,1664.2079 444.29,1665.7079 " fill="none" id="allocate_iterate-to-allocate_iterate" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="439.39,1666.0679,448.6623,1669.3882,444.3762,1665.6965,448.068,1661.4103,439.39,1666.0679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="92" x="478.5" y="1660.9549">no free address</text>
+ </g>
+ <!--link allocate_iterate to allocated-->
+ <g id="link_allocate_iterate_allocated">
+ <path d="M348.58,1675.4579 C360.18,1695.2779 378.46,1726.4779 391.17,1748.1579 " fill="none" id="allocate_iterate-to-allocated" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="393.66,1752.4179,392.5601,1742.6307,391.1318,1748.1042,385.6582,1746.6758,393.66,1752.4179" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="122" x="377.5" y="1719.9849">found a free address</text>
+ </g>
+ <!--link allocated to getHRmode-->
+ <g id="link_allocated_getHRmode">
+ <path d="M427.48,1793.5279 C446.09,1809.5379 472.83,1832.5479 492.69,1849.6279 " fill="none" id="allocated-to-getHRmode" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="496.44,1852.8479,492.2264,1843.9459,492.6498,1849.5869,487.0088,1850.0103,496.44,1852.8479" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link getHRmode to checkHRmode-->
+ <g id="link_getHRmode_checkHRmode">
+ <path d="M519.5,1893.5979 C519.5,1908.9279 519.5,1930.6779 519.5,1947.4979 " fill="none" id="getHRmode-to-checkHRmode" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="519.5,1952.3979,523.5,1943.3979,519.5,1947.3979,515.5,1943.3979,519.5,1952.3979" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link checkHRmode to global-->
+ <g id="link_checkHRmode_global">
+ <path d="M499.95,1993.6679 C479.12,2013.8179 446.11,2045.7479 423.66,2067.4579 " fill="none" id="checkHRmode-to-global" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="420.13,2070.8779,429.3782,2067.4911,423.7221,2067.3999,423.8133,2061.7437,420.13,2070.8779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="20" x="467.5" y="2038.1849">yes</text>
+ </g>
+ <!--link checkHRmode to bySubnet-->
+ <g id="link_checkHRmode_bySubnet">
+ <path d="M539.05,1993.6679 C559.88,2013.8179 592.89,2045.7479 615.34,2067.4579 " fill="none" id="checkHRmode-to-bySubnet" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="618.87,2070.8779,615.1867,2061.7437,615.2779,2067.3999,609.6218,2067.4911,618.87,2070.8779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="16" x="587.5" y="2038.1849">no</text>
+ </g>
+ <!--link global to return-->
+ <g id="link_global_return">
+ <path d="M398,2111.5979 C397.82,2126.2979 399.86,2146.5079 410.5,2160.3579 C419.21,2171.6979 431.16,2180.4579 443.95,2187.2079 " fill="none" id="global-to-return" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="448.12,2189.2979,441.859,2181.6953,443.6479,2187.0618,438.2813,2188.8507,448.12,2189.2979" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="179" x="411.5" y="2156.2549">return global host reservation</text>
+ </g>
+ <!--link bySubnet to return-->
+ <g id="link_bySubnet_return">
+ <path d="M630.17,2111.5779 C622.32,2126.0679 610.05,2146.0179 595.5,2160.3579 C585.35,2170.3579 572.79,2179.3379 560.79,2186.7779 " fill="none" id="bySubnet-to-return" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="556.61,2189.2979,566.3825,2188.0735,560.8912,2186.715,562.2497,2181.2237,556.61,2189.2979" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="184" x="611.5" y="2156.2549">return subnet host reservation</text>
+ </g>
+ <text fill="#888888" font-family="sans-serif" font-size="10" lengthAdjust="spacing" textLength="620" x="155.25" y="2245.1158">Only the initial lookup is always performed: other occasions to change the subnet so the current host are only in some branches</text>
+ <!--SRC=[bLR1Rjim3BtxAuYUMc0fDc27eOSEnGOinGuhsWye59k9XJfI82aDylUZH5cMR2TfBuP4yZrIbFHa9rs7KE-DGUMMsZhulVprzFuThl-YW6-tFsw_tbILs90NhbxsAuKENh33sacjha1O88yv3Y7ajnpcDBWMmOPeRA8vO2owbsXEo2NPwqxwiO0EXKLOQmFI9RDiVWOqJsNpscdzjjybXaU7HZW4hK1qdJwad3Om9PQIH3Bgp0fMnm7BbGM6jTwhvhNdcL_7LhYwbMh3C2w_FOYcCPyilXMcEGY3uB5eRSvqUGLb6Ldrp53KBbIV0-Y3vbtuhOwb2_l0lkV1TIBl3Cde29oXUhIXxfv3gb1MxCjEwuxhyQEFTF0WNHk24-f2q_h4FZIlMge7fdxACtjRxLCBK7Z8cYoYumCr5l6NgejXyJJc1IPNWluPeAs_t_b-R7N3XMxGBP_zBD-1SoZOT7ebuZRKBTPl8xnFop4J0iuOV1-fCeIlZYm-2V2CRczKhrpp81DYgYf852mMzuNmPEFW6ylAQ4un55HSNhW3fJDjqBjmYAV0-wdSZ0qm4qJyuUOksB6W599khkXepqaJA7VO_nak8YlbYQY2eTVMQkHUyEfcx_Si_RGHBcR8vV8B-CwJPQnVLOa_dPrxV15-FnR3SWgJ0ERb-vC_3h2LDZHzmf1woeuJ7OtES_sS8JwzzDZLQZuYdkbPx17j6LHGjj23S98SNsAlj5vudNX31IiZ53LYFa5FKWMcVfRw7wraxYM9iWLZJ6H3LXc4C6QYWdCE674IGQ_BFRHy54PeWXTwdU4lOyl2bIw3Izuyk4get9ejmJfXQ5EDtiBEu1f9lnguxWJfrzvYSnDbCESXeYJGVX7iyPjmeekJjFEbqAhM1eTBDOLbk5n2Pb6ieiDBCfGS131x8x8nO_YTu2CxTlRA7-gjOgXtiBgJEGvEl3PL8fW3dNXpAhhDcfe0_rHt33T2AkaaqKV5aVwfFOWZ_IAWeH-qBJPte2dGWAvhOOd3-gUwReNQ8Fy8SohBlsJ5m-MLJVjCj95MRt7OoVy0]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/currentHost4.uml b/doc/sphinx/uml/currentHost4.uml
new file mode 100644
index 0000000..1e9ef4d
--- /dev/null
+++ b/doc/sphinx/uml/currentHost4.uml
@@ -0,0 +1,83 @@
+@startuml
+
+title currentHost DHCPv4 (Kea 1.8.0)
+
+agent "Subnet Selection" as entry
+
+agent "Set subnet to the selected subnet" as setSelected
+
+agent "Has client a lease for its client id?" as clientid_lookup
+
+agent "Iterate on allowed subnets for client id" as clientid_iterate
+
+agent "Set subnet to the by client id lease" as found_clientid
+
+agent "Has client a matching lease?" as hwaddr_lookup
+
+agent "Iterate on allowed subnets for hardware address" as hwaddr_iterate
+
+agent "Set subnet to the matching lease" as found_hwaddr
+
+agent "Has an address reservation?" as hasAddressReservation
+
+agent "Set subnet to address reservation subnet" as setAddressReservation
+
+agent "Is the address in an allowed pool?" as pool
+
+agent "Iterate on allowed subnets with pool" as pool_iterate
+
+agent "Set subnet to address pool subnet" as inAllowedPool
+
+agent "Allocate a new lease" as allocate
+
+agent "Iterate on allowed subnets" as allocate_iterate
+
+agent "Set subnet to allocated lease subnet" as allocated
+
+agent "Get subnet host reservation mode" as getHRmode
+
+agent "Is subnet host reservation mode global?" as checkHRmode
+
+agent "Get global host reservation" as global
+
+agent "Get subnet host reservation" as bySubnet
+
+agent "Return current host reservation" as return
+
+entry --> setSelected
+setSelected --> clientid_lookup
+clientid_lookup ---> hwaddr_lookup : no client id option
+clientid_lookup --> clientid_iterate
+clientid_iterate -> clientid_iterate : match-client-id is false or no lease
+clientid_iterate --> found_clientid : found a lease
+found_clientid ----> hasAddressReservation
+clientid_iterate --> hwaddr_lookup : not found by client id, try by hardware address
+hwaddr_lookup ---> hasAddressReservation : no hardware address
+hwaddr_lookup --> hwaddr_iterate
+hwaddr_iterate -> hwaddr_iterate : no lease or client id mismatch
+hwaddr_iterate --> found_hwaddr : found a lease
+found_hwaddr ----> hasAddressReservation
+hwaddr_iterate --> hasAddressReservation : not found
+hasAddressReservation --> setAddressReservation : yes
+hasAddressReservation --> pool : no or not check in the taken branch
+setAddressReservation --> pool
+pool --> pool_iterate
+pool ---> allocate : pool check is not in all branches
+pool_iterate -> pool_iterate : address not in an allowed pool
+pool_iterate --> inAllowedPool : address in an allowed pool
+pool_iterate ---> allocate : no allowed pool
+inAllowedPool --> allocate
+allocate --> allocate_iterate : start from preferred (last used) subnet
+allocate ---> getHRmode : allocation is not in all branches
+allocate_iterate -> allocate_iterate : no free address
+allocate_iterate --> allocated : found a free address
+allocated --> getHRmode
+getHRmode --> checkHRmode
+checkHRmode --> global : yes
+checkHRmode --> bySubnet : no
+global --> return : return global host reservation
+bySubnet --> return : return subnet host reservation
+
+footer Only the initial lookup is always performed: other occasions to change the subnet so the current host are only in some branches
+
+@enduml \ No newline at end of file
diff --git a/doc/sphinx/uml/lease-states.png b/doc/sphinx/uml/lease-states.png
new file mode 100644
index 0000000..76fc762
--- /dev/null
+++ b/doc/sphinx/uml/lease-states.png
Binary files differ
diff --git a/doc/sphinx/uml/lease-states.svg b/doc/sphinx/uml/lease-states.svg
new file mode 100644
index 0000000..873cd64
--- /dev/null
+++ b/doc/sphinx/uml/lease-states.svg
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="728px" preserveAspectRatio="none" style="width:617px;height:728px;background:#FFFFFF;" version="1.1" viewBox="0 0 617 728" width="617px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="156" x="223.5" y="24.9659">lease states (Kea 1.8.0)</text>
+ <!--entity free-->
+ <g id="elem_free">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="49" x="266.5" y="49.3979"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="29" x="276.5" y="74.3639">Free</text>
+ </g>
+ <g id="elem_GMN3">
+ <path d="M61,55.0779 L61,82.7839 A0,0 0 0 0 61,82.7839 L231,82.7839 A0,0 0 0 0 231,82.7839 L231,73.0779 L266.26,68.9379 L231,65.0779 L231,65.0779 L221,55.0779 L61,55.0779 A0,0 0 0 0 61,55.0779 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M221,55.0779 L221,65.0779 L231,65.0779 L221,55.0779 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="149" x="67" y="73.9749">not in the lease database</text>
+ </g>
+ <!--entity assigned-->
+ <g id="elem_assigned">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="82" x="257" y="229.4679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="62" x="267" y="254.4339">Assigned</text>
+ </g>
+ <!--entity assigned_expired-->
+ <g id="elem_assigned_expired">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="137" x="353.5" y="347.5379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="117" x="363.5" y="372.5039">Assigned expired</text>
+ </g>
+ <!--entity declined-->
+ <g id="elem_declined">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="79" x="129.5" y="465.6079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="59" x="139.5" y="490.5739">Declined</text>
+ </g>
+ <!--entity declined_expired-->
+ <g id="elem_declined_expired">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="134" x="40" y="583.6679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="114" x="50" y="608.6339">Declined expired</text>
+ </g>
+ <!--entity reclaimed-->
+ <g id="elem_reclaimed">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="146" x="271" y="683.7379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="126" x="281" y="708.7039">Expired-Reclaimed</text>
+ </g>
+ <!--link free to assigned-->
+ <g id="link_free_assigned">
+ <path d="M273.81,88.6079 C258.11,108.0179 238.52,139.5279 247,168.4679 C253.01,188.9779 266.11,209.2079 277.53,224.1279 " fill="none" id="free-to-assigned" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="280.39,227.7879,278.0148,218.2298,277.3173,223.8435,271.7036,223.146,280.39,227.7879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="70" x="248" y="164.3649">assignment</text>
+ </g>
+ <!--reverse link free to assigned-->
+ <g id="link_free_assigned">
+ <path d="M303.84,94.7279 C311.04,110.0579 319.3,130.3779 323,149.4679 C328.44,177.5379 316.98,209.5379 307.84,229.2579 " fill="none" id="free-backto-assigned" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="301.7,90.2779,302.0088,100.1219,303.8729,94.7811,309.2138,96.6452,301.7,90.2779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="80" x="326" y="164.3649">release query</text>
+ </g>
+ <!--link assigned to assigned_expired-->
+ <g id="link_assigned_assigned_expired">
+ <path d="M339.42,264.1779 C357.42,272.0179 377.63,283.3079 392,298.5379 C403.22,310.4379 410.71,327.2279 415.39,341.0879 " fill="none" id="assigned-to-assigned_expired" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="416.89,345.7879,417.9535,335.9966,415.3646,341.0263,410.3349,338.4373,416.89,345.7879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="106" x="405" y="313.4349">after valid lifetime</text>
+ </g>
+ <!--reverse link assigned to assigned_expired-->
+ <g id="link_assigned_assigned_expired">
+ <path d="M313.23,274.0979 C322.56,287.7579 335.31,304.6379 349,317.5379 C360.86,328.6979 375.55,339.0079 388.65,347.2279 " fill="none" id="assigned-backto-assigned_expired" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="310.55,270.0979,312.2372,279.8012,313.3334,274.2516,318.883,275.3478,310.55,270.0979" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="33" x="350" y="313.4349">reuse</text>
+ </g>
+ <!--link assigned to assigned-->
+ <g id="link_assigned_assigned">
+ <path d="M339.43,239.7679 C357.97,238.8879 374,241.9679 374,248.9979 C374,255.2179 361.51,258.3379 345.82,258.3879 " fill="none" id="assigned-to-assigned" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="340.95,258.2679,349.8562,262.4725,345.9487,258.3822,350.0391,254.4746,340.95,258.2679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="75" x="380" y="254.3949">renew query</text>
+ </g>
+ <!--link assigned to declined-->
+ <g id="link_assigned_declined">
+ <path d="M268.22,269.0179 C243.65,286.3679 210,314.3379 192,347.5379 C172.82,382.9179 168.88,430.4079 168.46,459.0679 " fill="none" id="assigned-to-declined" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="168.43,463.7179,172.4814,454.7409,168.4586,458.718,164.4815,454.6952,168.43,463.7179" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="80" x="193" y="372.4649">decline query</text>
+ </g>
+ <!--link declined to declined_expired-->
+ <g id="link_declined_declined_expired">
+ <path d="M156.82,504.7479 C151.16,513.6679 144.49,524.5779 139,534.6679 C131.42,548.6079 123.78,564.6379 117.87,577.5879 " fill="none" id="declined-to-declined_expired" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="115.9,581.9179,123.2734,575.3886,117.9744,577.3685,115.9944,572.0695,115.9,581.9179" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="133" x="140" y="549.5649">after probation period</text>
+ </g>
+ <!--reverse link assigned to declined_expired-->
+ <g id="link_assigned_declined_expired">
+ <path d="M251.01,269.6579 C235.72,277.3479 219.28,287.0879 206,298.5379 C158.17,339.7579 153.15,359.1379 127,416.6079 C101.37,472.9479 102.85,548.5379 105.26,583.2879 " fill="none" id="assigned-backto-declined_expired" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="255.22,267.5879,245.3791,267.9849,250.7365,269.8011,248.9203,275.1585,255.22,267.5879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="33" x="128" y="431.5049">reuse</text>
+ </g>
+ <!--link assigned_expired to reclaimed-->
+ <g id="link_assigned_expired_reclaimed">
+ <path d="M417.58,386.9979 C404.31,443.8679 364.78,613.2479 349.82,677.3379 " fill="none" id="assigned_expired-to-reclaimed" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="348.71,682.0779,354.638,674.2129,349.8385,677.2069,346.8445,672.4074,348.71,682.0779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="43" x="384" y="549.5649">reclaim</text>
+ </g>
+ <!--link declined_expired to free-->
+ <g id="link_declined_expired_free">
+ <path d="M74.77,583.3279 C45.21,563.3179 6,528.7479 6,486.1379 C6,157.9679 6,157.9679 6,157.9679 C6,46.0679 142.63,126.1379 248,88.4679 C252.01,87.0379 256.18,85.4079 260.28,83.7379 " fill="none" id="declined_expired-to-free" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="264.81,81.8479,254.9636,81.628,260.1967,83.7761,258.0486,89.0092,264.81,81.8479" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="46" x="7" y="313.4349">remove</text>
+ </g>
+ <!--reverse link assigned to reclaimed-->
+ <g id="link_assigned_reclaimed">
+ <path d="M298.47,275.3379 C299.06,309.3879 300,371.8079 300,425.1079 C300,425.1079 300,425.1079 300,604.2079 C300,633.8779 316.97,664.5179 329.84,683.4879 " fill="none" id="assigned-backto-reclaimed" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="298.38,270.4279,294.5349,279.4952,298.4657,275.4272,302.5337,279.358,298.38,270.4279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="33" x="301" y="490.5349">reuse</text>
+ </g>
+ <!--reverse link free to reclaimed-->
+ <g id="link_free_reclaimed">
+ <path d="M321.67,73.3179 C392.62,81.9779 562,107.9079 562,157.9679 C562,157.9679 562,157.9679 562,604.2079 C562,669.1579 480.53,691.3479 417.2,698.7779 " fill="none" id="free-backto-reclaimed" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="317.03,72.7579,325.4985,77.7863,321.9955,73.3446,326.4372,69.8416,317.03,72.7579" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="46" x="563" y="372.4649">remove</text>
+ </g>
+ <!--link declined_expired to reclaimed-->
+ <!--SRC=[TP6nJiGm343tV8Lr4mmzmOOOa93W5ZPMX52Ek7UMsgGaxW5_ZzCskPRqDZjvVhOJ9cuG6jG18R1c373rWXfkjtVRcskbz04jmsRd4JUW0zGIAEiurjGCzo0na0K-9elHhFSIvDh74EXWqGprUagAMy2VZlm_wcCwpFGJVZQKN6PAYjk5Ar65wtdCgrS1DBKJxbEcLDmLggfwoFF8lcFOYa54wNY6f3OHhHazd7H31XggaKMxMYEiHulVyDMZ_vqXiNUQNw0fBB2-4swODeh6RnWPbQldgcB6phkzPd8MElJap7crZ8vD5XFtyic5yWAt8emJvTSUm25UKLRpzLft4jsvy4B39L0AoYvlHp86xVjyBFK7]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/lease-states.uml b/doc/sphinx/uml/lease-states.uml
new file mode 100644
index 0000000..66de2bb
--- /dev/null
+++ b/doc/sphinx/uml/lease-states.uml
@@ -0,0 +1,38 @@
+@startuml
+
+title lease states (Kea 1.8.0)
+
+agent "Free" as free
+note left : not in the lease database
+
+agent "Assigned" as assigned
+
+agent "Assigned expired" as assigned_expired
+
+agent "Declined" as declined
+
+agent "Declined expired" as declined_expired
+
+agent "Expired-Reclaimed" as reclaimed
+
+free ---> assigned : assignment
+
+assigned -> assigned_expired : after valid lifetime
+assigned -> assigned : renew query
+assigned ---> declined : decline query
+assigned -up-> free : release query
+
+declined --> declined_expired : after probation period
+
+assigned_expired -up-> assigned : reuse
+declined_expired -up-> assigned : reuse
+
+assigned_expired ---> reclaimed : reclaim
+declined_expired ---> free : remove
+
+reclaimed -up-> assigned : reuse
+reclaimed -up--> free : remove
+
+declined_expired -[hidden]-> reclaimed
+
+@enduml
diff --git a/doc/sphinx/uml/main-loop.png b/doc/sphinx/uml/main-loop.png
new file mode 100644
index 0000000..8734007
--- /dev/null
+++ b/doc/sphinx/uml/main-loop.png
Binary files differ
diff --git a/doc/sphinx/uml/main-loop.svg b/doc/sphinx/uml/main-loop.svg
new file mode 100644
index 0000000..f2d095e
--- /dev/null
+++ b/doc/sphinx/uml/main-loop.svg
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="883px" preserveAspectRatio="none" style="width:498px;height:883px;background:#FFFFFF;" version="1.1" viewBox="0 0 498 883" width="498px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="235" x="124.5" y="24.9659">DHCP server main loop (Kea 1.8.0)</text>
+ <!--cluster Main Loop-->
+ <g id="cluster_Main Loop">
+ <rect fill="none" height="809.48" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="485" x="7" y="47.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="73" x="213" y="64.0339">Main Loop</text>
+ </g>
+ <!--cluster run_one-->
+ <g id="cluster_run_one">
+ <rect fill="none" height="451.13" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="395" x="65" y="154.2779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="78" x="223.5" y="171.2439">Event Loop</text>
+ </g>
+ <!--entity run-->
+ <g id="elem_run">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="147" x="113.5" y="85.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="127" x="123.5" y="110.0339">Wait for next event</text>
+ </g>
+ <!--entity poll-->
+ <g id="elem_poll">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="93" x="108.5" y="641.4079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="73" x="118.5" y="666.3739">I/O Service</text>
+ </g>
+ <!--entity ready-->
+ <g id="elem_ready">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="171" x="236.5" y="641.4079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="151" x="246.5" y="666.3739">Execute ready handler</text>
+ </g>
+ <!--entity shutdown-->
+ <g id="elem_shutdown">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="132" x="56" y="801.4779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="112" x="66" y="826.4439">Check Shutdown</text>
+ </g>
+ <!--entity signal-->
+ <g id="elem_signal">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="62" x="246" y="245.1379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="42" x="256" y="270.1039">Signal</text>
+ </g>
+ <!--entity handleSignal-->
+ <g id="elem_handleSignal">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="114" x="97" y="245.1379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="94" x="107" y="270.1039">Handle Signal</text>
+ </g>
+ <!--entity external_socket-->
+ <g id="elem_external_socket">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="122" x="97" y="344.2079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="102" x="107" y="369.1739">External Socket</text>
+ </g>
+ <!--entity handleExternalSocket-->
+ <g id="elem_handleExternalSocket">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="174" x="254" y="344.2079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="154" x="264" y="369.1739">Handle External Socket</text>
+ </g>
+ <!--entity query-->
+ <g id="elem_query">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="102" x="248" y="443.2779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="82" x="258" y="468.2439">DHCP Query</text>
+ </g>
+ <!--entity processQuery-->
+ <g id="elem_processQuery">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="116" x="97" y="443.2779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="96" x="107" y="468.2439">Process Query</text>
+ </g>
+ <!--entity timeout-->
+ <g id="elem_timeout">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="75" x="226.5" y="542.3479"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="55" x="236.5" y="567.3139">Timeout</text>
+ </g>
+ <!--link run to run_one-->
+ <g id="link_run_run_one">
+ <path d="M232,124.3879 C232,129.8504 232,135.9954 232,142.2182 C232,145.3296 232,148.4605 232,151.5352 C232,152.3039 232,153.0691 232,153.8296 C232,153.9246 232,154.0196 232,154.1145 " fill="none" id="run-to-run_one" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="232,154.1145,236,145.1145,232,149.1145,228,145.1145,232,154.1145" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="87" x="144" y="150.0849">get next event</text>
+ </g>
+ <!--link run_one to signal-->
+ <g id="link_run_one_signal">
+ <path d="M232.06,185.5479 C233.31,185.5479 253.25,185.5479 253.25,185.5479 C253.25,185.5479 253.25,238.1979 253.25,238.1979 " fill="none" id="run_one-to-signal" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="253.25,243.1979,257.25,234.1979,253.25,238.1979,249.25,234.1979,253.25,243.1979" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link signal to handleSignal-->
+ <g id="link_signal_handleSignal">
+ <path d="M245.64,258.5479 C245.64,258.5479 217.64,258.5479 217.64,258.5479 " fill="none" id="signal-to-handleSignal" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="212.64,258.5479,221.64,262.5479,217.64,258.5479,221.64,254.5479,212.64,258.5479" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="59" x="186.89" y="273.4449">got signal</text>
+ </g>
+ <!--link handleSignal to poll-->
+ <g id="link_handleSignal_poll">
+ <path d="M96.86,265.5479 C85.25,265.5479 76.5,265.5479 76.5,265.5479 C76.5,265.5479 76.5,661.5479 76.5,661.5479 C76.5,661.5479 101.92,661.5479 101.92,661.5479 " fill="none" id="handleSignal-to-poll" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="106.92,661.5479,97.92,657.5479,101.92,661.5479,97.92,665.5479,106.92,661.5479" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link signal to external_socket-->
+ <g id="link_signal_external_socket">
+ <path d="M245.64,271.5479 C230.26,271.5479 215,271.5479 215,271.5479 C215,271.5479 215,337.2279 215,337.2279 " fill="none" id="signal-to-external_socket" style="stroke:#181818;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/>
+ <polygon fill="#181818" points="215,342.2279,219,333.2279,215,337.2279,211,333.2279,215,342.2279" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link external_socket to handleExternalSocket-->
+ <g id="link_external_socket_handleExternalSocket">
+ <path d="M219.45,354.5479 C219.45,354.5479 247.17,354.5479 247.17,354.5479 " fill="none" id="external_socket-to-handleExternalSocket" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="252.17,354.5479,243.17,350.5479,247.17,354.5479,243.17,358.5479,252.17,354.5479" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="128" x="234.31" y="350.4449">external socket ready</text>
+ </g>
+ <!--link handleExternalSocket to poll-->
+ <g id="link_handleExternalSocket_poll">
+ <path d="M253.51,373.5479 C235.94,373.5479 222.75,373.5479 222.75,373.5479 C222.75,373.5479 222.75,651.5479 222.75,651.5479 C222.75,651.5479 208.2,651.5479 208.2,651.5479 " fill="none" id="handleExternalSocket-to-poll" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="203.2,651.5479,212.2,655.5479,208.2,651.5479,212.2,647.5479,203.2,651.5479" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link external_socket to query-->
+ <g id="link_external_socket_query">
+ <path d="M219.4,364.5479 C222.66,364.5479 224.62,364.5479 224.62,364.5479 C224.62,364.5479 224.62,456.5479 224.62,456.5479 C224.62,456.5479 241.24,456.5479 241.24,456.5479 " fill="none" id="external_socket-to-query" style="stroke:#181818;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/>
+ <polygon fill="#181818" points="246.24,456.5479,237.24,452.5479,241.24,456.5479,237.24,460.5479,246.24,456.5479" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link query to processQuery-->
+ <g id="link_query_processQuery">
+ <path d="M247.83,469.5479 C247.83,469.5479 219.89,469.5479 219.89,469.5479 " fill="none" id="query-to-processQuery" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="214.89,469.5479,223.89,473.5479,219.89,469.5479,223.89,465.5479,214.89,469.5479" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="114" x="176.86" y="484.4449">DHCP socket ready</text>
+ </g>
+ <!--link processQuery to poll-->
+ <g id="link_processQuery_poll">
+ <path d="M155,482.5279 C155,482.5279 155,634.4979 155,634.4979 " fill="none" id="processQuery-to-poll" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="155,639.4979,159,630.4979,155,634.4979,151,630.4979,155,639.4979" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link query to timeout-->
+ <g id="link_query_timeout">
+ <path d="M274.75,482.3779 C274.75,482.3779 274.75,535.4979 274.75,535.4979 " fill="none" id="query-to-timeout" style="stroke:#181818;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/>
+ <polygon fill="#181818" points="274.75,540.4979,278.75,531.4979,274.75,535.4979,270.75,531.4979,274.75,540.4979" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link timeout to poll-->
+ <g id="link_timeout_poll">
+ <path d="M229.83,581.5679 C229.83,610.6179 229.83,661.5479 229.83,661.5479 C229.83,661.5479 208.48,661.5479 208.48,661.5479 " fill="none" id="timeout-to-poll" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="203.48,661.5479,212.48,665.5479,208.48,661.5479,212.48,657.5479,203.48,661.5479" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="96" x="132.83" y="628.1349">timeout expired</text>
+ </g>
+ <!--link poll to ready-->
+ <g id="link_poll_ready">
+ <path d="M201.96,671.5479 C201.96,671.5479 229.64,671.5479 229.64,671.5479 " fill="none" id="poll-to-ready" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="234.64,671.5479,225.64,667.5479,229.64,671.5479,225.64,675.5479,234.64,671.5479" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="83" x="174.3" y="686.4449">handler ready</text>
+ </g>
+ <!--link poll to shutdown-->
+ <g id="link_poll_shutdown">
+ <path d="M148.25,680.8979 C148.25,680.8979 148.25,794.5479 148.25,794.5479 " fill="none" id="poll-to-shutdown" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="148.25,799.5479,152.25,790.5479,148.25,794.5479,144.25,790.5479,148.25,799.5479" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="102" x="45.25" y="733.6249">no ready handler</text>
+ </g>
+ <!--link ready to ready-->
+ <g id="link_ready_ready">
+ <path d="M269,641.1379 C269,627.3979 269,611.5479 269,611.5479 C269,611.5479 233.17,611.5479 233.17,611.5479 C233.17,611.5479 233.17,741.5479 233.17,741.5479 C233.17,741.5479 322,741.5479 322,741.5479 C322,741.5479 322,687.3879 322,687.3879 " fill="none" id="ready-to-ready" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="322,682.3879,318,691.3879,322,687.3879,326,691.3879,322,682.3879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="133" x="239.17" y="735.2349">execute ready handler</text>
+ </g>
+ <!--reverse link run to shutdown-->
+ <g id="link_run_shutdown">
+ <path d="M220.87,131.0479 C220.87,131.0479 220.87,821.5479 220.87,821.5479 C220.87,821.5479 206.39,821.5479 188.5,821.5479 " fill="none" id="run-backto-shutdown" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="220.88,126.0479,216.88,135.0479,220.88,131.0479,224.88,135.0479,220.88,126.0479" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <text fill="#888888" font-family="sans-serif" font-size="10" lengthAdjust="spacing" textLength="139" x="172.5" y="872.2379">dashed arrow means priority</text>
+ <!--SRC=[VLF1ZjCm4BtFLunwWaDtuORsm6LPQLU0M5Ga3WYjh6GsiPfwWkFijaByEsEF7TnIk3JncpVlJVocmOQ1yDtjzJrEv9_8uyvOXmFpY2_Uay7N5sykNhq4cBRMZSQR7GxMKJYCXEn3pm2Ucc1S9pAhZx7tW_Iky2UWwSW5N7qrDk0ZUtIq3qXF0gxGJEXd1u1LzqqivNQjFx2Zg8I1EmgzJ1SFHNXZEsU6n9tIArJ5M-DQ4QquVK8K4E8l-9VkpJwG5n9kkDcI3aiPUvWITiRfN9TQbeeM_ckTWlWyap-a_X_nhVQwzzpGD5MKKP445Ed2_c9tnBEE4lGT4kF8EvFlBZ_XHbR0DgJ2FAGBNIw5cZcGv6NQG_umhpb5H7KpzxgdPekRVWujFpiDA1z0Q18ihjTlbu2lKAQfjWDA8P9oilh0jRTT7mIkmum270hpg18LqeUK_c-jcNfglmj-6kd9-TJhE4NnB7pCVBs7i-HbZByygdaqxFJxnxkELpprZrhx7H4MdwnIgPVyytEXYcY1Q3zQJoqaEEQJehugOHV3L4tHv4o5ulXuCK1FbGYTsnzO9DQpBWJ08xFS4UhaQBpdPzoHSN7LBNiR3l0R]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/main-loop.uml b/doc/sphinx/uml/main-loop.uml
new file mode 100644
index 0000000..9e29c9f
--- /dev/null
+++ b/doc/sphinx/uml/main-loop.uml
@@ -0,0 +1,60 @@
+@startuml
+
+title DHCP server main loop (Kea 1.8.0)
+
+skinparam linetype ortho
+
+rectangle "Main Loop" {
+ agent "Wait for next event" as run
+
+ rectangle "Event Loop" as run_one {
+ together {
+ agent "Signal" as signal
+
+ agent "Handle Signal" as handleSignal
+ }
+
+ together {
+ agent "External Socket" as external_socket
+
+ agent "Handle External Socket" as handleExternalSocket
+ }
+
+ together {
+ agent "DHCP Query" as query
+
+ agent "Process Query" as processQuery
+ }
+
+ agent "Timeout" as timeout
+ }
+
+ together {
+ agent "I/O Service" as poll
+
+ agent "Execute ready handler" as ready
+ }
+
+ agent "Check Shutdown" as shutdown
+}
+
+run --> run_one : get next event
+run_one --> signal
+signal -right-> handleSignal : got signal
+handleSignal --> poll
+signal -[dashed]-> external_socket
+external_socket -right-> handleExternalSocket : external socket ready
+handleExternalSocket --> poll
+external_socket -[dashed]-> query
+query -right-> processQuery : DHCP socket ready
+processQuery --> poll
+query -[dashed]-> timeout
+timeout --> poll : timeout expired
+poll -> ready : handler ready
+poll ---> shutdown : no ready handler
+ready -> ready : execute ready handler
+shutdown -u-> run
+
+footer dashed arrow means priority
+
+@enduml \ No newline at end of file
diff --git a/doc/sphinx/uml/option-data-priority.atxt b/doc/sphinx/uml/option-data-priority.atxt
new file mode 100644
index 0000000..cd02dbc
--- /dev/null
+++ b/doc/sphinx/uml/option-data-priority.atxt
@@ -0,0 +1,53 @@
+ Option Precedence
+
+ | _ _ _
+ | / \ / \ / \
+Globals | | | | | | |
+ | \_/ \_/ \_/
+ | |
+ | _ _ |
+ | / \ / \ |
+Class-N | | | | | |
+ | \_/ \_/ |
+[More classes | | |
+in reverse | | |
+order of | | |
+definition] | _ | |
+ | / \ | |
+Class-1 | | | | |
+ | \_/ | |
+ | | | |
+ | _ _ | _ | |
+ | / \ / \ | / \ | |
+Network | | | | | | | | | |
+ | \_/ \_/ | \_/ | |
+ | | | | |
+ | _ | _ | | | _
+ | / \ | / \ | | | / \
+Subnet | | | | | | | | | | |
+ | \_/ | \_/ | | | \_/
+ | | | | | | | |
+ | | _ | | _ | | | |
+ | | / \ | | / \ | | | |
+Pool | | | | | | | | | | | |
+ | | \_/ | | \_/ | | | |
+ | | | | | | | | |
+ | | _ | | | | | | | _
+ | | / \ | | | | | | /|\ / \
+Global Host | | | | | | | | | | | | | | |
+ | | \_/ | | | | | | \|/ \_/
+ | | | | | | | | |
+ | | _ | | | | | | |
+ | | / \ | | | | | | |
+Subnet Host | | | | | | | | | | |
+ | | \_/ | | | | | | |
+ | | | | | | | | | |
+----------------+----+------+------+------+------+------+------+------+------+------+
+ | | | | | | | | | |
+ | V V V V V V V V V
+ | _ _ _ _ _ _ _ _ _
+ | / \ / \ / \ / \ / \ / \ / \ / \ / \
+DHCP Response | | | | | | | | | | | | | | | | | | |
+ | \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
+
+* This diagram was manually created and does not have a UML source.
diff --git a/doc/sphinx/uml/packet4.png b/doc/sphinx/uml/packet4.png
new file mode 100644
index 0000000..6fef4e2
--- /dev/null
+++ b/doc/sphinx/uml/packet4.png
Binary files differ
diff --git a/doc/sphinx/uml/packet4.svg b/doc/sphinx/uml/packet4.svg
new file mode 100644
index 0000000..0badab5
--- /dev/null
+++ b/doc/sphinx/uml/packet4.svg
@@ -0,0 +1,355 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="1930px" preserveAspectRatio="none" style="width:1791px;height:1930px;background:#FFFFFF;" version="1.1" viewBox="0 0 1791 1930" width="1791px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="260" x="758.5" y="24.9659">DHCPv4 packet processing (Kea 1.8.0)</text>
+ <!--cluster process-->
+ <g id="cluster_process">
+ <rect fill="none" height="109.06" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="842" x="680" y="975.6179"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="243" x="979.5" y="992.5839">Process Query on its Message Type</text>
+ </g>
+ <!--entity processDiscover-->
+ <g id="elem_processDiscover">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="132" x="1050" y="1021.6179"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="112" x="1060" y="1046.5839">Process Discover</text>
+ </g>
+ <!--entity processRequest-->
+ <g id="elem_processRequest">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="130" x="723" y="1021.6179"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="110" x="733" y="1046.5839">Process Request</text>
+ </g>
+ <!--entity processRelease-->
+ <g id="elem_processRelease">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="127" x="888.5" y="1021.6179"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="107" x="898.5" y="1046.5839">Process Release</text>
+ </g>
+ <!--entity processDecline-->
+ <g id="elem_processDecline">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="125" x="1216.5" y="1021.6179"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="105" x="1226.5" y="1046.5839">Process Decline</text>
+ </g>
+ <!--entity processInform-->
+ <g id="elem_processInform">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="121" x="1376.5" y="1021.6179"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="101" x="1386.5" y="1046.5839">Process Inform</text>
+ </g>
+ <!--entity receivePacket-->
+ <g id="elem_receivePacket">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="114" x="1505" y="47.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="94" x="1515" y="72.0339">Receive query</text>
+ </g>
+ <g id="elem_GMN3">
+ <path d="M1417.5,52.7479 L1417.5,80.4539 A0,0 0 0 0 1417.5,80.4539 L1470.5,80.4539 A0,0 0 0 0 1470.5,80.4539 L1470.5,70.7479 L1504.6,66.6079 L1470.5,62.7479 L1470.5,62.7479 L1460.5,52.7479 L1417.5,52.7479 A0,0 0 0 0 1417.5,52.7479 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M1460.5,52.7479 L1460.5,62.7479 L1470.5,62.7479 L1460.5,52.7479 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="32" x="1423.5" y="71.6449">input</text>
+ </g>
+ <!--entity isServiceEnabled-->
+ <g id="elem_isServiceEnabled">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="127" x="1498.5" y="147.1379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="107" x="1508.5" y="172.1039">Service Enabled</text>
+ </g>
+ <!--entity buffer4_receive-->
+ <g id="elem_buffer4_receive">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="175" x="974.5" y="265.2079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="155" x="984.5" y="290.1739">Callout buffer4_receive</text>
+ </g>
+ <g id="elem_GMN8">
+ <path d="M1185,270.8879 L1185,280.7379 L1149.89,284.7379 L1185,288.7379 L1185,298.5939 A0,0 0 0 0 1185,298.5939 L1237,298.5939 A0,0 0 0 0 1237,298.5939 L1237,280.8879 L1227,270.8879 L1185,270.8879 A0,0 0 0 0 1185,270.8879 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M1227,270.8879 L1227,280.8879 L1237,280.8879 L1227,270.8879 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="31" x="1191" y="289.7849">hook</text>
+ </g>
+ <!--entity unpack-->
+ <g id="elem_unpack">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="113" x="630.5" y="383.2779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="93" x="640.5" y="408.2439">Unpack query</text>
+ </g>
+ <!--entity classify-->
+ <g id="elem_classify">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="114" x="630" y="483.3379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="94" x="640" y="508.3039">Classify query</text>
+ </g>
+ <!--entity pkt4_receive-->
+ <g id="elem_pkt4_receive">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="154" x="459" y="583.4079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="134" x="469" y="608.3739">Callout pkt4_receive</text>
+ </g>
+ <g id="elem_GMN14">
+ <path d="M648,589.0879 L648,598.9479 L613.36,602.9479 L648,606.9479 L648,616.7939 A0,0 0 0 0 648,616.7939 L700,616.7939 A0,0 0 0 0 700,616.7939 L700,599.0879 L690,589.0879 L648,589.0879 A0,0 0 0 0 648,589.0879 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M690,589.0879 L690,599.0879 L700,599.0879 L690,589.0879 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="31" x="654" y="607.9849">hook</text>
+ </g>
+ <!--entity drop_class-->
+ <g id="elem_drop_class">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="139" x="245.5" y="701.4779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="119" x="255.5" y="726.4439">Check DROP class</text>
+ </g>
+ <!--entity same_client-->
+ <g id="elem_same_client">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="336" x="147" y="801.5479"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="316" x="157" y="826.5139">Avoid same client race in multi-threaded mode</text>
+ </g>
+ <g id="elem_GMN19">
+ <path d="M588.5,807.2279 L588.5,817.0779 L483.47,821.0779 L588.5,825.0779 L588.5,834.9339 A0,0 0 0 0 588.5,834.9339 L783.5,834.9339 A0,0 0 0 0 783.5,834.9339 L783.5,817.2279 L773.5,807.2279 L588.5,807.2279 A0,0 0 0 0 588.5,807.2279 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M773.5,807.2279 L773.5,817.2279 L783.5,817.2279 L773.5,807.2279 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="174" x="594.5" y="826.1249">postpone processing or drop</text>
+ </g>
+ <!--entity leases4_committed-->
+ <g id="elem_leases4_committed">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="199" x="991.5" y="1157.6779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="179" x="1001.5" y="1182.6439">Callout leases4_committed</text>
+ </g>
+ <g id="elem_GMN29">
+ <path d="M1225,1163.3679 L1225,1173.2179 L1191,1177.2179 L1225,1181.2179 L1225,1191.0739 A0,0 0 0 0 1225,1191.0739 L1277,1191.0739 A0,0 0 0 0 1277,1191.0739 L1277,1173.3679 L1267,1163.3679 L1225,1163.3679 A0,0 0 0 0 1225,1163.3679 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M1267,1163.3679 L1267,1173.3679 L1277,1173.3679 L1267,1163.3679 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="31" x="1231" y="1182.2649">hook</text>
+ </g>
+ <!--entity park-->
+ <g id="elem_park">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="49" x="1130.5" y="1275.7479"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="29" x="1140.5" y="1300.7139">Park</text>
+ </g>
+ <!--entity pkt4_send-->
+ <g id="elem_pkt4_send">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="139" x="991.5" y="1393.8179"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="119" x="1001.5" y="1418.7839">Callout pkt4_send</text>
+ </g>
+ <!--entity send-->
+ <g id="elem_send">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="120" x="1000" y="1730.0179"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="100" x="1010" y="1754.9839">Send response</text>
+ </g>
+ <!--entity pack-->
+ <g id="elem_pack">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="116" x="1003" y="1511.8879"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="96" x="1013" y="1536.8539">Pack response</text>
+ </g>
+ <!--entity buffer4_send-->
+ <g id="elem_buffer4_send">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="160" x="953" y="1611.9579"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="140" x="963" y="1636.9239">Callout buffer4_send</text>
+ </g>
+ <g id="elem_GMN37">
+ <path d="M855.5,1617.6379 L855.5,1645.3439 A0,0 0 0 0 855.5,1645.3439 L918.5,1645.3439 A0,0 0 0 0 918.5,1645.3439 L918.5,1635.6379 L952.8,1631.4879 L918.5,1627.6379 L918.5,1627.6379 L908.5,1617.6379 L855.5,1617.6379 A0,0 0 0 0 855.5,1617.6379 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M908.5,1617.6379 L908.5,1627.6379 L918.5,1627.6379 L908.5,1617.6379 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="42" x="861.5" y="1636.5349">output</text>
+ </g>
+ <!--entity drop-->
+ <g id="elem_drop">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="101" x="920.5" y="1830.0879"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="81" x="930.5" y="1855.0539">Drop packet</text>
+ </g>
+ <g id="elem_GMN41">
+ <path d="M834.5,1835.7679 L834.5,1863.4739 A0,0 0 0 0 834.5,1863.4739 L885.5,1863.4739 A0,0 0 0 0 885.5,1863.4739 L885.5,1853.7679 L920.28,1849.6279 L885.5,1845.7679 L885.5,1845.7679 L875.5,1835.7679 L834.5,1835.7679 A0,0 0 0 0 834.5,1835.7679 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M875.5,1835.7679 L875.5,1845.7679 L885.5,1845.7679 L875.5,1835.7679 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="30" x="840.5" y="1854.6649">error</text>
+ </g>
+ <!--link receivePacket to isServiceEnabled-->
+ <g id="link_receivePacket_isServiceEnabled">
+ <path d="M1562,86.5179 C1562,101.8479 1562,123.5879 1562,140.4079 " fill="none" id="receivePacket-to-isServiceEnabled" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1562,145.3079,1566,136.3079,1562,140.3079,1558,136.3079,1562,145.3079" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link isServiceEnabled to buffer4_receive-->
+ <g id="link_isServiceEnabled_buffer4_receive">
+ <path d="M1498.17,182.4879 C1409.72,203.0279 1249.54,240.2079 1149.86,263.3479 " fill="none" id="isServiceEnabled-to-buffer4_receive" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1145.15,264.4379,1154.8218,266.2972,1150.0203,263.3063,1153.0112,258.5048,1145.15,264.4379" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="104" x="1342" y="231.1049">service is enabled</text>
+ </g>
+ <!--link isServiceEnabled to drop-->
+ <g id="link_isServiceEnabled_drop">
+ <path d="M1600.28,186.6079 C1633.4,206.0079 1676,239.5379 1676,283.7379 C1676,283.7379 1676,283.7379 1676,1750.5579 C1676,1815.7879 1196.34,1840.1279 1028.31,1846.6579 " fill="none" id="isServiceEnabled-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1023.5,1846.8379,1032.6423,1850.5011,1028.4966,1846.6523,1032.3453,1842.5066,1023.5,1846.8379" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="106" x="1677" y="947.5149">service is disabled</text>
+ </g>
+ <!--link buffer4_receive to unpack-->
+ <g id="link_buffer4_receive_unpack">
+ <path d="M1000.92,304.6479 C931.81,326.0379 819.84,360.6979 749.97,382.3179 " fill="none" id="buffer4_receive-to-unpack" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="745.2,383.7979,754.9823,384.9414,749.9739,382.3114,752.6039,377.3031,745.2,383.7979" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="66" x="897" y="349.1749">CONTINUE</text>
+ </g>
+ <!--link buffer4_receive to classify-->
+ <g id="link_buffer4_receive_classify">
+ <path d="M1039.06,304.5979 C1021.3,318.7879 995.8,338.2779 972,353.2779 C891.44,404.0279 791.65,453.1479 734.06,480.2579 " fill="none" id="buffer4_receive-to-classify" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="729.65,482.3279,739.4967,482.1195,734.1751,480.201,736.0936,474.8794,729.65,482.3279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="27" x="922" y="408.2049">SKIP</text>
+ </g>
+ <!--link buffer4_receive to drop-->
+ <g id="link_buffer4_receive_drop">
+ <path d="M1149.71,302.9379 C1298.88,332.4779 1586,390.7779 1586,401.8079 C1586,401.8079 1586,401.8079 1586,1750.5579 C1586,1806.7379 1180.52,1836.3379 1028.04,1845.4879 " fill="none" id="buffer4_receive-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1023.36,1845.7679,1032.5834,1849.2217,1028.351,1845.4685,1032.1043,1841.2361,1023.36,1845.7679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="35" x="1587" y="1046.5449">DROP</text>
+ </g>
+ <!--link unpack to classify-->
+ <g id="link_unpack_classify">
+ <path d="M687,422.7179 C687,438.0479 687,459.7979 687,476.6179 " fill="none" id="unpack-to-classify" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="687,481.5079,691,472.5079,687,476.5079,683,472.5079,687,481.5079" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link unpack to drop-->
+ <g id="link_unpack_drop">
+ <path d="M630.11,406.0479 C466.44,413.2279 6,439.0179 6,501.8779 C6,501.8779 6,501.8779 6,1750.5579 C6,1796.6079 52.59,1787.9079 97,1800.0879 C269.84,1847.5279 725.82,1803.0179 903,1830.0879 C906.56,1830.6379 910.21,1831.2979 913.87,1832.0479 " fill="none" id="unpack-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="918.55,1833.0679,910.6157,1827.2329,913.6661,1831.9969,908.9021,1835.0472,918.55,1833.0679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49" x="7" y="1123.5749">on error</text>
+ </g>
+ <!--link classify to pkt4_receive-->
+ <g id="link_classify_pkt4_receive">
+ <path d="M657.89,522.7879 C632.9,539.0179 596.84,562.4279 570.43,579.5879 " fill="none" id="classify-to-pkt4_receive" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="566.55,582.0979,576.2763,580.5491,570.7429,579.374,571.918,573.8405,566.55,582.0979" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link pkt4_receive to drop_class-->
+ <g id="link_pkt4_receive_drop_class">
+ <path d="M498.89,622.9779 C481.58,631.8879 460.71,642.6779 442,652.4779 C413.11,667.6179 380.64,684.8879 355.76,698.1779 " fill="none" id="pkt4_receive-to-drop_class" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="351.58,700.4079,361.4032,699.6971,355.9906,698.0528,357.6349,692.6402,351.58,700.4079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="66" x="443" y="667.3749">CONTINUE</text>
+ </g>
+ <!--link pkt4_receive to drop-->
+ <g id="link_pkt4_receive_drop">
+ <path d="M536,622.8379 C536,645.8179 536,685.7579 536,720.0079 C536,720.0079 536,720.0079 536,1750.5579 C536,1860.5579 649.96,1782.9379 903,1830.0879 C906.62,1830.7679 910.33,1831.5379 914.06,1832.3779 " fill="none" id="pkt4_receive-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="918.87,1833.4979,911.005,1827.5699,913.999,1832.3694,909.1994,1835.3635,918.87,1833.4979" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="35" x="537" y="1241.6449">DROP</text>
+ </g>
+ <!--link drop_class to same_client-->
+ <g id="link_drop_class_same_client">
+ <path d="M315,740.9179 C315,756.2479 315,777.9979 315,794.8179 " fill="none" id="drop_class-to-same_client" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="315,799.7179,319,790.7179,315,794.7179,311,790.7179,315,799.7179" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link drop_class to drop-->
+ <g id="link_drop_class_drop">
+ <path d="M245.07,724.7079 C180.33,731.3979 94,752.9179 94,820.0779 C94,820.0779 94,820.0779 94,1750.5579 C94,1903.2579 282.12,1784.1979 434,1800.0879 C641.74,1821.8279 696.97,1795.7879 903,1830.0879 C906.56,1830.6779 910.2,1831.3879 913.85,1832.1579 " fill="none" id="drop_class-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="918.53,1833.2079,910.612,1827.3508,913.6491,1832.1233,908.8766,1835.1603,918.53,1833.2079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="118" x="95" y="1300.6849">query in DROP class</text>
+ </g>
+ <!--link same_client to process-->
+ <g id="link_same_client_process">
+ <path d="M347.45,841.0479 C392.47,867.3679 476.635,916.5729 550.025,959.4792 C586.72,980.9323 620.7213,1000.8107 645.7888,1015.4665 C658.3225,1022.7944 668.6228,1028.8166 675.9097,1033.0772 C676.8205,1033.6098 677.6843,1034.1148 678.4995,1034.5914 C678.9071,1034.8298 679.3025,1035.061 679.6856,1035.285 " fill="none" id="same_client-to-process" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="679.6856,1035.285,673.9353,1027.2891,675.3693,1032.7612,669.8972,1034.1952,679.6856,1035.285" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link same_client to drop-->
+ <g id="link_same_client_drop">
+ <path d="M295.48,841.0979 C282.19,856.1279 267,878.3879 267,901.6179 C267,901.6179 267,901.6179 267,1237.2479 C267,1293.6179 257.26,1307.4779 259,1363.8179 C259.67,1385.4179 262,1390.7379 262,1412.3479 C262,1412.3479 262,1412.3479 262,1750.5579 C262,1862.9579 398.65,1784.7779 510,1800.0879 C683.54,1823.9579 730.38,1800.2579 903,1830.0879 C906.63,1830.7179 910.35,1831.4579 914.08,1832.2679 " fill="none" id="same_client-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="918.89,1833.3579,910.9884,1827.4788,914.0121,1832.2597,909.2312,1835.2834,918.89,1833.3579" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="249" x="260" y="1359.7149">queries from the same client possible race</text>
+ </g>
+ <!--link process to drop-->
+ <g id="link_process_drop">
+ <path d="M679.8577,1049.157 C679.7594,1049.2455 679.6604,1049.3349 679.5607,1049.4249 C677.9649,1050.8664 676.1853,1052.5069 674.2595,1054.3326 C666.5566,1061.6354 656.5156,1071.9017 646.5513,1084.2479 C626.6225,1108.9404 607,1141.9529 607,1176.2179 C607,1176.2179 607,1176.2179 607,1355.3179 C607,1455.5979 607,1480.6679 607,1580.9579 C607,1580.9579 607,1580.9579 607,1750.5579 C607,1886.7779 771.51,1794.5079 903,1830.0879 C906.56,1831.0479 910.22,1832.0479 913.91,1833.0479 " fill="none" id="process-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="918.67,1834.3379,911.0365,1828.1146,913.8455,1833.0247,908.9354,1835.8338,918.67,1834.3379" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="143" x="608" y="1477.7849">unknown message type</text>
+ </g>
+ <!--link processDiscover to leases4_committed-->
+ <g id="link_processDiscover_leases4_committed">
+ <path d="M1112.47,1061.0679 C1108.08,1084.6279 1100.56,1124.9379 1095.69,1151.0679 " fill="none" id="processDiscover-to-leases4_committed" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1094.79,1155.8979,1100.3645,1147.7785,1095.702,1150.9818,1092.4987,1146.3193,1094.79,1155.8979" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link processDiscover to drop-->
+ <g id="link_processDiscover_drop">
+ <path d="M1141.38,1060.9579 C1157.61,1072.7479 1179.24,1088.0579 1199,1100.6779 C1255.63,1136.8679 1330,1109.0079 1330,1176.2179 C1330,1176.2179 1330,1176.2179 1330,1414.3479 C1330,1435.9679 1327.67,1441.2879 1327,1462.8879 C1326.74,1471.3279 1326.04,1473.4979 1327,1481.8879 C1332.14,1526.7979 1350,1535.7579 1350,1580.9579 C1350,1580.9579 1350,1580.9579 1350,1750.5579 C1350,1816.2779 1133.7,1838.6679 1027.91,1845.6879 " fill="none" id="processDiscover-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1023.23,1845.9879,1032.4632,1849.4154,1028.2202,1845.6743,1031.9613,1841.4312,1023.23,1845.9879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49" x="1328" y="1477.7849">on error</text>
+ </g>
+ <!--link processRequest to leases4_committed-->
+ <g id="link_processRequest_leases4_committed">
+ <path d="M810.84,1060.9379 C826.91,1073.4679 849.33,1089.6079 871,1100.6779 C916.25,1123.8079 970.17,1142.4479 1013.3,1155.4279 " fill="none" id="processRequest-to-leases4_committed" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1017.86,1156.7879,1010.3749,1150.3868,1013.0677,1155.3616,1008.0929,1158.0544,1017.86,1156.7879" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link processRequest to drop-->
+ <g id="link_processRequest_drop">
+ <path d="M785.96,1060.7779 C783.3,1086.6379 779,1134.9279 779,1176.2179 C779,1176.2179 779,1176.2179 779,1414.3479 C779,1576.7879 769.86,1638.8979 867,1769.0879 C884.42,1792.4379 910.46,1812.3379 932.12,1826.3879 " fill="none" id="processRequest-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="936.25,1829.0279,930.8099,1820.8178,932.0335,1826.3408,926.5105,1827.5643,936.25,1829.0279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49" x="779" y="1477.7849">on error</text>
+ </g>
+ <!--link processRelease to leases4_committed-->
+ <g id="link_processRelease_leases4_committed">
+ <path d="M971.62,1061.0679 C996.66,1085.2279 1039.97,1126.9979 1066.96,1153.0379 " fill="none" id="processRelease-to-leases4_committed" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1070.39,1156.3379,1066.6879,1147.2113,1066.7908,1152.8672,1061.1349,1152.9701,1070.39,1156.3379" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link processRelease to drop-->
+ <g id="link_processRelease_drop">
+ <path d="M931.31,1060.8179 C907.02,1084.8979 870,1129.2879 870,1176.2179 C870,1176.2179 870,1176.2179 870,1414.3479 C870,1520.4979 807.02,1549.4979 838,1651.0179 C859.66,1721.9979 916.3,1790.5579 948.24,1825.2179 " fill="none" id="processRelease-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="951.36,1828.5679,948.1796,1819.2467,947.9627,1824.8994,942.31,1824.6824,951.36,1828.5679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49" x="866" y="1477.7849">on error</text>
+ </g>
+ <!--link processDecline to leases4_committed-->
+ <g id="link_processDecline_leases4_committed">
+ <path d="M1253.52,1061.0079 C1237.85,1072.5479 1217.34,1087.5679 1199,1100.6779 C1173.78,1118.7279 1145,1138.8279 1123.49,1153.7579 " fill="none" id="processDecline-to-leases4_committed" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1119.43,1156.5779,1129.1039,1154.7298,1123.5368,1153.7259,1124.5407,1148.1589,1119.43,1156.5779" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link processDecline to drop-->
+ <g id="link_processDecline_drop">
+ <path d="M1302.45,1061.0779 C1342.78,1094.0579 1421,1160.1079 1421,1176.2179 C1421,1176.2179 1421,1176.2179 1421,1750.5579 C1421,1830.0879 1149.24,1845.2379 1028.1,1848.0379 " fill="none" id="processDecline-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1023.26,1848.1379,1032.3491,1851.9312,1028.2587,1848.0237,1032.1662,1843.9333,1023.26,1848.1379" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49" x="1422" y="1477.7849">on error</text>
+ </g>
+ <!--link processInform to leases4_committed-->
+ <g id="link_processInform_leases4_committed">
+ <path d="M1416.23,1061.1179 C1401.28,1073.8879 1380.1,1090.2479 1359,1100.6779 C1308.2,1125.8079 1247.72,1143.7579 1197.09,1155.8379 " fill="none" id="processInform-to-leases4_committed" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1192.44,1156.9379,1202.1169,1158.7702,1197.3071,1155.7927,1200.2846,1150.9829,1192.44,1156.9379" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link processInform to drop-->
+ <g id="link_processInform_drop">
+ <path d="M1455.93,1061.1579 C1478.14,1085.5879 1512,1130.3479 1512,1176.2179 C1512,1176.2179 1512,1176.2179 1512,1750.5579 C1512,1804.9879 1452.54,1785.8579 1400,1800.0879 C1270.09,1835.2779 1111.35,1844.9779 1028.14,1847.6379 " fill="none" id="processInform-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1023.31,1847.7879,1032.4312,1851.5032,1028.3075,1847.6309,1032.1799,1843.5072,1023.31,1847.7879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49" x="1513" y="1477.7849">on error</text>
+ </g>
+ <!--link leases4_committed to pkt4_send-->
+ <g id="link_leases4_committed_pkt4_send">
+ <path d="M1078.99,1197.0979 C1067.68,1216.1179 1051.51,1246.6979 1045,1275.7479 C1036.36,1314.3079 1045.58,1360.0979 1053.17,1387.6579 " fill="none" id="leases4_committed-to-pkt4_send" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1054.45,1392.1279,1055.8505,1382.3791,1053.0896,1387.3165,1048.1522,1384.5557,1054.45,1392.1279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="66" x="1046" y="1300.6849">CONTINUE</text>
+ </g>
+ <!--link leases4_committed to drop-->
+ <g id="link_leases4_committed_drop">
+ <path d="M1128.87,1197.2279 C1142.26,1205.1379 1156.76,1215.1979 1168,1226.7479 C1185.64,1244.8979 1188.99,1251.7479 1197,1275.7479 C1250.04,1434.7279 1272.49,1487.9079 1234,1651.0179 C1215.74,1728.4279 1205.78,1763.8179 1135,1800.0879 C1101.17,1817.4279 1060.16,1829.5579 1027.61,1837.3579 " fill="none" id="leases4_committed-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1023.11,1838.4179,1032.7857,1840.2566,1027.9778,1837.2759,1030.9585,1832.468,1023.11,1838.4179" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="35" x="1254" y="1536.8149">DROP</text>
+ </g>
+ <!--link leases4_committed to park-->
+ <g id="link_leases4_committed_park">
+ <path d="M1101.43,1197.1279 C1112.35,1216.9379 1129.55,1248.1379 1141.51,1269.8179 " fill="none" id="leases4_committed-to-park" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1143.85,1274.0579,1143.0139,1264.2446,1141.4388,1269.6777,1136.0056,1268.1026,1143.85,1274.0579" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="32" x="1127" y="1241.6449">PARK</text>
+ </g>
+ <!--link park to pkt4_send-->
+ <g id="link_park_pkt4_send">
+ <path d="M1139.69,1315.1879 C1123.51,1335.1679 1097.94,1366.7379 1080.37,1388.4379 " fill="none" id="park-to-pkt4_send" style="stroke:#181818;stroke-width:1.0;stroke-dasharray:7.0,7.0;"/>
+ <polygon fill="#181818" points="1077.26,1392.2779,1086.0398,1387.8153,1080.413,1388.3973,1079.8309,1382.7705,1077.26,1392.2779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="43" x="1114" y="1359.7149">unpark</text>
+ </g>
+ <!--link pkt4_send to pack-->
+ <g id="link_pkt4_send_pack">
+ <path d="M1061,1433.2579 C1061,1452.8179 1061,1483.4779 1061,1505.1179 " fill="none" id="pkt4_send-to-pack" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1061,1510.0079,1065,1501.0079,1061,1505.0079,1057,1501.0079,1061,1510.0079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="66" x="1062" y="1477.7849">CONTINUE</text>
+ </g>
+ <!--link pkt4_send to buffer4_send-->
+ <g id="link_pkt4_send_buffer4_send">
+ <path d="M1029.86,1433.1479 C1004.93,1450.1879 971.76,1477.8179 957,1511.8879 C941.54,1547.5879 974.65,1584.7179 1001.93,1607.7179 " fill="none" id="pkt4_send-to-buffer4_send" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1005.77,1610.8879,1001.3586,1602.0823,1001.9079,1607.7124,996.2777,1608.2617,1005.77,1610.8879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="27" x="958" y="1536.8149">SKIP</text>
+ </g>
+ <!--link pkt4_send to drop-->
+ <g id="link_pkt4_send_drop">
+ <path d="M1106.46,1433.2979 C1118.54,1440.6679 1130.13,1450.4079 1137,1462.8879 C1202.62,1582.1179 1210.65,1654.0179 1138,1769.0879 C1114,1807.1079 1066.3,1827.3279 1028,1837.8479 " fill="none" id="pkt4_send-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1023.4,1839.0679,1033.1219,1840.6444,1028.2352,1837.7947,1031.0848,1832.9081,1023.4,1839.0679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="35" x="1190" y="1636.8849">DROP</text>
+ </g>
+ <!--link pack to buffer4_send-->
+ <g id="link_pack_buffer4_send">
+ <path d="M1055.6,1551.3279 C1051.19,1566.7979 1044.91,1588.7879 1040.09,1605.6679 " fill="none" id="pack-to-buffer4_send" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1038.8,1610.1779,1045.1186,1602.6231,1040.1736,1605.3703,1037.4264,1600.4253,1038.8,1610.1779" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link buffer4_send to send-->
+ <g id="link_buffer4_send_send">
+ <path d="M1037.4,1651.3979 C1041.97,1671.0379 1049.14,1701.8779 1054.18,1723.5379 " fill="none" id="buffer4_send-to-send" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1055.26,1728.1879,1057.1255,1718.5174,1054.1315,1723.3169,1049.332,1720.3229,1055.26,1728.1879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="66" x="1049" y="1695.9149">CONTINUE</text>
+ </g>
+ <!--link buffer4_send to drop-->
+ <g id="link_buffer4_send_drop">
+ <path d="M1007.81,1651.3279 C986.51,1668.9779 957.52,1697.4479 945,1730.0179 C932.96,1761.3479 945.95,1799.5979 957.5,1824.0279 " fill="none" id="buffer4_send-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="959.57,1828.2679,959.2236,1818.4252,957.3799,1823.7731,952.0319,1821.9294,959.57,1828.2679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="35" x="946" y="1754.9549">DROP</text>
+ </g>
+ <!--link send to drop-->
+ <text fill="#888888" font-family="sans-serif" font-size="10" lengthAdjust="spacing" textLength="225" x="776" y="1919.9479">dashed arrow means asynchronous processing</text>
+ <!--SRC=[XLLDRzim3BthLn0zROSK6z33iCE08YcmebYRfUsfA0BLPc8XZkH9SefWs7yVzH5RifFq5Z- -Hr8I-MA5hH5cFwVptHKqlDYWXKQh0eqHSWsVxf33ryjlbry-CyRNA2rSBB10iKFuqwBUNm0te0Cozt8cbKMeSMNXEmZPjBPJFgBUYGBXHlAt6akl5IQY4Up8KrxNghNmrgvMgA-MiOWN3R1GKejrvMfMIcswBC_I7IhfjVL0NwNcTDBLFc4K4HfrqcnitaOAdEbXMY6rC5iyp4DkhosrQfR-i-DTxvGemV0j4ayuI7EwBI5XszPMJ6obaPTOmbQLeG77NGPkNhzHnZPAOlgMIlkYZ577biirlVl5F8JXjpizA0d26lX52BK4J_icdZEo_ZA8hHvqCs4AjKETqWxOYBn0kcHZKsw4Zb1hv0PpgeV6BI1TWCoe4HfHR-LAwMtA30ZxDtfhNyjSBGkrtGfhux2Eq3ElFkTw4qhHZ-EpP52M_I2vhsHLP4bxPUWHupLaORTgUJYVwi7kT8LIuADQwHvJidIHPpH7qIswOSxugBNIVj9wOu39vCTutOU0PmtscnAQQ1t209vMEgth9HUKmWJ5CAsZ1pyWmVJX_kds_ldcAElW2SHxlBkT7-4afTscivWt5VTOpwML2tVLLN31p5ZI3pX8WtMaRGzeWtOIZGkbnf65UfatH6S-lN6nH1QuSOt6WII9G0ChhRPWAypSZKpA27eORtFiO3Afl9KRgTx9_w8ZMN8aDh0PVvxnOevPHouzTw7pcNhIkKHU_b6Y0-bqekXW7voj8vrE50pkV9wESoJDIDJFfpE9T02FKGTJU28R6IGnvjUBE-P_JrvARYeiNmUbtFwGb_PGKEVhcqTJmpiiRyeODDZjR6QKMHA_FeEZ3mY3f05zgKHPedoDGSPMYWoJ_fFzKO7JTR_Je7Dfo57tigYqageroJyu-my0]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/packet4.uml b/doc/sphinx/uml/packet4.uml
new file mode 100644
index 0000000..15263c7
--- /dev/null
+++ b/doc/sphinx/uml/packet4.uml
@@ -0,0 +1,92 @@
+@startuml
+
+title DHCPv4 packet processing (Kea 1.8.0)
+
+agent "Receive query" as receivePacket
+note left : input
+
+agent "Service Enabled" as isServiceEnabled
+
+agent "Callout buffer4_receive" as buffer4_receive
+note right : hook
+
+agent "Unpack query" as unpack
+
+agent "Classify query" as classify
+
+agent "Callout pkt4_receive" as pkt4_receive
+note right : hook
+
+agent "Check DROP class" as drop_class
+
+agent "Avoid same client race in multi-threaded mode" as same_client
+note right : postpone processing or drop
+
+rectangle "Process Query on its Message Type" as process {
+ agent "Process Discover" as processDiscover
+ agent "Process Request" as processRequest
+ agent "Process Release" as processRelease
+ agent "Process Decline" as processDecline
+ agent "Process Inform" as processInform
+}
+
+agent "Callout leases4_committed" as leases4_committed
+note right : hook
+
+agent "Park" as park
+
+agent "Callout pkt4_send" as pkt4_send
+
+agent "Send response" as send
+
+agent "Pack response" as pack
+
+agent "Callout buffer4_send" as buffer4_send
+
+agent "Send response" as send
+note left : output
+
+agent "Drop packet" as drop
+note left : error
+
+receivePacket --> isServiceEnabled
+isServiceEnabled --> buffer4_receive : service is enabled
+isServiceEnabled ----> drop : service is disabled
+buffer4_receive --> unpack : CONTINUE
+buffer4_receive --> classify : SKIP
+buffer4_receive ----> drop : DROP
+unpack --> classify
+unpack ---> drop : on error
+classify --> pkt4_receive
+pkt4_receive --> drop_class : CONTINUE
+pkt4_receive ---> drop : DROP
+drop_class --> same_client
+drop_class ---> drop : query in DROP class
+same_client ---> process
+same_client ---> drop : queries from the same client possible race
+process ---> drop : unknown message type
+processDiscover --> leases4_committed
+processDiscover ---> drop : on error
+processRequest --> leases4_committed
+processRequest ---> drop : on error
+processRelease --> leases4_committed
+processRelease ---> drop : on error
+processDecline --> leases4_committed
+processDecline ---> drop : on error
+processInform --> leases4_committed
+processInform ---> drop : on error
+leases4_committed --> pkt4_send : CONTINUE
+leases4_committed ---> drop : DROP
+leases4_committed --> park : PARK
+park -[dashed]-> pkt4_send : unpark
+pkt4_send --> pack : CONTINUE
+pkt4_send --> buffer4_send : SKIP
+pkt4_send ---> drop : DROP
+pack --> buffer4_send
+buffer4_send --> send : CONTINUE
+buffer4_send ---> drop : DROP
+send -[hidden]-> drop
+
+footer dashed arrow means asynchronous processing
+
+@enduml
diff --git a/doc/sphinx/uml/priority-of-lease-lifetimes-and-dhcpv4-fields.atxt b/doc/sphinx/uml/priority-of-lease-lifetimes-and-dhcpv4-fields.atxt
new file mode 100644
index 0000000..76f2889
--- /dev/null
+++ b/doc/sphinx/uml/priority-of-lease-lifetimes-and-dhcpv4-fields.atxt
@@ -0,0 +1,38 @@
+ Precedence of Lease Lifetimes and DHCPv4 Fields
+
+ | _ _ _
+ | / \ / \ / \
+Globals | | | | | | |
+ | \_/ \_/ \_/
+ | |
+ | _ _ _ |
+ | / \ / \ / \ |
+Network | | | | | | | |
+ | \_/ \_/ \_/ |
+ | | | |
+ | _ | _ | |
+ | / \ | / \ | |
+Subnet | | | | | | | |
+ | \_/ | \_/ | |
+ | | | | |
+ | _ | _ | | _ |
+ | / \ | / \ | | / \ |
+Class-N | | | | | | | | | | |
+ | \_/ | \_/ | | \_/ |
+[More classes | | | | | | |
+in reverse | | | | | | |
+order of | | | | | | |
+definition] | | | _ | | | |
+ | | | / \ | | | |
+Class-1 | | | | | | | | |
+ | | | \_/ | | | |
+ | | | | | | | |
+----------------+----+------+------+------+------+------+------+
+ | | | | | | | |
+ | V V V V V V V
+ | _ _ _ _ _ _ _
+ | / \ / \ / \ / \ / \ / \ / \
+DHCP Response | | | | | | | | | | | | | | |
+ | \_/ \_/ \_/ \_/ \_/ \_/ \_/
+
+* This diagram was manually created and does not have a UML source.
diff --git a/doc/sphinx/uml/radius.png b/doc/sphinx/uml/radius.png
new file mode 100644
index 0000000..315eae4
--- /dev/null
+++ b/doc/sphinx/uml/radius.png
Binary files differ
diff --git a/doc/sphinx/uml/radius.svg b/doc/sphinx/uml/radius.svg
new file mode 100644
index 0000000..778d1f8
--- /dev/null
+++ b/doc/sphinx/uml/radius.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="2972px" preserveAspectRatio="none" style="width:839px;height:2972px;background:#FFFFFF;" version="1.1" viewBox="0 0 839 2972" width="839px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <!--entity _-->
+ <g id="elem__">
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="4" x="17" y="31.9659">&#160;</text>
+ <image height="1437" width="798" x="17" xlink:href="data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE0MzciIHdpZHRoPSI3OTgiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciID48ZGVmcy8+PGc+PHJlY3QgZmlsbD0iI0ZGRkZGRiIgc3R5bGU9IndpZHRoOjc5OHB4O2hlaWdodDoxNDM3cHg7YmFja2dyb3VuZDojRkZGRkZGOyIgd2lkdGg9Ijc5OCIgaGVpZ2h0PSIxNDM3Ii8+IDx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI0MDgiIHg9IjE5My41IiB5PSIzNC45NjU5Ij5SQURJVVMgd29ya2Zsb3cgZm9yIGxlYXNlIGFsbG9jYXRpb24gaW4gdGhlIERIQ1B2NCBzZXJ2ZXI8L3RleHQ+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSIzNi4zNDQxIiByeD0iMTIuNSIgcnk9IjEyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjExMCIgeD0iMzQ0IiB5PSI1MS4wNjc5Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iOTAiIHg9IjM1NCIgeT0iNzMuODk2Ij5QYWNrZXQgUmVjZWl2ZWQ8L3RleHQ+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSIzNi4zNDQxIiByeD0iMTIuNSIgcnk9IjEyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjExMyIgeD0iMzQyLjUiIHk9IjEwNy40MTIiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI5MyIgeD0iMzUyLjUiIHk9IjEzMC4yNDAxIj5TdWJuZXQgU2VsZWN0aW9uPC90ZXh0PjxlbGxpcHNlIGN4PSIzOTkiIGN5PSIxNzMuNzU2MSIgZmlsbD0iIzIyMjIyMiIgcng9IjEwIiByeT0iMTAiIHN0eWxlPSJzdHJva2U6IzIyMjIyMjtzdHJva2Utd2lkdGg6MS4wOyIvPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iMTE0MC43NjY1IiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiIHdpZHRoPSI3NzYiIHg9IjExIiB5PSIxOTMuNzU2MSIvPjxwYXRoIGQ9Ik0xNzIsMTkzLjc1NjEgTDE3MiwyMDUuODI0MSBMMTYyLDIxNS44MjQxIEwxMSwyMTUuODI0MSAiIGZpbGw9Im5vbmUiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxMDIiIHg9IjE0IiB5PSIyMDkuNzIyMSI+c3VibmV0NF9zZWxlY3Q8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNDUiIHg9IjEyMCIgeT0iMjA5LjcyMjEiPmNhbGxvdXQ8L3RleHQ+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSIzNi4zNDQxIiByeD0iMTIuNSIgcnk9IjEyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjQwNSIgeD0iMTk2LjUiIHk9IjIzMi44MjQxIi8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMzg1IiB4PSIyMDYuNSIgeT0iMjU1LjY1MjIiPlJldHJpZXZlIHJlc2VydmF0aW9uIGZyb20gaG9zdCBjYWNoZSBmb3IgdGhlIGN1cnJlbnQgaG9zdCBhbmQgc3VibmV0LjwvdGV4dD48cmVjdCBmaWxsPSIjRjFGMUYxIiBoZWlnaHQ9IjM2LjM0NDEiIHJ4PSIxMi41IiByeT0iMTIuNSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB3aWR0aD0iMzA5IiB4PSIyNDQuNSIgeT0iMzg4LjE1MDEiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIyODkiIHg9IjI1NC41IiB5PSI0MTAuOTc4MiI+UmVzZWxlY3QgdGhlIHN1Ym5ldCB0byB0aGUgb25lIGZyb20gdGhlIHJlc2VydmF0aW9uLjwvdGV4dD48cG9seWdvbiBmaWxsPSIjRjFGMUYxIiBwb2ludHM9IjMwNy41LDMzOC42NTkxLDQ5MC41LDMzOC42NTkxLDUwMi41LDM1MC42NTkxLDQ5MC41LDM2Mi42NTkxLDMwNy41LDM2Mi42NTkxLDI5NS41LDM1MC42NTkxLDMwNy41LDMzOC42NTkxIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNyIgeD0iNDAzIiB5PSIzNzQuNDE4Ij55ZXM8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTEiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTgzIiB4PSIzMDcuNSIgeT0iMzU0LjkyNzEiPlJlc2VydmF0aW9uIGZvciBhIGRpZmZlcmVudCBzdWJuZXQ/PC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE0IiB4PSI1MDIuNSIgeT0iMzQ3LjQzNjEiPm5vPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNGMUYxRjEiIHBvaW50cz0iMzk5LDQ0NC40OTQyLDQxMSw0NTYuNDk0MiwzOTksNDY4LjQ5NDIsMzg3LDQ1Ni40OTQyLDM5OSw0NDQuNDk0MiIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7Ii8+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSIzNi4zNDQxIiByeD0iMTIuNSIgcnk9IjEyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjQ1NCIgeD0iMTcyIiB5PSI0ODguNDk0MiIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEyIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjQzNCIgeD0iMTgyIiB5PSI1MTEuMzIyMyI+UmV0cmlldmUgcmVzZXJ2YXRpb24gZnJvbSBob3N0IGNhY2hlIGFnYWluIGluIGNhc2UgdGhlIHN1Ym5ldCB3YXMgcmVzZWxlY3RlZC48L3RleHQ+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSIzNi4zNDQxIiByeD0iMTIuNSIgcnk9IjEyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjI2MyIgeD0iMjY3LjUiIHk9IjU5NS42NTcyIi8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMjQzIiB4PSIyNzcuNSIgeT0iNjE4LjQ4NTMiPlVzZSBjYWNoZWQgYXR0cmlidXRlcyBmcm9tIHRoZSByZXNlcnZhdGlvbi48L3RleHQ+PGVsbGlwc2UgY3g9IjM5OSIgY3k9IjY3MC4zMjkyIiBmaWxsPSJub25lIiByeD0iMTEiIHJ5PSIxMSIgc3R5bGU9InN0cm9rZTojMjIyMjIyO3N0cm9rZS13aWR0aDoxLjA7Ii8+PGVsbGlwc2UgY3g9IjM5OSIgY3k9IjY3MC4zMjkyIiBmaWxsPSIjMjIyMjIyIiByeD0iNiIgcnk9IjYiIHN0eWxlPSJzdHJva2U6IzIyMjIyMjtzdHJva2Utd2lkdGg6MS4wOyIvPjxwb2x5Z29uIGZpbGw9IiNGMUYxRjEiIHBvaW50cz0iMzA2LDU0Ni4xNjYyLDQ5Miw1NDYuMTY2Miw1MDQsNTU4LjE2NjIsNDkyLDU3MC4xNjYyLDMwNiw1NzAuMTY2MiwyOTQsNTU4LjE2NjIsMzA2LDU0Ni4xNjYyIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNyIgeD0iNDAzIiB5PSI1ODEuOTI1MSI+eWVzPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE4NiIgeD0iMzA2IiB5PSI1NjIuNDM0MiI+UmVzZXJ2YXRpb24gd2l0aCBjYWNoZWQgYXR0cmlidXRlcz88L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTEiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTQiIHg9IjUwNCIgeT0iNTU0Ljk0MzMiPm5vPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNGMUYxRjEiIHBvaW50cz0iMzQ4LDI4OS4xNjgyLDQ1MCwyODkuMTY4Miw0NjIsMzAxLjE2ODIsNDUwLDMxMy4xNjgyLDM0OCwzMTMuMTY4MiwzMzYsMzAxLjE2ODIsMzQ4LDI4OS4xNjgyIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNyIgeD0iNDAzIiB5PSIzMjQuOTI3MSI+eWVzPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjEwMiIgeD0iMzQ4IiB5PSIzMDUuNDM2MSI+UmVzZXJ2YXRpb24gZm91bmQ/PC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE0IiB4PSI0NjIiIHk9IjI5Ny45NDUyIj5ubzwvdGV4dD48cG9seWdvbiBmaWxsPSIjRjFGMUYxIiBwb2ludHM9IjM5OSw3MjMuMzI5Miw0MTEsNzM1LjMyOTIsMzk5LDc0Ny4zMjkyLDM4Nyw3MzUuMzI5MiwzOTksNzIzLjMyOTIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIvPjxyZWN0IGZpbGw9IiNGMUYxRjEiIGhlaWdodD0iMzYuMzQ0MSIgcng9IjEyLjUiIHJ5PSIxMi41IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiIHdpZHRoPSIxNDIiIHg9IjMyOCIgeT0iNzY3LjMyOTIiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxMjIiIHg9IjMzOCIgeT0iNzkwLjE1NzMiPlNlbmQgQWNjZXNzLVJlcXVlc3QuPC90ZXh0PjxyZWN0IGZpbGw9IiNGMUYxRjEiIGhlaWdodD0iMzYuMzQ0MSIgcng9IjEyLjUiIHJ5PSIxMi41IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiIHdpZHRoPSIzNjgiIHg9IjIxNSIgeT0iOTIyLjY1NTIiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIzNDgiIHg9IjIyNSIgeT0iOTQ1LjQ4MzMiPlJlc2VsZWN0IHN1Ym5ldCB0byBtYXRjaCBhIHN1Ym5ldCB0aGUgY29udGFpbnMgdGhlIElQIGFkZHJlc3MuPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNGMUYxRjEiIHBvaW50cz0iMzAwLDg3My4xNjQzLDQ5OCw4NzMuMTY0Myw1MTAsODg1LjE2NDMsNDk4LDg5Ny4xNjQzLDMwMCw4OTcuMTY0MywyODgsODg1LjE2NDMsMzAwLDg3My4xNjQzIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNyIgeD0iNDAzIiB5PSI5MDguOTIzMiI+eWVzPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE5OCIgeD0iMzAwIiB5PSI4ODkuNDMyMyI+RnJhbWVkLUlQLUFkZHJlc3MgYXR0cmlidXRlIHByZXNlbnQ/PC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE0IiB4PSI1MTAiIHk9Ijg4MS45NDEzIj5ubzwvdGV4dD48cG9seWdvbiBmaWxsPSIjRjFGMUYxIiBwb2ludHM9IjM5OSw5NzguOTk5NCw0MTEsOTkwLjk5OTQsMzk5LDEwMDIuOTk5NCwzODcsOTkwLjk5OTQsMzk5LDk3OC45OTk0IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48cmVjdCBmaWxsPSIjRjFGMUYxIiBoZWlnaHQ9IjM2LjM0NDEiIHJ4PSIxMi41IiByeT0iMTIuNSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB3aWR0aD0iMzY1IiB4PSIyMTYuNSIgeT0iMTA3Mi40OTAzIi8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMzQ1IiB4PSIyMjYuNSIgeT0iMTA5NS4zMTg0Ij5SZXNlbGVjdCBzdWJuZXQgdG8gbWF0Y2ggYSBzdWJuZXQgZ3VhcmRlZCBieSB0aGUgY2xpZW50IGNsYXNzLjwvdGV4dD48cmVjdCBmaWxsPSIjRjFGMUYxIiBoZWlnaHQ9IjM2LjM0NDEiIHJ4PSIxMi41IiByeT0iMTIuNSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB3aWR0aD0iNDIzIiB4PSIxODcuNSIgeT0iMTE0My44MzQ0Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNDAzIiB4PSIxOTcuNSIgeT0iMTE2Ni42NjI1Ij5Bc3NpZ24gcGFja2V0IHRvIGNsaWVudCBjbGFzcyByZXByZXNlbnRlZCBieSB0aGUgdmFsdWUgb2YgRnJhbWVkLUlQLVBvb2wuPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNGMUYxRjEiIHBvaW50cz0iMzA5LDEwMjIuOTk5NCw0ODksMTAyMi45OTk0LDUwMSwxMDM0Ljk5OTQsNDg5LDEwNDYuOTk5NCwzMDksMTA0Ni45OTk0LDI5NywxMDM0Ljk5OTQsMzA5LDEwMjIuOTk5NCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTEiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTciIHg9IjQwMyIgeT0iMTA1OC43NTgzIj55ZXM8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTEiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTgwIiB4PSIzMDkiIHk9IjEwMzkuMjY3MyI+RnJhbWVkLUlQLVBvb2wgYXR0cmlidXRlIHByZXNlbnQ/PC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE0IiB4PSI1MDEiIHk9IjEwMzEuNzc2NCI+bm88L3RleHQ+PHBvbHlnb24gZmlsbD0iI0YxRjFGMSIgcG9pbnRzPSIzOTksMTIwMC4xNzg1LDQxMSwxMjEyLjE3ODUsMzk5LDEyMjQuMTc4NSwzODcsMTIxMi4xNzg1LDM5OSwxMjAwLjE3ODUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIvPjxyZWN0IGZpbGw9IiNGMUYxRjEiIGhlaWdodD0iMzYuMzQ0MSIgcng9IjEyLjUiIHJ5PSIxMi41IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiIHdpZHRoPSI3NTIiIHg9IjIzIiB5PSIxMjQ0LjE3ODUiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI3MzIiIHg9IjMzIiB5PSIxMjY3LjAwNjYiPlBvcHVsYXRlIHRoZSBob3N0IGNhY2hlIHdpdGggdGhlIHJlc2VsZWN0ZWQgc3VibmV0IElEIGFuZCB0aGUgcG90ZW50aWFsIGNsYXNzIGd1YXJkIG9yIElQIGFkZHJlc3MgcmV0dXJuZWQgZnJvbSB0aGUgUkFESVVTIHNlcnZlci48L3RleHQ+PHBvbHlnb24gZmlsbD0iI0YxRjFGMSIgcG9pbnRzPSIzNzQsODIzLjY3MzQsNDI0LDgyMy42NzM0LDQzNiw4MzUuNjczNCw0MjQsODQ3LjY3MzQsMzc0LDg0Ny42NzM0LDM2Miw4MzUuNjczNCwzNzQsODIzLjY3MzQiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjcxIiB4PSI0MDMiIHk9Ijg1OS40MzIzIj5BY2Nlc3MtQWNjZXB0PC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjUwIiB4PSIzNzQiIHk9IjgzOS45NDEzIj5SZXNwb25zZTwvdGV4dD48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI2OCIgeD0iNDM2IiB5PSI4MzIuNDUwNCI+QWNjZXNzLVJlamVjdDwvdGV4dD48ZWxsaXBzZSBjeD0iNTI2IiBjeT0iODM1LjY3MzQiIGZpbGw9Im5vbmUiIHJ4PSIxMSIgcnk9IjExIiBzdHlsZT0ic3Ryb2tlOiMyMjIyMjI7c3Ryb2tlLXdpZHRoOjEuMDsiLz48ZWxsaXBzZSBjeD0iNTI2IiBjeT0iODM1LjY3MzQiIGZpbGw9IiMyMjIyMjIiIHJ4PSI2IiByeT0iNiIgc3R5bGU9InN0cm9rZTojMjIyMjIyO3N0cm9rZS13aWR0aDoxLjA7Ii8+PGVsbGlwc2UgY3g9IjM5OSIgY3k9IjEzMTEuNTIyNiIgZmlsbD0ibm9uZSIgcng9IjExIiByeT0iMTEiIHN0eWxlPSJzdHJva2U6IzIyMjIyMjtzdHJva2Utd2lkdGg6MS4wOyIvPjxlbGxpcHNlIGN4PSIzOTkiIGN5PSIxMzExLjUyMjYiIGZpbGw9IiMyMjIyMjIiIHJ4PSI2IiByeT0iNiIgc3R5bGU9InN0cm9rZTojMjIyMjIyO3N0cm9rZS13aWR0aDoxLjA7Ii8+PHJlY3QgZmlsbD0ibm9uZSIgaGVpZ2h0PSI4MC40MTIiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgd2lkdGg9IjU3NyIgeD0iMTEwLjUiIHk9IjEzNDYuMTI0NiIvPjxwYXRoIGQ9Ik02NzcuNSwxMzQ2LjEyNDYgTDY3Ny41LDEzNTguMTkyNSBMNjY3LjUsMTM2OC4xOTI1IEwxMTAuNSwxMzY4LjE5MjUgIiBmaWxsPSJub25lIiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZm9udC13ZWlnaHQ9ImJvbGQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iODkiIHg9IjExMy41IiB5PSIxMzYyLjA5MDYiPmxlYXNlNF9zZWxlY3Q8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNCIgeD0iMjAyLjUiIHk9IjEzNjIuMDkwNiI+LDwvdGV4dD48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZm9udC13ZWlnaHQ9ImJvbGQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iOTIiIHg9IjIxMC41IiB5PSIxMzYyLjA5MDYiPmxlYXNlNF9yZW5ldzwvdGV4dD48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI0IiB4PSIzMDIuNSIgeT0iMTM2Mi4wOTA2Ij4sPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI5OCIgeD0iMzEwLjUiIHk9IjEzNjIuMDkwNiI+bGVhc2U0X3JlbGVhc2U8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNCIgeD0iNDA4LjUiIHk9IjEzNjIuMDkwNiI+LDwvdGV4dD48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZm9udC13ZWlnaHQ9ImJvbGQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iOTgiIHg9IjQxNi41IiB5PSIxMzYyLjA5MDYiPmxlYXNlNF9kZWNsaW5lPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjQiIHg9IjUxNC41IiB5PSIxMzYyLjA5MDYiPiw8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjkyIiB4PSI1MjIuNSIgeT0iMTM2Mi4wOTA2Ij5sZWFzZTRfZXhwaXJlPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjUyIiB4PSI2MTguNSIgeT0iMTM2Mi4wOTA2Ij5jYWxsb3V0czwvdGV4dD48cmVjdCBmaWxsPSIjRjFGMUYxIiBoZWlnaHQ9IjM2LjM0NDEiIHJ4PSIxMi41IiByeT0iMTIuNSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB3aWR0aD0iMjU1IiB4PSIyNzEuNSIgeT0iMTM3OC4xOTI1Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMjM1IiB4PSIyODEuNSIgeT0iMTQwMS4wMjA2Ij5TZW5kIEFjY291bnRpbmctUmVxdWVzdCBhc3luY2hyb25vdXNseS48L3RleHQ+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iODcuNDEyIiB5Mj0iMTA3LjQxMiIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDk3LjQxMiwzOTksMTA3LjQxMiw0MDMsOTcuNDEyLDM5OSwxMDEuNDEyIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSIxNDMuNzU2MSIgeTI9IjE2My43NTYxIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsMTUzLjc1NjEsMzk5LDE2My43NTYxLDQwMywxNTMuNzU2MSwzOTksMTU3Ljc1NjEiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9IjM2Mi42NTkxIiB5Mj0iMzg4LjE1MDEiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSwzNzguMTUwMSwzOTksMzg4LjE1MDEsNDAzLDM3OC4xNTAxLDM5OSwzODIuMTUwMSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iNTAyLjUiIHgyPSI1NjMuNSIgeTE9IjM1MC42NTkxIiB5Mj0iMzUwLjY1OTEiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjU1OS41LDM5Ni4zMjIxLDU2My41LDQwNi4zMjIxLDU2Ny41LDM5Ni4zMjIxLDU2My41LDQwMC4zMjIxIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSI1NjMuNSIgeDI9IjU2My41IiB5MT0iMzUwLjY1OTEiIHkyPSI0NTYuNDk0MiIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjU2My41IiB4Mj0iNDExIiB5MT0iNDU2LjQ5NDIiIHkyPSI0NTYuNDk0MiIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iNDIxLDQ1Mi40OTQyLDQxMSw0NTYuNDk0Miw0MjEsNDYwLjQ5NDIsNDE3LDQ1Ni40OTQyIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSI0MjQuNDk0MiIgeTI9IjQ0NC40OTQyIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsNDM0LjQ5NDIsMzk5LDQ0NC40OTQyLDQwMyw0MzQuNDk0MiwzOTksNDM4LjQ5NDIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9IjQ2OC40OTQyIiB5Mj0iNDg4LjQ5NDIiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSw0NzguNDk0MiwzOTksNDg4LjQ5NDIsNDAzLDQ3OC40OTQyLDM5OSw0ODIuNDk0MiIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iNjMyLjAwMTMiIHkyPSI2NTkuMzI5MiIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDY0OS4zMjkyLDM5OSw2NTkuMzI5Miw0MDMsNjQ5LjMyOTIsMzk5LDY1My4zMjkyIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSI1NzAuMTY2MiIgeTI9IjU5NS42NTcyIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsNTg1LjY1NzIsMzk5LDU5NS42NTcyLDQwMyw1ODUuNjU3MiwzOTksNTg5LjY1NzIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjUwNCIgeDI9IjU0MC41IiB5MT0iNTU4LjE2NjIiIHkyPSI1NTguMTY2MiIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iNTM2LjUsNjI5LjMyOTIsNTQwLjUsNjM5LjMyOTIsNTQ0LjUsNjI5LjMyOTIsNTQwLjUsNjMzLjMyOTIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjU0MC41IiB4Mj0iNTQwLjUiIHkxPSI1NTguMTY2MiIgeTI9IjcwMy4zMjkyIi8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iNTQwLjUiIHgyPSIzOTkiIHkxPSI3MDMuMzI5MiIgeTI9IjcwMy4zMjkyIi8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iNzAzLjMyOTIiIHkyPSI3MjMuMzI5MiIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDcxMy4zMjkyLDM5OSw3MjMuMzI5Miw0MDMsNzEzLjMyOTIsMzk5LDcxNy4zMjkyIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSI1MjQuODM4MyIgeTI9IjU0Ni4xNjYyIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsNTM2LjE2NjIsMzk5LDU0Ni4xNjYyLDQwMyw1MzYuMTY2MiwzOTksNTQwLjE2NjIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9IjMxMy4xNjgyIiB5Mj0iMzM4LjY1OTEiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSwzMjguNjU5MSwzOTksMzM4LjY1OTEsNDAzLDMyOC42NTkxLDM5OSwzMzIuNjU5MSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iNDYyIiB4Mj0iNjM2IiB5MT0iMzAxLjE2ODIiIHkyPSIzMDEuMTY4MiIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iNjMyLDUxNi4xNjYyLDYzNiw1MjYuMTY2Miw2NDAsNTE2LjE2NjIsNjM2LDUyMC4xNjYyIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSI2MzYiIHgyPSI2MzYiIHkxPSIzMDEuMTY4MiIgeTI9IjczNS4zMjkyIi8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iNjM2IiB4Mj0iNDExIiB5MT0iNzM1LjMyOTIiIHkyPSI3MzUuMzI5MiIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iNDIxLDczMS4zMjkyLDQxMSw3MzUuMzI5Miw0MjEsNzM5LjMyOTIsNDE3LDczNS4zMjkyIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSIyNjkuMTY4MiIgeTI9IjI4OS4xNjgyIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsMjc5LjE2ODIsMzk5LDI4OS4xNjgyLDQwMywyNzkuMTY4MiwzOTksMjgzLjE2ODIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9Ijc0Ny4zMjkyIiB5Mj0iNzY3LjMyOTIiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSw3NTcuMzI5MiwzOTksNzY3LjMyOTIsNDAzLDc1Ny4zMjkyLDM5OSw3NjEuMzI5MiIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iODk3LjE2NDMiIHkyPSI5MjIuNjU1MiIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDkxMi42NTUyLDM5OSw5MjIuNjU1Miw0MDMsOTEyLjY1NTIsMzk5LDkxNi42NTUyIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSI1MTAiIHgyPSI1OTMiIHkxPSI4ODUuMTY0MyIgeTI9Ijg4NS4xNjQzIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSI1ODksOTMwLjgyNzMsNTkzLDk0MC44MjczLDU5Nyw5MzAuODI3Myw1OTMsOTM0LjgyNzMiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjU5MyIgeDI9IjU5MyIgeTE9Ijg4NS4xNjQzIiB5Mj0iOTkwLjk5OTQiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSI1OTMiIHgyPSI0MTEiIHkxPSI5OTAuOTk5NCIgeTI9Ijk5MC45OTk0Ii8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSI0MjEsOTg2Ljk5OTQsNDExLDk5MC45OTk0LDQyMSw5OTQuOTk5NCw0MTcsOTkwLjk5OTQiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9Ijk1OC45OTk0IiB5Mj0iOTc4Ljk5OTQiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSw5NjguOTk5NCwzOTksOTc4Ljk5OTQsNDAzLDk2OC45OTk0LDM5OSw5NzIuOTk5NCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iMTEwOC44MzQ0IiB5Mj0iMTE0My44MzQ0Ii8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsMTEzMy44MzQ0LDM5OSwxMTQzLjgzNDQsNDAzLDExMzMuODM0NCwzOTksMTEzNy44MzQ0IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSIxMDQ2Ljk5OTQiIHkyPSIxMDcyLjQ5MDMiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSwxMDYyLjQ5MDMsMzk5LDEwNzIuNDkwMyw0MDMsMTA2Mi40OTAzLDM5OSwxMDY2LjQ5MDMiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjUwMSIgeDI9IjYyMC41IiB5MT0iMTAzNC45OTk0IiB5Mj0iMTAzNC45OTk0Ii8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSI2MTYuNSwxMTE2LjMzNDQsNjIwLjUsMTEyNi4zMzQ0LDYyNC41LDExMTYuMzM0NCw2MjAuNSwxMTIwLjMzNDQiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjYyMC41IiB4Mj0iNjIwLjUiIHkxPSIxMDM0Ljk5OTQiIHkyPSIxMjEyLjE3ODUiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSI2MjAuNSIgeDI9IjQxMSIgeTE9IjEyMTIuMTc4NSIgeTI9IjEyMTIuMTc4NSIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iNDIxLDEyMDguMTc4NSw0MTEsMTIxMi4xNzg1LDQyMSwxMjE2LjE3ODUsNDE3LDEyMTIuMTc4NSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iMTE4MC4xNzg1IiB5Mj0iMTIwMC4xNzg1Ii8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsMTE5MC4xNzg1LDM5OSwxMjAwLjE3ODUsNDAzLDExOTAuMTc4NSwzOTksMTE5NC4xNzg1IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSIxMDAyLjk5OTQiIHkyPSIxMDIyLjk5OTQiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSwxMDEyLjk5OTQsMzk5LDEwMjIuOTk5NCw0MDMsMTAxMi45OTk0LDM5OSwxMDE2Ljk5OTQiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9IjEyMjQuMTc4NSIgeTI9IjEyNDQuMTc4NSIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDEyMzQuMTc4NSwzOTksMTI0NC4xNzg1LDQwMywxMjM0LjE3ODUsMzk5LDEyMzguMTc4NSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iODQ3LjY3MzQiIHkyPSI4NzMuMTY0MyIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDg2My4xNjQzLDM5OSw4NzMuMTY0Myw0MDMsODYzLjE2NDMsMzk5LDg2Ny4xNjQzIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSI0MzYiIHgyPSI1MTUiIHkxPSI4MzUuNjczNCIgeTI9IjgzNS42NzM0Ii8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSI1MDUsODMxLjY3MzQsNTE1LDgzNS42NzM0LDUwNSw4MzkuNjczNCw1MDksODM1LjY3MzQiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9IjEyODAuNTIyNiIgeTI9IjEzMDAuNTIyNiIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDEyOTAuNTIyNiwzOTksMTMwMC41MjI2LDQwMywxMjkwLjUyMjYsMzk5LDEyOTQuNTIyNiIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iODAzLjY3MzQiIHkyPSI4MjMuNjczNCIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDgxMy42NzM0LDM5OSw4MjMuNjczNCw0MDMsODEzLjY3MzQsMzk5LDgxNy42NzM0IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSIxODMuNzU2MSIgeTI9IjIzMi44MjQxIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsMjIyLjgyNDEsMzk5LDIzMi44MjQxLDQwMywyMjIuODI0MSwzOTksMjI2LjgyNDEiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjwhLS1TUkM9W2hMSERSbmVuNEJ0ZEx1bXU0TEh3b1lhdkxBWWVBaFNMQUVWQVU2VFBEeVJVc2NDZWdsQlZFeFFOX08xNDlMOUZRdV83Unp4UlVIdTAwNUFhNFRRQnZVZnYwcVZoTllmalpyMVAxbmc1SG4xUU1vYjhNR0ZBMERLOG8telZZeUMzVTdHN1Q3U0MwTy01YTJ6OGk0UTl3ZTNiS3V2a21qUG1UOENRUEdKOE9LXzJLTGVybDUwOVVKQVItUEp5eUNFZHhEYUNQQW1TUTA5X0taQlpoUDZTbVdFMm1yV3lhd2dTdEtEakZWNDVvVW1ZeXlYSTFrVkdLM3VJZmVIU3VGREpZd09nY0F3N0UzUU95aWp6bDZqV1VhOV90b1EtYlVmMEdBY2cyYkU5WkZwc3RLR3hRcWdxU1l3R0pKamhDMGs4Y3U2Z1pZSzBRY3gyckRXVTRXc05sWGwwdF8xTm40d2VyNDBQVXBlV1NYRy1OT200aUhwS2xISHpMNUhkaTE4NFNTYmo4RkpsWWR4Y0NiVlBfejhRcE02UjZ3TFZkV3hGN1pVeVd1TUt3RnJ5WlJ5Mi1nbEVEelB1RkREbEstRWRlTjdkbHBjbm50QS1Ba1FCaWNKVWxYUzRKVEhYUmNYenRfUXo4NWNwVlN3SFE1TGhZRGxacXNQTFdDWHJGYzIyQ1NfMk1sclZJVXcyUzJMdFNkbEFWQk1BamZUUVo0V29yQzl4akpGeWlEQ3VPQTFYQVpVemZUOTM3T0dFXzBtZzZCRl9ZRnlCc21HakFCandPRmJhc0JGTmlodEZVYlJCRDE3WU9NRTkySWNYTXV2OUFsMDN4X2wwMDFJU3VVa1RWVGpuY0tUV0hwUEp4THB0YXNsc2gwVkU3V2hlbWdfTjhwNER1N3VZVmU4a242QzdablVIajFaNUlrSlZSeU9uX0RxZVhfcnV6UEZCOXlEcGE5SlBkUHlEMjd5b2lkUk1zRTNyZ0hOeC1YUzBdLS0+PC9nPjwvc3ZnPg==" y="36.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="4" x="17" y="1488.0339">&#160;</text>
+ <image height="1437" width="798" x="17" xlink:href="data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE0MzciIHdpZHRoPSI3OTgiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciID48ZGVmcy8+PGc+PHJlY3QgZmlsbD0iI0ZGRkZGRiIgc3R5bGU9IndpZHRoOjc5OHB4O2hlaWdodDoxNDM3cHg7YmFja2dyb3VuZDojRkZGRkZGOyIgd2lkdGg9Ijc5OCIgaGVpZ2h0PSIxNDM3Ii8+IDx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI0MDgiIHg9IjE5My41IiB5PSIzNC45NjU5Ij5SQURJVVMgd29ya2Zsb3cgZm9yIGxlYXNlIGFsbG9jYXRpb24gaW4gdGhlIERIQ1B2NiBzZXJ2ZXI8L3RleHQ+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSIzNi4zNDQxIiByeD0iMTIuNSIgcnk9IjEyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjExMCIgeD0iMzQ0IiB5PSI1MS4wNjc5Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iOTAiIHg9IjM1NCIgeT0iNzMuODk2Ij5QYWNrZXQgUmVjZWl2ZWQ8L3RleHQ+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSIzNi4zNDQxIiByeD0iMTIuNSIgcnk9IjEyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjExMyIgeD0iMzQyLjUiIHk9IjEwNy40MTIiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI5MyIgeD0iMzUyLjUiIHk9IjEzMC4yNDAxIj5TdWJuZXQgU2VsZWN0aW9uPC90ZXh0PjxlbGxpcHNlIGN4PSIzOTkiIGN5PSIxNzMuNzU2MSIgZmlsbD0iIzIyMjIyMiIgcng9IjEwIiByeT0iMTAiIHN0eWxlPSJzdHJva2U6IzIyMjIyMjtzdHJva2Utd2lkdGg6MS4wOyIvPjxyZWN0IGZpbGw9Im5vbmUiIGhlaWdodD0iMTE0MC43NjY1IiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiIHdpZHRoPSI3NzYiIHg9IjExIiB5PSIxOTMuNzU2MSIvPjxwYXRoIGQ9Ik0xNzIsMTkzLjc1NjEgTDE3MiwyMDUuODI0MSBMMTYyLDIxNS44MjQxIEwxMSwyMTUuODI0MSAiIGZpbGw9Im5vbmUiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxMDIiIHg9IjE0IiB5PSIyMDkuNzIyMSI+c3VibmV0Nl9zZWxlY3Q8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNDUiIHg9IjEyMCIgeT0iMjA5LjcyMjEiPmNhbGxvdXQ8L3RleHQ+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSIzNi4zNDQxIiByeD0iMTIuNSIgcnk9IjEyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjQwNSIgeD0iMTk2LjUiIHk9IjIzMi44MjQxIi8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMzg1IiB4PSIyMDYuNSIgeT0iMjU1LjY1MjIiPlJldHJpZXZlIHJlc2VydmF0aW9uIGZyb20gaG9zdCBjYWNoZSBmb3IgdGhlIGN1cnJlbnQgaG9zdCBhbmQgc3VibmV0LjwvdGV4dD48cmVjdCBmaWxsPSIjRjFGMUYxIiBoZWlnaHQ9IjM2LjM0NDEiIHJ4PSIxMi41IiByeT0iMTIuNSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7IiB3aWR0aD0iMzA5IiB4PSIyNDQuNSIgeT0iMzg4LjE1MDEiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIyODkiIHg9IjI1NC41IiB5PSI0MTAuOTc4MiI+UmVzZWxlY3QgdGhlIHN1Ym5ldCB0byB0aGUgb25lIGZyb20gdGhlIHJlc2VydmF0aW9uLjwvdGV4dD48cG9seWdvbiBmaWxsPSIjRjFGMUYxIiBwb2ludHM9IjMwNy41LDMzOC42NTkxLDQ5MC41LDMzOC42NTkxLDUwMi41LDM1MC42NTkxLDQ5MC41LDM2Mi42NTkxLDMwNy41LDM2Mi42NTkxLDI5NS41LDM1MC42NTkxLDMwNy41LDMzOC42NTkxIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNyIgeD0iNDAzIiB5PSIzNzQuNDE4Ij55ZXM8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTEiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTgzIiB4PSIzMDcuNSIgeT0iMzU0LjkyNzEiPlJlc2VydmF0aW9uIGZvciBhIGRpZmZlcmVudCBzdWJuZXQ/PC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE0IiB4PSI1MDIuNSIgeT0iMzQ3LjQzNjEiPm5vPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNGMUYxRjEiIHBvaW50cz0iMzk5LDQ0NC40OTQyLDQxMSw0NTYuNDk0MiwzOTksNDY4LjQ5NDIsMzg3LDQ1Ni40OTQyLDM5OSw0NDQuNDk0MiIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7Ii8+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSIzNi4zNDQxIiByeD0iMTIuNSIgcnk9IjEyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjQ1NCIgeD0iMTcyIiB5PSI0ODguNDk0MiIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEyIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjQzNCIgeD0iMTgyIiB5PSI1MTEuMzIyMyI+UmV0cmlldmUgcmVzZXJ2YXRpb24gZnJvbSBob3N0IGNhY2hlIGFnYWluIGluIGNhc2UgdGhlIHN1Ym5ldCB3YXMgcmVzZWxlY3RlZC48L3RleHQ+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSIzNi4zNDQxIiByeD0iMTIuNSIgcnk9IjEyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9IjI2MyIgeD0iMjY3LjUiIHk9IjU5NS42NTcyIi8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMjQzIiB4PSIyNzcuNSIgeT0iNjE4LjQ4NTMiPlVzZSBjYWNoZWQgYXR0cmlidXRlcyBmcm9tIHRoZSByZXNlcnZhdGlvbi48L3RleHQ+PGVsbGlwc2UgY3g9IjM5OSIgY3k9IjY3MC4zMjkyIiBmaWxsPSJub25lIiByeD0iMTEiIHJ5PSIxMSIgc3R5bGU9InN0cm9rZTojMjIyMjIyO3N0cm9rZS13aWR0aDoxLjA7Ii8+PGVsbGlwc2UgY3g9IjM5OSIgY3k9IjY3MC4zMjkyIiBmaWxsPSIjMjIyMjIyIiByeD0iNiIgcnk9IjYiIHN0eWxlPSJzdHJva2U6IzIyMjIyMjtzdHJva2Utd2lkdGg6MS4wOyIvPjxwb2x5Z29uIGZpbGw9IiNGMUYxRjEiIHBvaW50cz0iMzA2LDU0Ni4xNjYyLDQ5Miw1NDYuMTY2Miw1MDQsNTU4LjE2NjIsNDkyLDU3MC4xNjYyLDMwNiw1NzAuMTY2MiwyOTQsNTU4LjE2NjIsMzA2LDU0Ni4xNjYyIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNyIgeD0iNDAzIiB5PSI1ODEuOTI1MSI+eWVzPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE4NiIgeD0iMzA2IiB5PSI1NjIuNDM0MiI+UmVzZXJ2YXRpb24gd2l0aCBjYWNoZWQgYXR0cmlidXRlcz88L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTEiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTQiIHg9IjUwNCIgeT0iNTU0Ljk0MzMiPm5vPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNGMUYxRjEiIHBvaW50cz0iMzQ4LDI4OS4xNjgyLDQ1MCwyODkuMTY4Miw0NjIsMzAxLjE2ODIsNDUwLDMxMy4xNjgyLDM0OCwzMTMuMTY4MiwzMzYsMzAxLjE2ODIsMzQ4LDI4OS4xNjgyIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNyIgeD0iNDAzIiB5PSIzMjQuOTI3MSI+eWVzPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjEwMiIgeD0iMzQ4IiB5PSIzMDUuNDM2MSI+UmVzZXJ2YXRpb24gZm91bmQ/PC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE0IiB4PSI0NjIiIHk9IjI5Ny45NDUyIj5ubzwvdGV4dD48cG9seWdvbiBmaWxsPSIjRjFGMUYxIiBwb2ludHM9IjM5OSw3MjMuMzI5Miw0MTEsNzM1LjMyOTIsMzk5LDc0Ny4zMjkyLDM4Nyw3MzUuMzI5MiwzOTksNzIzLjMyOTIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIvPjxyZWN0IGZpbGw9IiNGMUYxRjEiIGhlaWdodD0iMzYuMzQ0MSIgcng9IjEyLjUiIHJ5PSIxMi41IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiIHdpZHRoPSIxNDIiIHg9IjMyOCIgeT0iNzY3LjMyOTIiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxMjIiIHg9IjMzOCIgeT0iNzkwLjE1NzMiPlNlbmQgQWNjZXNzLVJlcXVlc3QuPC90ZXh0PjxyZWN0IGZpbGw9IiNGMUYxRjEiIGhlaWdodD0iMzYuMzQ0MSIgcng9IjEyLjUiIHJ5PSIxMi41IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiIHdpZHRoPSIzNjgiIHg9IjIxNSIgeT0iOTIyLjY1NTIiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIzNDgiIHg9IjIyNSIgeT0iOTQ1LjQ4MzMiPlJlc2VsZWN0IHN1Ym5ldCB0byBtYXRjaCBhIHN1Ym5ldCB0aGUgY29udGFpbnMgdGhlIElQIGFkZHJlc3MuPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNGMUYxRjEiIHBvaW50cz0iMjk0LDg3My4xNjQzLDUwNCw4NzMuMTY0Myw1MTYsODg1LjE2NDMsNTA0LDg5Ny4xNjQzLDI5NCw4OTcuMTY0MywyODIsODg1LjE2NDMsMjk0LDg3My4xNjQzIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNyIgeD0iNDAzIiB5PSI5MDguOTIzMiI+eWVzPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjIxMCIgeD0iMjk0IiB5PSI4ODkuNDMyMyI+RnJhbWVkLUlQdjYtQWRkcmVzcyBhdHRyaWJ1dGUgcHJlc2VudD88L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTEiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTQiIHg9IjUxNiIgeT0iODgxLjk0MTMiPm5vPC90ZXh0Pjxwb2x5Z29uIGZpbGw9IiNGMUYxRjEiIHBvaW50cz0iMzk5LDk3OC45OTk0LDQxMSw5OTAuOTk5NCwzOTksMTAwMi45OTk0LDM4Nyw5OTAuOTk5NCwzOTksOTc4Ljk5OTQiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIvPjxyZWN0IGZpbGw9IiNGMUYxRjEiIGhlaWdodD0iMzYuMzQ0MSIgcng9IjEyLjUiIHJ5PSIxMi41IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiIHdpZHRoPSIzNjUiIHg9IjIxNi41IiB5PSIxMDcyLjQ5MDMiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIzNDUiIHg9IjIyNi41IiB5PSIxMDk1LjMxODQiPlJlc2VsZWN0IHN1Ym5ldCB0byBtYXRjaCBhIHN1Ym5ldCBndWFyZGVkIGJ5IHRoZSBjbGllbnQgY2xhc3MuPC90ZXh0PjxyZWN0IGZpbGw9IiNGMUYxRjEiIGhlaWdodD0iMzYuMzQ0MSIgcng9IjEyLjUiIHJ5PSIxMi41IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiIHdpZHRoPSI0MjMiIHg9IjE4Ny41IiB5PSIxMTQzLjgzNDQiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI0MDMiIHg9IjE5Ny41IiB5PSIxMTY2LjY2MjUiPkFzc2lnbiBwYWNrZXQgdG8gY2xpZW50IGNsYXNzIHJlcHJlc2VudGVkIGJ5IHRoZSB2YWx1ZSBvZiBGcmFtZWQtSVAtUG9vbC48L3RleHQ+PHBvbHlnb24gZmlsbD0iI0YxRjFGMSIgcG9pbnRzPSIzMDksMTAyMi45OTk0LDQ4OSwxMDIyLjk5OTQsNTAxLDEwMzQuOTk5NCw0ODksMTA0Ni45OTk0LDMwOSwxMDQ2Ljk5OTQsMjk3LDEwMzQuOTk5NCwzMDksMTAyMi45OTk0IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNyIgeD0iNDAzIiB5PSIxMDU4Ljc1ODMiPnllczwvdGV4dD48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMSIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxODAiIHg9IjMwOSIgeT0iMTAzOS4yNjczIj5GcmFtZWQtSVAtUG9vbCBhdHRyaWJ1dGUgcHJlc2VudD88L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTEiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTQiIHg9IjUwMSIgeT0iMTAzMS43NzY0Ij5ubzwvdGV4dD48cG9seWdvbiBmaWxsPSIjRjFGMUYxIiBwb2ludHM9IjM5OSwxMjAwLjE3ODUsNDExLDEyMTIuMTc4NSwzOTksMTIyNC4xNzg1LDM4NywxMjEyLjE3ODUsMzk5LDEyMDAuMTc4NSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7Ii8+PHJlY3QgZmlsbD0iI0YxRjFGMSIgaGVpZ2h0PSIzNi4zNDQxIiByeD0iMTIuNSIgcnk9IjEyLjUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MC41OyIgd2lkdGg9Ijc1MiIgeD0iMjMiIHk9IjEyNDQuMTc4NSIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEyIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjczMiIgeD0iMzMiIHk9IjEyNjcuMDA2NiI+UG9wdWxhdGUgdGhlIGhvc3QgY2FjaGUgd2l0aCB0aGUgcmVzZWxlY3RlZCBzdWJuZXQgSUQgYW5kIHRoZSBwb3RlbnRpYWwgY2xhc3MgZ3VhcmQgb3IgSVAgYWRkcmVzcyByZXR1cm5lZCBmcm9tIHRoZSBSQURJVVMgc2VydmVyLjwvdGV4dD48cG9seWdvbiBmaWxsPSIjRjFGMUYxIiBwb2ludHM9IjM3NCw4MjMuNjczNCw0MjQsODIzLjY3MzQsNDM2LDgzNS42NzM0LDQyNCw4NDcuNjczNCwzNzQsODQ3LjY3MzQsMzYyLDgzNS42NzM0LDM3NCw4MjMuNjczNCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDowLjU7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTEiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNzEiIHg9IjQwMyIgeT0iODU5LjQzMjMiPkFjY2Vzcy1BY2NlcHQ8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTEiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNTAiIHg9IjM3NCIgeT0iODM5Ljk0MTMiPlJlc3BvbnNlPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjExIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjY4IiB4PSI0MzYiIHk9IjgzMi40NTA0Ij5BY2Nlc3MtUmVqZWN0PC90ZXh0PjxlbGxpcHNlIGN4PSI1MjYiIGN5PSI4MzUuNjczNCIgZmlsbD0ibm9uZSIgcng9IjExIiByeT0iMTEiIHN0eWxlPSJzdHJva2U6IzIyMjIyMjtzdHJva2Utd2lkdGg6MS4wOyIvPjxlbGxpcHNlIGN4PSI1MjYiIGN5PSI4MzUuNjczNCIgZmlsbD0iIzIyMjIyMiIgcng9IjYiIHJ5PSI2IiBzdHlsZT0ic3Ryb2tlOiMyMjIyMjI7c3Ryb2tlLXdpZHRoOjEuMDsiLz48ZWxsaXBzZSBjeD0iMzk5IiBjeT0iMTMxMS41MjI2IiBmaWxsPSJub25lIiByeD0iMTEiIHJ5PSIxMSIgc3R5bGU9InN0cm9rZTojMjIyMjIyO3N0cm9rZS13aWR0aDoxLjA7Ii8+PGVsbGlwc2UgY3g9IjM5OSIgY3k9IjEzMTEuNTIyNiIgZmlsbD0iIzIyMjIyMiIgcng9IjYiIHJ5PSI2IiBzdHlsZT0ic3Ryb2tlOiMyMjIyMjI7c3Ryb2tlLXdpZHRoOjEuMDsiLz48cmVjdCBmaWxsPSJub25lIiBoZWlnaHQ9IjgwLjQxMiIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7IiB3aWR0aD0iNjc5IiB4PSI1OS41IiB5PSIxMzQ2LjEyNDYiLz48cGF0aCBkPSJNNzI4LjUsMTM0Ni4xMjQ2IEw3MjguNSwxMzU4LjE5MjUgTDcxOC41LDEzNjguMTkyNSBMNTkuNSwxMzY4LjE5MjUgIiBmaWxsPSJub25lIiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZm9udC13ZWlnaHQ9ImJvbGQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iODkiIHg9IjYyLjUiIHk9IjEzNjIuMDkwNiI+bGVhc2U2X3NlbGVjdDwvdGV4dD48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI0IiB4PSIxNTEuNSIgeT0iMTM2Mi4wOTA2Ij4sPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI5MiIgeD0iMTU5LjUiIHk9IjEzNjIuMDkwNiI+bGVhc2U2X3JlbmV3PC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjQiIHg9IjI1MS41IiB5PSIxMzYyLjA5MDYiPiw8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9Ijk0IiB4PSIyNTkuNSIgeT0iMTM2Mi4wOTA2Ij5sZWFzZTZfcmViaW5kPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjQiIHg9IjM1My41IiB5PSIxMzYyLjA5MDYiPiw8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9Ijk4IiB4PSIzNjEuNSIgeT0iMTM2Mi4wOTA2Ij5sZWFzZTZfcmVsZWFzZTwvdGV4dD48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI0IiB4PSI0NTkuNSIgeT0iMTM2Mi4wOTA2Ij4sPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI5OCIgeD0iNDY3LjUiIHk9IjEzNjIuMDkwNiI+bGVhc2U2X2RlY2xpbmU8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNCIgeD0iNTY1LjUiIHk9IjEzNjIuMDkwNiI+LDwvdGV4dD48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZm9udC13ZWlnaHQ9ImJvbGQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iOTIiIHg9IjU3My41IiB5PSIxMzYyLjA5MDYiPmxlYXNlNl9leHBpcmU8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNTIiIHg9IjY2OS41IiB5PSIxMzYyLjA5MDYiPmNhbGxvdXRzPC90ZXh0PjxyZWN0IGZpbGw9IiNGMUYxRjEiIGhlaWdodD0iMzYuMzQ0MSIgcng9IjEyLjUiIHJ5PSIxMi41IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjAuNTsiIHdpZHRoPSIyNTUiIHg9IjI3MS41IiB5PSIxMzc4LjE5MjUiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMiIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIyMzUiIHg9IjI4MS41IiB5PSIxNDAxLjAyMDYiPlNlbmQgQWNjb3VudGluZy1SZXF1ZXN0IGFzeW5jaHJvbm91c2x5LjwvdGV4dD48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSI4Ny40MTIiIHkyPSIxMDcuNDEyIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsOTcuNDEyLDM5OSwxMDcuNDEyLDQwMyw5Ny40MTIsMzk5LDEwMS40MTIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9IjE0My43NTYxIiB5Mj0iMTYzLjc1NjEiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSwxNTMuNzU2MSwzOTksMTYzLjc1NjEsNDAzLDE1My43NTYxLDM5OSwxNTcuNzU2MSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iMzYyLjY1OTEiIHkyPSIzODguMTUwMSIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDM3OC4xNTAxLDM5OSwzODguMTUwMSw0MDMsMzc4LjE1MDEsMzk5LDM4Mi4xNTAxIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSI1MDIuNSIgeDI9IjU2My41IiB5MT0iMzUwLjY1OTEiIHkyPSIzNTAuNjU5MSIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iNTU5LjUsMzk2LjMyMjEsNTYzLjUsNDA2LjMyMjEsNTY3LjUsMzk2LjMyMjEsNTYzLjUsNDAwLjMyMjEiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjU2My41IiB4Mj0iNTYzLjUiIHkxPSIzNTAuNjU5MSIgeTI9IjQ1Ni40OTQyIi8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iNTYzLjUiIHgyPSI0MTEiIHkxPSI0NTYuNDk0MiIgeTI9IjQ1Ni40OTQyIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSI0MjEsNDUyLjQ5NDIsNDExLDQ1Ni40OTQyLDQyMSw0NjAuNDk0Miw0MTcsNDU2LjQ5NDIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9IjQyNC40OTQyIiB5Mj0iNDQ0LjQ5NDIiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSw0MzQuNDk0MiwzOTksNDQ0LjQ5NDIsNDAzLDQzNC40OTQyLDM5OSw0MzguNDk0MiIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iNDY4LjQ5NDIiIHkyPSI0ODguNDk0MiIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDQ3OC40OTQyLDM5OSw0ODguNDk0Miw0MDMsNDc4LjQ5NDIsMzk5LDQ4Mi40OTQyIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSI2MzIuMDAxMyIgeTI9IjY1OS4zMjkyIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsNjQ5LjMyOTIsMzk5LDY1OS4zMjkyLDQwMyw2NDkuMzI5MiwzOTksNjUzLjMyOTIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9IjU3MC4xNjYyIiB5Mj0iNTk1LjY1NzIiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSw1ODUuNjU3MiwzOTksNTk1LjY1NzIsNDAzLDU4NS42NTcyLDM5OSw1ODkuNjU3MiIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iNTA0IiB4Mj0iNTQwLjUiIHkxPSI1NTguMTY2MiIgeTI9IjU1OC4xNjYyIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSI1MzYuNSw2MjkuMzI5Miw1NDAuNSw2MzkuMzI5Miw1NDQuNSw2MjkuMzI5Miw1NDAuNSw2MzMuMzI5MiIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iNTQwLjUiIHgyPSI1NDAuNSIgeTE9IjU1OC4xNjYyIiB5Mj0iNzAzLjMyOTIiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSI1NDAuNSIgeDI9IjM5OSIgeTE9IjcwMy4zMjkyIiB5Mj0iNzAzLjMyOTIiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSI3MDMuMzI5MiIgeTI9IjcyMy4zMjkyIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsNzEzLjMyOTIsMzk5LDcyMy4zMjkyLDQwMyw3MTMuMzI5MiwzOTksNzE3LjMyOTIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9IjUyNC44MzgzIiB5Mj0iNTQ2LjE2NjIiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSw1MzYuMTY2MiwzOTksNTQ2LjE2NjIsNDAzLDUzNi4xNjYyLDM5OSw1NDAuMTY2MiIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iMzEzLjE2ODIiIHkyPSIzMzguNjU5MSIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDMyOC42NTkxLDM5OSwzMzguNjU5MSw0MDMsMzI4LjY1OTEsMzk5LDMzMi42NTkxIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSI0NjIiIHgyPSI2MzYiIHkxPSIzMDEuMTY4MiIgeTI9IjMwMS4xNjgyIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSI2MzIsNTE2LjE2NjIsNjM2LDUyNi4xNjYyLDY0MCw1MTYuMTY2Miw2MzYsNTIwLjE2NjIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjYzNiIgeDI9IjYzNiIgeTE9IjMwMS4xNjgyIiB5Mj0iNzM1LjMyOTIiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSI2MzYiIHgyPSI0MTEiIHkxPSI3MzUuMzI5MiIgeTI9IjczNS4zMjkyIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSI0MjEsNzMxLjMyOTIsNDExLDczNS4zMjkyLDQyMSw3MzkuMzI5Miw0MTcsNzM1LjMyOTIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9IjI2OS4xNjgyIiB5Mj0iMjg5LjE2ODIiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSwyNzkuMTY4MiwzOTksMjg5LjE2ODIsNDAzLDI3OS4xNjgyLDM5OSwyODMuMTY4MiIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iNzQ3LjMyOTIiIHkyPSI3NjcuMzI5MiIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDc1Ny4zMjkyLDM5OSw3NjcuMzI5Miw0MDMsNzU3LjMyOTIsMzk5LDc2MS4zMjkyIiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSI4OTcuMTY0MyIgeTI9IjkyMi42NTUyIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsOTEyLjY1NTIsMzk5LDkyMi42NTUyLDQwMyw5MTIuNjU1MiwzOTksOTE2LjY1NTIiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjUxNiIgeDI9IjU5MyIgeTE9Ijg4NS4xNjQzIiB5Mj0iODg1LjE2NDMiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjU4OSw5MzAuODI3Myw1OTMsOTQwLjgyNzMsNTk3LDkzMC44MjczLDU5Myw5MzQuODI3MyIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iNTkzIiB4Mj0iNTkzIiB5MT0iODg1LjE2NDMiIHkyPSI5OTAuOTk5NCIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjU5MyIgeDI9IjQxMSIgeTE9Ijk5MC45OTk0IiB5Mj0iOTkwLjk5OTQiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjQyMSw5ODYuOTk5NCw0MTEsOTkwLjk5OTQsNDIxLDk5NC45OTk0LDQxNyw5OTAuOTk5NCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iOTU4Ljk5OTQiIHkyPSI5NzguOTk5NCIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDk2OC45OTk0LDM5OSw5NzguOTk5NCw0MDMsOTY4Ljk5OTQsMzk5LDk3Mi45OTk0IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSIxMTA4LjgzNDQiIHkyPSIxMTQzLjgzNDQiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSwxMTMzLjgzNDQsMzk5LDExNDMuODM0NCw0MDMsMTEzMy44MzQ0LDM5OSwxMTM3LjgzNDQiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9IjEwNDYuOTk5NCIgeTI9IjEwNzIuNDkwMyIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDEwNjIuNDkwMywzOTksMTA3Mi40OTAzLDQwMywxMDYyLjQ5MDMsMzk5LDEwNjYuNDkwMyIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iNTAxIiB4Mj0iNjIwLjUiIHkxPSIxMDM0Ljk5OTQiIHkyPSIxMDM0Ljk5OTQiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjYxNi41LDExMTYuMzM0NCw2MjAuNSwxMTI2LjMzNDQsNjI0LjUsMTExNi4zMzQ0LDYyMC41LDExMjAuMzM0NCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iNjIwLjUiIHgyPSI2MjAuNSIgeTE9IjEwMzQuOTk5NCIgeTI9IjEyMTIuMTc4NSIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjYyMC41IiB4Mj0iNDExIiB5MT0iMTIxMi4xNzg1IiB5Mj0iMTIxMi4xNzg1Ii8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSI0MjEsMTIwOC4xNzg1LDQxMSwxMjEyLjE3ODUsNDIxLDEyMTYuMTc4NSw0MTcsMTIxMi4xNzg1IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSIxMTgwLjE3ODUiIHkyPSIxMjAwLjE3ODUiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSwxMTkwLjE3ODUsMzk5LDEyMDAuMTc4NSw0MDMsMTE5MC4xNzg1LDM5OSwxMTk0LjE3ODUiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9IjEwMDIuOTk5NCIgeTI9IjEwMjIuOTk5NCIvPjxwb2x5Z29uIGZpbGw9IiMxODE4MTgiIHBvaW50cz0iMzk1LDEwMTIuOTk5NCwzOTksMTAyMi45OTk0LDQwMywxMDEyLjk5OTQsMzk5LDEwMTYuOTk5NCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iMTIyNC4xNzg1IiB5Mj0iMTI0NC4xNzg1Ii8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsMTIzNC4xNzg1LDM5OSwxMjQ0LjE3ODUsNDAzLDEyMzQuMTc4NSwzOTksMTIzOC4xNzg1IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSI4NDcuNjczNCIgeTI9Ijg3My4xNjQzIi8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsODYzLjE2NDMsMzk5LDg3My4xNjQzLDQwMyw4NjMuMTY0MywzOTksODY3LjE2NDMiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjQzNiIgeDI9IjUxNSIgeTE9IjgzNS42NzM0IiB5Mj0iODM1LjY3MzQiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjUwNSw4MzEuNjczNCw1MTUsODM1LjY3MzQsNTA1LDgzOS42NzM0LDUwOSw4MzUuNjczNCIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7IiB4MT0iMzk5IiB4Mj0iMzk5IiB5MT0iMTI4MC41MjI2IiB5Mj0iMTMwMC41MjI2Ii8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsMTI5MC41MjI2LDM5OSwxMzAwLjUyMjYsNDAzLDEyOTAuNTIyNiwzOTksMTI5NC41MjI2IiBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMxODE4MTg7c3Ryb2tlLXdpZHRoOjEuMDsiIHgxPSIzOTkiIHgyPSIzOTkiIHkxPSI4MDMuNjczNCIgeTI9IjgyMy42NzM0Ii8+PHBvbHlnb24gZmlsbD0iIzE4MTgxOCIgcG9pbnRzPSIzOTUsODEzLjY3MzQsMzk5LDgyMy42NzM0LDQwMyw4MTMuNjczNCwzOTksODE3LjY3MzQiIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzE4MTgxODtzdHJva2Utd2lkdGg6MS4wOyIgeDE9IjM5OSIgeDI9IjM5OSIgeTE9IjE4My43NTYxIiB5Mj0iMjMyLjgyNDEiLz48cG9seWdvbiBmaWxsPSIjMTgxODE4IiBwb2ludHM9IjM5NSwyMjIuODI0MSwzOTksMjMyLjgyNDEsNDAzLDIyMi44MjQxLDM5OSwyMjYuODI0MSIgc3R5bGU9InN0cm9rZTojMTgxODE4O3N0cm9rZS13aWR0aDoxLjA7Ii8+PCEtLVNSQz1baExIRFJuZW40QnRkTHVtdTRMSHdwMjR2TEFZZUFaUzRvaGFvellvdUNWUk03YURIYlZfVWlSdGkxb0dnYU5mWUZId19VT3p2Q20wMGZDYVdoNlJwblZDUVppd19MQ09UZU44VTM4ZzA4OG5uS2YxczVoRzVzWTdDbHR6VDdYdVdlMy1XbHNDOFU1bUEtTzg0QXZJZTN3WVVJZEdURHZRWlFwR2U0cTA5MW5BVXliVkQ3cGVaWm9RSmE5Q1ZWZUlTRlBjMEo5S1paVTFGSmNReTVQQk5VNDNtYzhlTktmTHRVell2R0ZuMENobDRGQjZLcU5rcUxBdzRMTDBBVjd2Z3E3RzV1cktWbnFNaGxqb2RqblI2OW1wdEpVOVJnSHU0QTVyTGM0aUt2QlZWUGpmNUt3UExTZTVTRlpjQkhLMHd6NUlyQjA3R1MxRjZyZE1HUUJkcU5HXy1YZHo1UjhORTNQSWZmcHFZSG43b21xR0dMUV9rZlVZWmZicTFLbzI4SXN1WU9OWE56M0VOa1NoLWJ6UGEzYlZWQUZ0b2pkX3RrRU9KcEFKNDRBT2hfMUtuTjdNLVRaUldjTndKY2R2ZzZkUi1jblR4TERDNVV0dXdLdWdQWHF1SXI0YzlsUTd2TlVGdFdrSUUzTklFOUJDd0l6b1dhMC1COU9YSXZtQ3M2MzJUQmZxcF92TmFEV2dsazlVUkstNWhUM0EtRDY5MGFnNWM4VVlqdlQ3RTJ1NjEtZ2RTemVQQTF0S0c5bDhXTDMxYV92NDlNQmV3NmE3NXJwdEpQeWtVdExPQ1ZqUXBjRVVUYTJ2aEhxbjgyekRtcDVBMUh4cGgwbURHejlRVmptUGs1Y1BQV1l0UEdoTnJ0Sy1rc1I3a1VSaWxlMHNfTllfNWw4QXhkVlc5c1gwbDdablVIM1JRZ2VqR19YWjQ1Nzk3eDMyNmxzbGppVGt2T05Hdkh4bVNJVGxqVVBQMFg5RUxFLSAtaVl5NlM2bHNsVm00MF0tLT48L2c+PC9zdmc+" y="1492.1358"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="4" x="17" y="2944.1018">&#160;</text>
+ </g>
+ <!--SRC=[xLLDRnen4BtpAqOv4LHwYlQG72fKL9NR2fHJLKN6EmjkZBsrlKjHn7_ls5xO3wXArBJgWGlOu_6RDplFCv8jKS8JV1aC05vUw0V02ISHvfFfx741MssUSwcta6i34fb5O59gpfpG2eG2jqQOVlwOLdTWqLHe0iPzolWpEfWZHr5XzX2jYtAfoBf0YTm3HBDrpBYmAcWX0lBDQ6I3yzsJ3TwZ4N0VkNGt47aItXoT4LWX6FJ18wdSw0sijNLqWHCxpzopvAKng5myOYg36E3zGuqcSXZEspYwLDc7MtzNmN27zhPsFETgW44cyXn3Y8Xy_cwW7NCAjA8lE1rsMc5Cm6zQMHrP0g2a8WoLRY1HKUX12_w2hy9MJ8G2Sb_J5f4ji-6Y9uXPAsu_wQrmwmYM0NCKSbawjBzC-f72d7Z_BbSl3brSc7h_j7rslw0TJ3X7QyTp_5wYFQbyePN50_tQrVyLhblvJuPjC1lFql4aouYtRHA2mkUXBYXzK_ODStnDyZbOl5IrSbGU6pQp55YCymeHT7ccMiit9RagcSceaijTv2k5bpsNh4EIe2RMYfMYXntQ0G6rNQdeDPK6gcAof6UGGvVzQ_IVwgAKp4LLjoGV17lGMfJt8P_PD7G4VrXeHuG4apN7a2hG0s_gG02kD8gk7-LRjylO0eza8zMZwhvHp8PrIzdj18xc_MbB30suwOZlu6YYjeFRdYKiEhOCwVEhhWr_5C9WqrxjJV_9K1zqGgqEpmQOtIc-DbhfqifTdQndkz__-HX9_iuOIQvZv3f6_jCnKYNNGN8T9Fz-a2IdWoGv6IJUiXGgwvdwioKvCrkIjvujNmS_0G00]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/radius.uml b/doc/sphinx/uml/radius.uml
new file mode 100644
index 0000000..8baad5b
--- /dev/null
+++ b/doc/sphinx/uml/radius.uml
@@ -0,0 +1,91 @@
+@startuml
+
+label _ [
+
+ {{
+ title RADIUS workflow for lease allocation in the DHCPv4 server
+ :Packet Received;
+ :Subnet Selection;
+ start
+ partition "**subnet4_select** callout" {
+ :Retrieve reservation from host cache for the current host and subnet.;
+ if (Reservation found?) then (yes)
+ if (Reservation for a different subnet?) then (yes)
+ :Reselect the subnet to the one from the reservation.;
+ else (no)
+ endif
+ :Retrieve reservation from host cache again in case the subnet was reselected.;
+ if (Reservation with cached attributes?) then (yes)
+ :Use cached attributes from the reservation.;
+ stop
+ else (no)
+ endif
+ else (no)
+ endif
+ :Send Access-Request.;
+ if (Response) then (Access-Accept)
+ if (Framed-IP-Address attribute present?) then (yes)
+ :Reselect subnet to match a subnet the contains the IP address.;
+ else (no)
+ endif
+ if (Framed-IP-Pool attribute present?) then (yes)
+ :Reselect subnet to match a subnet guarded by the client class.;
+ :Assign packet to client class represented by the value of Framed-IP-Pool.;
+ else (no)
+ endif
+ :Populate the host cache with the reselected subnet ID and the potential class guard or IP address returned from the RADIUS server.;
+ else (Access-Reject)
+ stop
+ endif
+ stop
+ }
+ partition "**lease4_select**, **lease4_renew**, **lease4_release**, **lease4_decline**, **lease4_expire** callouts"
+ :Send Accounting-Request asynchronously.;
+ }
+ }}
+
+ {{
+ title RADIUS workflow for lease allocation in the DHCPv6 server
+ :Packet Received;
+ :Subnet Selection;
+ start
+ partition "**subnet6_select** callout" {
+ :Retrieve reservation from host cache for the current host and subnet.;
+ if (Reservation found?) then (yes)
+ if (Reservation for a different subnet?) then (yes)
+ :Reselect the subnet to the one from the reservation.;
+ else (no)
+ endif
+ :Retrieve reservation from host cache again in case the subnet was reselected.;
+ if (Reservation with cached attributes?) then (yes)
+ :Use cached attributes from the reservation.;
+ stop
+ else (no)
+ endif
+ else (no)
+ endif
+ :Send Access-Request.;
+ if (Response) then (Access-Accept)
+ if (Framed-IPv6-Address attribute present?) then (yes)
+ :Reselect subnet to match a subnet the contains the IP address.;
+ else (no)
+ endif
+ if (Framed-IP-Pool attribute present?) then (yes)
+ :Reselect subnet to match a subnet guarded by the client class.;
+ :Assign packet to client class represented by the value of Framed-IP-Pool.;
+ else (no)
+ endif
+ :Populate the host cache with the reselected subnet ID and the potential class guard or IP address returned from the RADIUS server.;
+ else (Access-Reject)
+ stop
+ endif
+ stop
+ }
+ partition "**lease6_select**, **lease6_renew**, **lease6_rebind**, **lease6_release**, **lease6_decline**, **lease6_expire** callouts"
+ :Send Accounting-Request asynchronously.;
+ }
+ }}
+
+]
+
+@enduml
diff --git a/doc/sphinx/uml/recognizing-same-client.png b/doc/sphinx/uml/recognizing-same-client.png
new file mode 100644
index 0000000..3a5a526
--- /dev/null
+++ b/doc/sphinx/uml/recognizing-same-client.png
Binary files differ
diff --git a/doc/sphinx/uml/recognizing-same-client.svg b/doc/sphinx/uml/recognizing-same-client.svg
new file mode 100644
index 0000000..37f5cbc
--- /dev/null
+++ b/doc/sphinx/uml/recognizing-same-client.svg
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="472px" preserveAspectRatio="none" style="width:1806px;height:472px;background:#FFFFFF;" version="1.1" viewBox="0 0 1806 472" width="1806px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="527" x="638.375" y="34.9659">How Kea Recognizes the Same Client In Different DHCP Messages (Kea 2.4.0)</text>
+ <ellipse cx="765.4688" cy="60.0679" fill="#222222" rx="10" ry="10" style="stroke:#222222;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="699.4688,90.0679,831.4688,90.0679,843.4688,102.0679,831.4688,114.0679,699.4688,114.0679,687.4688,102.0679,699.4688,90.0679" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="132" x="699.4688" y="106.3359">libdhcp_flex_id.so is used</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="670.4688" y="98.8449">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="843.4688" y="98.8449">no</text>
+ <polygon fill="#F1F1F1" points="158.6875,124.0679,344.6875,124.0679,356.6875,136.0679,344.6875,148.0679,158.6875,148.0679,146.6875,136.0679,158.6875,124.0679" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="186" x="158.6875" y="140.3359">replace-client-id is true (the default)</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="129.6875" y="132.8449">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="356.6875" y="132.8449">no</text>
+ <rect fill="#98FB98" height="101.7206" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="129" x="11" y="158.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="109" x="21" y="180.896">Client is recognized</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="104" x="21" y="197.2401">by the result of the</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="50" x="21" y="213.5842">identifier</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="74" x="21" y="229.9283">expression in</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="99" x="21" y="246.2725">libdhcp_flex_id.so.</text>
+ <ellipse cx="75.5" cy="305.7885" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="75.5" cy="305.7885" fill="#222222" rx="6" ry="6" style="stroke:#222222;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="367.375,158.0679,488.375,158.0679,500.375,170.0679,488.375,182.0679,367.375,182.0679,355.375,170.0679,367.375,158.0679" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="121" x="367.375" y="174.3359">client has a reservation</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="338.375" y="166.8449">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="500.375" y="166.8449">no</text>
+ <polygon fill="#F1F1F1" points="318,242.9774,318,242.9774,330,254.9774,318,266.9774,318,266.9774,306,254.9774,318,242.9774" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="83" x="170" y="236.7725">for the purpose</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="136" x="170" y="251.7544">of acquiring a reservation</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="83" x="330" y="191.8268">for the purpose</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="102" x="330" y="206.8087">of leasing outside a</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="128" x="330" y="221.7906">reservation or acquiring</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="124" x="330" y="236.7725">any other value outside</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="79" x="330" y="251.7544">of reservations</text>
+ <rect fill="#98FB98" height="101.7206" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="129" x="181.75" y="276.9774"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="109" x="191.75" y="299.8054">Client is recognized</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="104" x="191.75" y="316.1496">by the result of the</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="50" x="191.75" y="332.4937">identifier</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="74" x="191.75" y="348.8378">expression in</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="99" x="191.75" y="365.1819">libdhcp_flex_id.so.</text>
+ <ellipse cx="246.25" cy="424.6979" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="246.25" cy="424.6979" fill="#222222" rx="6" ry="6" style="stroke:#222222;stroke-width:1.0;"/>
+ <rect fill="#F1F1F1" height="101.7206" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="118" x="330.75" y="276.9774"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="84" x="340.75" y="299.8054">Go through the</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="98" x="340.75" y="316.1496">diagram from the</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="81" x="340.75" y="332.4937">beginning as if</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="96" x="340.75" y="348.8378">libdhcp_flex_id.so</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="56" x="340.75" y="365.1819">is unused.</text>
+ <ellipse cx="389.75" cy="424.6979" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="389.75" cy="424.6979" fill="#222222" rx="6" ry="6" style="stroke:#222222;stroke-width:1.0;"/>
+ <rect fill="#F1F1F1" height="101.7206" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="118" x="478.75" y="192.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="84" x="488.75" y="214.896">Go through the</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="98" x="488.75" y="231.2401">diagram from the</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="81" x="488.75" y="247.5842">beginning as if</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="96" x="488.75" y="263.9283">libdhcp_flex_id.so</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="56" x="488.75" y="280.2725">is unused.</text>
+ <ellipse cx="537.75" cy="339.7885" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="537.75" cy="339.7885" fill="#222222" rx="6" ry="6" style="stroke:#222222;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="1305.75,124.0679,1352.75,124.0679,1364.75,146.5407,1352.75,169.0136,1305.75,169.0136,1293.75,146.5407,1305.75,124.0679" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="42" x="1305.75" y="135.8268">DHCPv4</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="12" x="1305.75" y="150.8087">or</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="47" x="1305.75" y="165.7906">DHCPv6?</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="42" x="1251.75" y="143.3178">DHCPv4</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="42" x="1364.75" y="143.3178">DHCPv6</text>
+ <polygon fill="#F1F1F1" points="1052.25,179.0136,1104.25,179.0136,1116.25,201.4864,1104.25,223.9592,1052.25,223.9592,1040.25,201.4864,1052.25,179.0136" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="24" x="1052.25" y="190.7725">MAC</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="52" x="1052.25" y="205.7544">address is</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="40" x="1052.25" y="220.7363">present</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="1023.25" y="198.2634">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="1116.25" y="198.2634">no</text>
+ <polygon fill="#F1F1F1" points="885.75,233.9592,963.75,233.9592,975.75,256.4321,963.75,278.9049,885.75,278.9049,873.75,256.4321,885.75,233.9592" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="78" x="885.75" y="245.7182">match-client-id</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="33" x="885.75" y="260.7">is true</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="63" x="885.75" y="275.6819">(the default)</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="856.75" y="253.2091">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="975.75" y="253.2091">no</text>
+ <polygon fill="#F1F1F1" points="693.25,288.9049,877.25,288.9049,889.25,300.9049,877.25,312.9049,693.25,312.9049,681.25,300.9049,693.25,288.9049" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="184" x="693.25" y="305.1729">client ID option (code 61) is present</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="664.25" y="297.6819">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="889.25" y="297.6819">no</text>
+ <rect fill="#98FB98" height="69.0323" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="129" x="606.75" y="322.9049"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="109" x="616.75" y="345.733">Client is recognized</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="83" x="616.75" y="362.0771">by the client ID</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="93" x="616.75" y="378.4212">option (code 61).</text>
+ <ellipse cx="671.25" cy="437.9373" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="671.25" cy="437.9373" fill="#222222" rx="6" ry="6" style="stroke:#222222;stroke-width:1.0;"/>
+ <rect fill="#98FB98" height="52.6882" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="135" x="831.75" y="322.9049"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="109" x="841.75" y="345.733">Client is recognized</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="115" x="841.75" y="362.0771">by the MAC address.</text>
+ <ellipse cx="899.25" cy="421.5931" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="899.25" cy="421.5931" fill="#222222" rx="6" ry="6" style="stroke:#222222;stroke-width:1.0;"/>
+ <rect fill="#98FB98" height="52.6882" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="135" x="996.75" y="288.9049"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="109" x="1006.75" y="311.733">Client is recognized</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="115" x="1006.75" y="328.0771">by the MAC address.</text>
+ <ellipse cx="1064.25" cy="387.5931" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="1064.25" cy="387.5931" fill="#222222" rx="6" ry="6" style="stroke:#222222;stroke-width:1.0;"/>
+ <rect fill="#FFC0CB" height="52.6882" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="140" x="1141.75" y="233.9592"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="120" x="1151.75" y="256.7873">DHCP message is not</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="69" x="1151.75" y="273.1314">well formed.</text>
+ <ellipse cx="1211.75" cy="332.6475" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="1211.75" cy="332.6475" fill="#222222" rx="6" ry="6" style="stroke:#222222;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="1555.75,179.0136,1644.75,179.0136,1656.75,201.4864,1644.75,223.9592,1555.75,223.9592,1543.75,201.4864,1555.75,179.0136" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="43" x="1555.75" y="190.7725">client ID</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="89" x="1555.75" y="205.7544">option (code 1) is</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="40" x="1555.75" y="220.7363">present</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="1526.75" y="198.2634">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="1656.75" y="198.2634">no</text>
+ <polygon fill="#F1F1F1" points="1434.75,233.9592,1514.75,233.9592,1526.75,271.414,1514.75,308.8687,1434.75,308.8687,1422.75,271.414,1434.75,233.9592" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="66" x="1434.75" y="245.7182">interested in</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="80" x="1434.75" y="260.7">identifying as a</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="69" x="1434.75" y="275.6819">single client -</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="68" x="1434.75" y="290.6638">the device or</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="46" x="1434.75" y="305.6457">the lease</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="33" x="1389.75" y="268.191">device</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="26" x="1526.75" y="268.191">lease</text>
+ <rect fill="#98FB98" height="85.3765" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="129" x="1331.75" y="318.8687"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="109" x="1341.75" y="341.6968">Client is recognized</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="83" x="1341.75" y="358.0409">by the client ID</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="106" x="1341.75" y="374.385">option (code 1) aka</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="34" x="1341.75" y="390.7291">DUID.</text>
+ <ellipse cx="1396.25" cy="450.2451" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="1396.25" cy="450.2451" fill="#222222" rx="6" ry="6" style="stroke:#222222;stroke-width:1.0;"/>
+ <rect fill="#98FB98" height="69.0323" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="145" x="1480.75" y="318.8687"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="109" x="1490.75" y="341.6968">Client is recognized</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="65" x="1490.75" y="358.0409">by the tuple</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="125" x="1490.75" y="374.385">&lt;DUID, IA-type, IAID&gt;.</text>
+ <ellipse cx="1553.25" cy="433.901" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="1553.25" cy="433.901" fill="#222222" rx="6" ry="6" style="stroke:#222222;stroke-width:1.0;"/>
+ <rect fill="#FFC0CB" height="52.6882" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="140" x="1655.75" y="233.9592"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="120" x="1665.75" y="256.7873">DHCP message is not</text>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="69" x="1665.75" y="273.1314">well formed.</text>
+ <ellipse cx="1725.75" cy="332.6475" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="1725.75" cy="332.6475" fill="#222222" rx="6" ry="6" style="stroke:#222222;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="75.5" x2="75.5" y1="259.7885" y2="294.7885"/>
+ <polygon fill="#181818" points="71.5,284.7885,75.5,294.7885,79.5,284.7885,75.5,288.7885" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="246.25" x2="246.25" y1="378.6979" y2="413.6979"/>
+ <polygon fill="#181818" points="242.25,403.6979,246.25,413.6979,250.25,403.6979,246.25,407.6979" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="389.75" x2="389.75" y1="378.6979" y2="413.6979"/>
+ <polygon fill="#181818" points="385.75,403.6979,389.75,413.6979,393.75,403.6979,389.75,407.6979" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="306" x2="246.25" y1="254.9774" y2="254.9774"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="246.25" x2="246.25" y1="254.9774" y2="276.9774"/>
+ <polygon fill="#181818" points="242.25,266.9774,246.25,276.9774,250.25,266.9774,246.25,270.9774" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="330" x2="389.75" y1="254.9774" y2="254.9774"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="389.75" x2="389.75" y1="254.9774" y2="276.9774"/>
+ <polygon fill="#181818" points="385.75,266.9774,389.75,276.9774,393.75,266.9774,389.75,270.9774" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="537.75" x2="537.75" y1="293.7885" y2="328.7885"/>
+ <polygon fill="#181818" points="533.75,318.7885,537.75,328.7885,541.75,318.7885,537.75,322.7885" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="355.375" x2="318" y1="170.0679" y2="170.0679"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="318" x2="318" y1="170.0679" y2="242.9774"/>
+ <polygon fill="#181818" points="314,232.9774,318,242.9774,322,232.9774,318,236.9774" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="500.375" x2="537.75" y1="170.0679" y2="170.0679"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="537.75" x2="537.75" y1="170.0679" y2="192.0679"/>
+ <polygon fill="#181818" points="533.75,182.0679,537.75,192.0679,541.75,182.0679,537.75,186.0679" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="146.6875" x2="75.5" y1="136.0679" y2="136.0679"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="75.5" x2="75.5" y1="136.0679" y2="158.0679"/>
+ <polygon fill="#181818" points="71.5,148.0679,75.5,158.0679,79.5,148.0679,75.5,152.0679" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="356.6875" x2="427.875" y1="136.0679" y2="136.0679"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="427.875" x2="427.875" y1="136.0679" y2="158.0679"/>
+ <polygon fill="#181818" points="423.875,148.0679,427.875,158.0679,431.875,148.0679,427.875,152.0679" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="671.25" x2="671.25" y1="391.9373" y2="426.9373"/>
+ <polygon fill="#181818" points="667.25,416.9373,671.25,426.9373,675.25,416.9373,671.25,420.9373" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="899.25" x2="899.25" y1="375.5931" y2="410.5931"/>
+ <polygon fill="#181818" points="895.25,400.5931,899.25,410.5931,903.25,400.5931,899.25,404.5931" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="681.25" x2="671.25" y1="300.9049" y2="300.9049"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="671.25" x2="671.25" y1="300.9049" y2="322.9049"/>
+ <polygon fill="#181818" points="667.25,312.9049,671.25,322.9049,675.25,312.9049,671.25,316.9049" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="889.25" x2="899.25" y1="300.9049" y2="300.9049"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="899.25" x2="899.25" y1="300.9049" y2="322.9049"/>
+ <polygon fill="#181818" points="895.25,312.9049,899.25,322.9049,903.25,312.9049,899.25,316.9049" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1064.25" x2="1064.25" y1="341.5931" y2="376.5931"/>
+ <polygon fill="#181818" points="1060.25,366.5931,1064.25,376.5931,1068.25,366.5931,1064.25,370.5931" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="873.75" x2="785.25" y1="256.4321" y2="256.4321"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="785.25" x2="785.25" y1="256.4321" y2="288.9049"/>
+ <polygon fill="#181818" points="781.25,278.9049,785.25,288.9049,789.25,278.9049,785.25,282.9049" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="975.75" x2="1064.25" y1="256.4321" y2="256.4321"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1064.25" x2="1064.25" y1="256.4321" y2="288.9049"/>
+ <polygon fill="#181818" points="1060.25,278.9049,1064.25,288.9049,1068.25,278.9049,1064.25,282.9049" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1211.75" x2="1211.75" y1="286.6475" y2="321.6475"/>
+ <polygon fill="#181818" points="1207.75,311.6475,1211.75,321.6475,1215.75,311.6475,1211.75,315.6475" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1040.25" x2="924.75" y1="201.4864" y2="201.4864"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="924.75" x2="924.75" y1="201.4864" y2="233.9592"/>
+ <polygon fill="#181818" points="920.75,223.9592,924.75,233.9592,928.75,223.9592,924.75,227.9592" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1116.25" x2="1211.75" y1="201.4864" y2="201.4864"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1211.75" x2="1211.75" y1="201.4864" y2="233.9592"/>
+ <polygon fill="#181818" points="1207.75,223.9592,1211.75,233.9592,1215.75,223.9592,1211.75,227.9592" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1396.25" x2="1396.25" y1="404.2451" y2="439.2451"/>
+ <polygon fill="#181818" points="1392.25,429.2451,1396.25,439.2451,1400.25,429.2451,1396.25,433.2451" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1553.25" x2="1553.25" y1="387.901" y2="422.901"/>
+ <polygon fill="#181818" points="1549.25,412.901,1553.25,422.901,1557.25,412.901,1553.25,416.901" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1422.75" x2="1396.25" y1="271.414" y2="271.414"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1396.25" x2="1396.25" y1="271.414" y2="318.8687"/>
+ <polygon fill="#181818" points="1392.25,308.8687,1396.25,318.8687,1400.25,308.8687,1396.25,312.8687" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1526.75" x2="1553.25" y1="271.414" y2="271.414"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1553.25" x2="1553.25" y1="271.414" y2="318.8687"/>
+ <polygon fill="#181818" points="1549.25,308.8687,1553.25,318.8687,1557.25,308.8687,1553.25,312.8687" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1725.75" x2="1725.75" y1="286.6475" y2="321.6475"/>
+ <polygon fill="#181818" points="1721.75,311.6475,1725.75,321.6475,1729.75,311.6475,1725.75,315.6475" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1543.75" x2="1474.75" y1="201.4864" y2="201.4864"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1474.75" x2="1474.75" y1="201.4864" y2="233.9592"/>
+ <polygon fill="#181818" points="1470.75,223.9592,1474.75,233.9592,1478.75,223.9592,1474.75,227.9592" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1656.75" x2="1725.75" y1="201.4864" y2="201.4864"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1725.75" x2="1725.75" y1="201.4864" y2="233.9592"/>
+ <polygon fill="#181818" points="1721.75,223.9592,1725.75,233.9592,1729.75,223.9592,1725.75,227.9592" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1293.75" x2="1078.25" y1="146.5407" y2="146.5407"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1078.25" x2="1078.25" y1="146.5407" y2="179.0136"/>
+ <polygon fill="#181818" points="1074.25,169.0136,1078.25,179.0136,1082.25,169.0136,1078.25,173.0136" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1364.75" x2="1600.25" y1="146.5407" y2="146.5407"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1600.25" x2="1600.25" y1="146.5407" y2="179.0136"/>
+ <polygon fill="#181818" points="1596.25,169.0136,1600.25,179.0136,1604.25,169.0136,1600.25,173.0136" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="687.4688" x2="251.6875" y1="102.0679" y2="102.0679"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="251.6875" x2="251.6875" y1="102.0679" y2="124.0679"/>
+ <polygon fill="#181818" points="247.6875,114.0679,251.6875,124.0679,255.6875,114.0679,251.6875,118.0679" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="843.4688" x2="1329.25" y1="102.0679" y2="102.0679"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1329.25" x2="1329.25" y1="102.0679" y2="124.0679"/>
+ <polygon fill="#181818" points="1325.25,114.0679,1329.25,124.0679,1333.25,114.0679,1329.25,118.0679" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="765.4688" x2="765.4688" y1="70.0679" y2="90.0679"/>
+ <polygon fill="#181818" points="761.4688,80.0679,765.4688,90.0679,769.4688,80.0679,765.4688,84.0679" style="stroke:#181818;stroke-width:1.0;"/>
+ <!--SRC=[nLLDRzim3BthLn3eeGcGfDjGzD0D6ue6M8CjmD2jvqAnQKUeB7cIdDJxzIDbEz_9sbvsIMnBudkFVAGkpiMlkVBmCFqEbKSFOOvWS0c-Da4-Wyr09a4jLAWXLJ9til2GELj0gQK9LQ57ui5areNAo82w7e0A8BMtuD1NEdXG1YGKrY4iLOhWIuSovS0CrMnI9XSBvIkfEv01K8IuFynbu9WpJ6n112l3v-LCKxmvKkmgs486bKYjQ-7f3rD8xT8mC4FSt4z744Kgts9ARmqGwVWgyvnOUBMWWDQbOkbaMH8ZGjHs2KC8blSc67Ut_FZZ345RawC3QLBG52Co7l2ha9vKD-eeH9JVyHw9ymiXV90k215K82Lt1FGD9ToJoDoeFsqXVie2uLOhD04c1iOgoz3no_ZkzWTC2L7cjBN7Hp-CBaVl-aAe37fQpT9vKZvc6fyVLJgYN99sgc_QvxW6UZNwlW3WpGwfb0aEauWpL6dCawiGUb4mPf94z7TE0foLb6fA7PhhbYATSvs0DEwPrHjLRciU5oXF9gXCeOkl-5p66bYkMlooBs7qCIxuO4jwGEs9eB4D5zRHy8Tvbtbq2xA5DNl4c-tTvyu6PULAwr54n_-kb6CBR0TgJxzC-adv1r9mE0qdKx6HZdWwfkI07as6PpMs2fvRIeeDLK3xLuA5D3LOEkvW8JLPe3t2KJRE-7KghhzQWdEsokTRwhe-YRDYSs66kJ8c9fSwCZkXSfqfxdfsxn7T9ctZR9lY6BaZr0uJEqfhdzIAKaUe-TyanSRZtbrS2kj4VBhwqZcnMLWRUdfpAsIQiYC8JR0ro357x4mZC9clUrYqFInEz_1Exqp6OCjeYbvYoIXNx_kiypZoQvfXrGuhi8sbNTYDIc_LUh_6Rw90gOKsjIUGLhuwXFhA6N0OyKKkFgFRwEawZloY6VaCPMnervUeDTzGnKalng2Du_Pc9fLYgm2nxFymd3A1xY8VwBQbMTKEixfj7obuuEXLhOUYiU52qJLApkStdahO1M-MtfpTVKljwf5FShKsVfYCJutGXjbRkOIgrEjnzud11Z2v6OQwH7wOZ3__7mSqqoZ-_WK0]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/recognizing-same-client.uml b/doc/sphinx/uml/recognizing-same-client.uml
new file mode 100644
index 0000000..ef5b965
--- /dev/null
+++ b/doc/sphinx/uml/recognizing-same-client.uml
@@ -0,0 +1,113 @@
+@startuml
+
+/'
+This UML uses the new syntax of activity diagrams from plantuml.
+Unfortunately, it also results in a more wide spread of the resulting
+visual diagram, so wide that it becomes unreadable when automatically
+scaled down in the ARM. This is the reason for the aggressive word
+wrapping below - to force the diagram to be longer and less wide, and
+as a result - more readable.
+'/
+
+start
+
+title How Kea Recognizes the Same Client In Different DHCP Messages (Kea 2.4.0)
+
+if (libdhcp_flex_id.so is used) then (yes)
+ if (replace-client-id is true (the default)) then (yes)
+ #palegreen:Client is recognized
+ by the result of the
+ identifier
+ expression in
+ libdhcp_flex_id.so.;
+ stop
+ else (no)
+ if (client has a reservation) then (yes)
+ if () then (for the purpose
+of acquiring a reservation)
+ #palegreen:Client is recognized
+ by the result of the
+ identifier
+ expression in
+ libdhcp_flex_id.so.;
+ stop
+ else (for the purpose
+of leasing outside a
+reservation or acquiring
+any other value outside
+of reservations)
+ :Go through the
+ diagram from the
+ beginning as if
+ libdhcp_flex_id.so
+ is unused.;
+ stop
+ endif
+ else (no)
+ :Go through the
+ diagram from the
+ beginning as if
+ libdhcp_flex_id.so
+ is unused.;
+ stop
+ endif
+ endif
+else (no)
+ if (DHCPv4
+or
+DHCPv6?) then (DHCPv4)
+ if (MAC
+address is
+present) then (yes)
+ if (match-client-id
+is true
+(the default)) then (yes)
+ if (client ID option (code 61) is present) then (yes)
+ #palegreen:Client is recognized
+ by the client ID
+ option (code 61).;
+ stop
+ else (no)
+ #palegreen:Client is recognized
+ by the MAC address.;
+ stop
+ endif
+ else (no)
+ #palegreen:Client is recognized
+ by the MAC address.;
+ stop
+ endif
+ else (no)
+ #pink:DHCP message is not
+ well formed.;
+ stop
+ endif
+ else (DHCPv6)
+ if (client ID
+option (code 1) is
+present) then (yes)
+ if (interested in
+identifying as a
+single client -
+the device or
+the lease) then (device)
+ #palegreen:Client is recognized
+ by the client ID
+ option (code 1) aka
+ DUID.;
+ stop
+ else (lease)
+ #palegreen:Client is recognized
+ by the tuple
+ <DUID, IA-type, IAID>.;
+ stop
+ endif
+ else (no)
+ #pink:DHCP message is not
+ well formed.;
+ stop
+ endif
+ endif
+endif
+
+@enduml
diff --git a/doc/sphinx/uml/request4-lease.png b/doc/sphinx/uml/request4-lease.png
new file mode 100644
index 0000000..23b95cd
--- /dev/null
+++ b/doc/sphinx/uml/request4-lease.png
Binary files differ
diff --git a/doc/sphinx/uml/request4-lease.svg b/doc/sphinx/uml/request4-lease.svg
new file mode 100644
index 0000000..2a8a0a2
--- /dev/null
+++ b/doc/sphinx/uml/request4-lease.svg
@@ -0,0 +1,437 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="2463px" preserveAspectRatio="none" style="width:2209px;height:2463px;background:#FFFFFF;" version="1.1" viewBox="0 0 2209 2463" width="2209px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="312" x="941.5" y="24.9659">Allocate a lease for DHCPREQUEST (Kea 1.8.0)</text>
+ <!--cluster check_hint-->
+ <g id="cluster_check_hint">
+ <rect fill="none" height="392.85" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="926" x="903" y="277.2079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="174" x="1279" y="294.1739">Check requested address</text>
+ </g>
+ <!--cluster get_existing-->
+ <g id="cluster_get_existing">
+ <rect fill="none" height="172.92" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="235" x="1554" y="379.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="219" x="1562" y="396.0339">Get lease for requested address</text>
+ </g>
+ <!--cluster check_done-->
+ <g id="cluster_check_done">
+ <rect fill="none" height="172.93" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="154" x="447" y="737.0579"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="123" x="462.5" y="754.0239">Check client lease</text>
+ </g>
+ <!--cluster new_lease-->
+ <g id="cluster_new_lease">
+ <rect fill="none" height="1404.29" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="1087" x="7" y="958.9879"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="140" x="480.5" y="975.9539">Allocate a new lease</text>
+ </g>
+ <!--cluster allocateOrReuseLease4-->
+ <g id="cluster_allocateOrReuseLease4">
+ <rect fill="none" height="710.3" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="293" x="55" y="1526.9079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="156" x="123.5" y="1543.8739">Allocate or reuse lease</text>
+ </g>
+ <!--cluster allocateUnreservedLease4-->
+ <g id="cluster_allocateUnreservedLease4">
+ <rect fill="none" height="614.02" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="502" x="388" y="1086.8479"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="177" x="550.5" y="1103.8139">Allocate unreserved lease</text>
+ </g>
+ <!--entity getReservedLease-->
+ <g id="elem_getReservedLease">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="243" x="1086.5" y="488.9279"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="223" x="1096.5" y="513.8939">Check lease for reserved address</text>
+ </g>
+ <!--entity out_of_pool-->
+ <g id="elem_out_of_pool">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="196" x="973" y="606.9879"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="176" x="983" y="631.9539">Check out-of-pool address</text>
+ </g>
+ <!--entity existing-->
+ <g id="elem_existing">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="172" x="1593" y="488.9279"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="152" x="1603" y="513.8939">Check requested lease</text>
+ </g>
+ <!--entity client_lease-->
+ <g id="elem_client_lease">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="106" x="471" y="846.9179"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="86" x="481" y="871.8839">Check renew</text>
+ </g>
+ <!--entity create-->
+ <g id="elem_create">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="147" x="922.5" y="1773.8679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="127" x="932.5" y="1798.8339">Create a new lease</text>
+ </g>
+ <!--entity old_lease-->
+ <g id="elem_old_lease">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="127" x="703.5" y="2300.2079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="107" x="713.5" y="2325.1739">Delete old lease</text>
+ </g>
+ <!--entity candidate-->
+ <g id="elem_candidate">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="153" x="166.5" y="1637.8079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="133" x="176.5" y="1662.7739">Get candidate lease</text>
+ </g>
+ <!--entity reuseExpiredLease4-->
+ <g id="elem_reuseExpiredLease4">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="155" x="104.5" y="1773.8679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="135" x="114.5" y="1798.8339">Reuse expired lease</text>
+ </g>
+ <!--entity reclaimExpiredLease-->
+ <g id="elem_reclaimExpiredLease">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="167" x="97.5" y="1873.9379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="147" x="107.5" y="1898.9039">Reclaim expired lease</text>
+ </g>
+ <!--entity updateLease4Information-->
+ <g id="elem_updateLease4Information">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="190" x="81" y="1974.0079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="170" x="91" y="1998.9739">update lease information</text>
+ </g>
+ <!--entity lease4_select-->
+ <g id="elem_lease4_select">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="159" x="78.5" y="2074.0779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="139" x="88.5" y="2099.0439">Callout lease4_select</text>
+ </g>
+ <g id="elem_GMN23">
+ <path d="M272,2079.7579 L272,2089.6079 L237.84,2093.6079 L272,2097.6079 L272,2107.4639 A0,0 0 0 0 272,2107.4639 L324,2107.4639 A0,0 0 0 0 324,2107.4639 L324,2089.7579 L314,2079.7579 L272,2079.7579 A0,0 0 0 0 272,2079.7579 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M314,2079.7579 L314,2089.7579 L324,2089.7579 L314,2079.7579 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="31" x="278" y="2098.6549">hook</text>
+ </g>
+ <!--entity updateLease4-->
+ <g id="elem_updateLease4">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="108" x="188" y="2174.1479"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="88" x="198" y="2199.1139">Update lease</text>
+ </g>
+ <!--entity iterate-->
+ <g id="elem_iterate">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="193" x="426.5" y="1178.7079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="173" x="436.5" y="1203.6739">Iterate pools and subnets</text>
+ </g>
+ <!--entity pick-->
+ <g id="elem_pick">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="104" x="486" y="1296.7779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="84" x="496" y="1321.7439">Pick address</text>
+ </g>
+ <!--entity pick_reserved-->
+ <g id="elem_pick_reserved">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="198" x="607" y="1414.8379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="178" x="617" y="1439.8039">Check reserved addressed</text>
+ </g>
+ <!--entity mt_in_use-->
+ <g id="elem_mt_in_use">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="285" x="482.5" y="1537.7379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="265" x="492.5" y="1562.7039">Check already in use by another thread</text>
+ </g>
+ <!--entity pick_lease-->
+ <g id="elem_pick_lease">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="99" x="451.5" y="1637.8079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="79" x="461.5" y="1662.7739">Check lease</text>
+ </g>
+ <!--entity findClientLease-->
+ <g id="elem_findClientLease">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="129" x="1335.5" y="47.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="109" x="1345.5" y="72.0339">Find client lease</text>
+ </g>
+ <g id="elem_GMN3">
+ <path d="M1500,52.7479 L1500,62.5979 L1464.97,66.5979 L1500,70.5979 L1500,80.4539 A0,0 0 0 0 1500,80.4539 L1588,80.4539 A0,0 0 0 0 1588,80.4539 L1588,62.7479 L1578,52.7479 L1500,52.7479 A0,0 0 0 0 1500,52.7479 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M1578,52.7479 L1578,62.7479 L1588,62.7479 L1578,52.7479 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="67" x="1506" y="71.6449">entry point</text>
+ </g>
+ <!--entity addressReserved-->
+ <g id="elem_addressReserved">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="213" x="1564.5" y="165.1379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="193" x="1574.5" y="190.1039">Check requested reservation</text>
+ </g>
+ <!--entity hasAddressReservation-->
+ <g id="elem_hasAddressReservation">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="123" x="1068.5" y="165.1379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="103" x="1078.5" y="190.1039">Get reservation</text>
+ </g>
+ <!--entity update_hint-->
+ <g id="elem_update_hint">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="200" x="1330" y="165.1379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="180" x="1340" y="190.1039">Update requested address</text>
+ </g>
+ <!--entity no_lease-->
+ <g id="elem_no_lease">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="126" x="1382" y="2300.2079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="106" x="1392" y="2325.1739">Return no lease</text>
+ </g>
+ <g id="elem_GMN36">
+ <path d="M1543,2305.8979 L1543,2315.7479 L1508.42,2319.7479 L1543,2323.7479 L1543,2333.6039 A0,0 0 0 0 1543,2333.6039 L1621,2333.6039 A0,0 0 0 0 1621,2333.6039 L1621,2315.8979 L1611,2305.8979 L1543,2305.8979 A0,0 0 0 0 1543,2305.8979 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M1611,2305.8979 L1611,2315.8979 L1621,2315.8979 L1611,2305.8979 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="57" x="1549" y="2324.7949">exit point</text>
+ </g>
+ <!--entity renew-->
+ <g id="elem_renew">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="168" x="927" y="846.9179"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="148" x="937" y="871.8839">Return renewed lease</text>
+ </g>
+ <g id="elem_GMN40">
+ <path d="M1130,852.5979 L1130,862.4479 L1095.25,866.4479 L1130,870.4479 L1130,880.3039 A0,0 0 0 0 1130,880.3039 L1208,880.3039 A0,0 0 0 0 1208,880.3039 L1208,862.5979 L1198,852.5979 L1130,852.5979 A0,0 0 0 0 1130,852.5979 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M1198,852.5979 L1198,862.5979 L1208,862.5979 L1198,852.5979 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="57" x="1136" y="871.4949">exit point</text>
+ </g>
+ <!--entity return-->
+ <g id="elem_return">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="105" x="714.5" y="2418.2779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="85" x="724.5" y="2443.2439">Return lease</text>
+ </g>
+ <g id="elem_GMN44">
+ <path d="M854,2423.9579 L854,2433.8179 L819.66,2437.8179 L854,2441.8179 L854,2451.6639 A0,0 0 0 0 854,2451.6639 L932,2451.6639 A0,0 0 0 0 932,2451.6639 L932,2433.9579 L922,2423.9579 L854,2423.9579 A0,0 0 0 0 854,2423.9579 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M922,2423.9579 L922,2433.9579 L932,2433.9579 L922,2423.9579 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="57" x="860" y="2442.8549">exit point</text>
+ </g>
+ <!--link findClientLease to addressReserved-->
+ <g id="link_findClientLease_addressReserved">
+ <path d="M1444.14,86.5079 C1492.67,107.2879 1570.45,140.6079 1621.14,162.3179 " fill="none" id="findClientLease-to-addressReserved" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1625.48,164.1779,1618.7834,156.9561,1620.8843,162.2083,1615.632,164.3092,1625.48,164.1779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="170" x="1552" y="131.0349">has requested address (hint)</text>
+ </g>
+ <!--link findClientLease to hasAddressReservation-->
+ <g id="link_findClientLease_hasAddressReservation">
+ <path d="M1356.02,86.5079 C1307.67,107.2879 1230.18,140.6079 1179.68,162.3179 " fill="none" id="findClientLease-to-hasAddressReservation" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1175.35,164.1679,1185.198,164.2992,1179.9457,162.1983,1182.0466,156.9461,1175.35,164.1679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="165" x="1282" y="131.0349">no requested address (hint)</text>
+ </g>
+ <!--link addressReserved to check_hint-->
+ <g id="link_addressReserved_check_hint">
+ <path d="M1653.97,204.5579 C1638.88,221.2929 1616.365,246.2654 1597.3888,267.3129 C1595.0167,269.9439 1592.7,272.5135 1590.4599,274.998 C1589.8999,275.6191 1589.3447,276.2349 1588.7946,276.8451 " fill="none" id="addressReserved-to-check_hint" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1588.7946,276.8451,1597.792,272.8392,1592.1427,273.1315,1591.8504,267.4822,1588.7946,276.8451" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="152" x="1627" y="249.1049">no conflicting reservation</text>
+ </g>
+ <!--link addressReserved to no_lease-->
+ <g id="link_addressReserved_no_lease">
+ <path d="M1771.76,204.6379 C1863.26,224.8279 1984,260.2479 1984,306.5679 C1984,306.5679 1984,306.5679 1984,2194.6779 C1984,2299.1179 1627.93,2277.4479 1526,2300.2079 C1522.21,2301.0579 1518.33,2301.9279 1514.4,2302.8179 " fill="none" id="addressReserved-to-no_lease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1509.69,2303.8779,1519.3488,2305.8034,1514.5679,2302.7797,1517.5916,2297.9988,1509.69,2303.8779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="216" x="1985" y="1262.6749">reservation owned by another client</text>
+ </g>
+ <!--link hasAddressReservation to update_hint-->
+ <g id="link_hasAddressReservation_update_hint">
+ <path d="M1191.8,184.6679 C1229.64,184.6679 1279.11,184.6679 1323.15,184.6679 " fill="none" id="hasAddressReservation-to-update_hint" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1328.09,184.6679,1319.09,180.6679,1323.09,184.6679,1319.09,188.6679,1328.09,184.6679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="102" x="1209.75" y="177.5649">has a reservation</text>
+ </g>
+ <!--link update_hint to check_hint-->
+ <g id="link_update_hint_check_hint">
+ <path d="M1432.09,204.5379 C1434.6,219.2979 1440.09,239.5379 1452,253.2079 C1456.3938,258.2467 1461.3717,262.8532 1466.7236,267.0577 C1469.3996,269.1599 1472.169,271.1616 1475.0056,273.0665 C1476.4239,274.019 1477.859,274.9473 1479.3077,275.8518 C1480.032,276.3041 1480.7597,276.7505 1481.4903,277.1909 " fill="none" id="update_hint-to-check_hint" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1481.4903,277.1909,1475.8477,269.1187,1477.2082,274.6095,1471.7174,275.97,1481.4903,277.1909" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="151" x="1453" y="249.1049">request reserved address</text>
+ </g>
+ <!--link hasAddressReservation to check_done-->
+ <g id="link_hasAddressReservation_check_done">
+ <path d="M1068.19,186.7879 C928,191.0079 598,211.0779 598,306.5679 C598,306.5679 598,306.5679 598,627.5279 C598,657.9279 596.79,692.0479 595.56,718.8617 C595.4063,722.2134 595.2522,725.4509 595.1001,728.5528 C595.0241,730.1037 594.9486,731.6207 594.8739,733.1011 C594.8365,733.8413 594.7993,734.5724 594.7624,735.2939 C594.7439,735.6547 594.7255,736.0131 594.7072,736.3691 C594.698,736.5471 594.6889,736.7245 594.6797,736.9013 " fill="none" id="hasAddressReservation-to-check_done" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="594.6797,736.9013,599.1391,728.1198,594.9379,731.9079,591.1497,727.7067,594.6797,736.9013" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="87" x="599" y="454.8249">no reservation</text>
+ </g>
+ <!--link check_hint to get_existing-->
+ <g id="link_check_hint_get_existing">
+ <path d="M1562,308.3479 C1562,311.2479 1562,335.5354 1562,359.4554 C1562,365.4354 1562,371.3924 1562,376.9866 C1562,377.6859 1562,378.3795 1562,379.0667 " fill="none" id="check_hint-to-get_existing" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1562,379.0667,1566,370.0667,1562,374.0667,1558,370.0667,1562,379.0667" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link get_existing to existing-->
+ <g id="link_get_existing_existing">
+ <path d="M1562.34,410.1979 C1566.97,414.0679 1618.62,457.1179 1651.62,484.6279 " fill="none" id="get_existing-to-existing" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1655.18,487.5979,1650.8235,478.765,1651.3377,484.3984,1645.7043,484.9126,1655.18,487.5979" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="118" x="1621" y="454.8249">has requested lease</text>
+ </g>
+ <!--link existing to no_lease-->
+ <g id="link_existing_no_lease">
+ <path d="M1679,528.3579 C1679,551.3279 1679,591.2679 1679,625.5279 C1679,625.5279 1679,625.5279 1679,2194.6779 C1679,2232.9179 1580.23,2273.8379 1511.02,2297.8179 " fill="none" id="existing-to-no_lease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1506.69,2299.3079,1516.502,2300.1591,1511.4175,2297.6797,1513.8969,2292.5952,1506.69,2299.3079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="250" x="1680" y="1439.7749">not expired lease owned by another client</text>
+ </g>
+ <!--link get_existing to out_of_pool-->
+ <g id="link_get_existing_out_of_pool">
+ <path d="M1553.7013,410.1388 C1553.4182,410.1466 1553.1282,410.1547 1552.8314,410.163 C1548.0828,410.296 1541.5925,410.4854 1533.6022,410.7367 C1517.6218,411.2395 1495.6421,411.9901 1469.5986,413.0329 C1417.5116,415.1185 1349.1694,418.3729 1280.0562,423.1504 C1141.83,432.7054 1000.52,448.3529 980,472.9279 C952.92,505.3579 959.61,530.9779 980,567.9879 C988.27,583.0079 1002.3,594.6479 1016.9,603.4079 " fill="none" id="get_existing-to-out_of_pool" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1020.98,605.7579,1015.187,597.7929,1016.6503,603.2572,1011.1859,604.7205,1020.98,605.7579" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="87" x="981" y="513.8549">no reservation</text>
+ </g>
+ <!--link get_existing to out_of_pool-->
+ <g id="link_get_existing_out_of_pool">
+ <path d="M1553.7208,411.9855 C1553.6146,412.0133 1553.5076,412.0414 1553.3996,412.0698 C1553.1837,412.1266 1552.9642,412.1845 1552.7411,412.2435 C1551.8486,412.4797 1550.8989,412.7341 1549.8951,413.0069 C1547.8877,413.5524 1545.6642,414.1711 1543.2506,414.8639 C1533.5963,417.6348 1520.9,421.5892 1506.82,426.7704 C1478.66,437.1329 1444.965,452.4029 1419,472.9279 C1377.42,505.7879 1391.79,539.6579 1347,567.9879 C1296.02,600.2479 1230.15,614.7779 1175.55,621.1779 " fill="none" id="get_existing-to-out_of_pool-1" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1170.79,621.7079,1180.1795,624.6807,1175.7589,621.1511,1179.2885,616.7305,1170.79,621.7079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="156" x="1420" y="513.8549">has requested reservation</text>
+ </g>
+ <!--link get_existing to getReservedLease-->
+ <g id="link_get_existing_getReservedLease">
+ <path d="M1553.4552,409.9078 C1552.9981,409.9078 1552.5239,409.908 1552.0329,409.9083 C1551.0509,409.9088 1550.0018,409.9097 1548.8879,409.9113 C1546.6601,409.9143 1544.1733,409.9196 1541.4469,409.9281 C1530.5414,409.9623 1515.8032,410.0482 1498.477,410.249 C1463.8247,410.6507 1418.8206,411.5123 1373.4238,413.3392 C1282.63,416.9929 1190.265,424.5079 1176,439.9279 C1164.54,452.3079 1172.98,469.5379 1184.06,483.4879 " fill="none" id="get_existing-to-getReservedLease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1187.26,487.3079,1184.5539,477.8381,1184.0521,483.4727,1178.4175,482.9708,1187.26,487.3079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="213" x="1177" y="454.8249">has reservation for another address</text>
+ </g>
+ <!--link getReservedLease to no_lease-->
+ <g id="link_getReservedLease_no_lease">
+ <path d="M1250.44,528.4779 C1323.43,561.2179 1465,624.7979 1465,625.5279 C1465,625.5279 1465,625.5279 1465,1376.3379 C1465,1412.8479 1470.29,1424.5679 1492,1453.9079 C1505.43,1472.0579 1519.93,1465.4979 1533,1483.9079 C1552.45,1511.3279 1555,1522.6579 1555,1556.2679 C1555,1556.2679 1555,1556.2679 1555,2194.6779 C1555,2238.8979 1515.39,2274.7179 1483.71,2296.3079 " fill="none" id="getReservedLease-to-no_lease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1479.56,2299.0679,1489.2673,2297.4038,1483.7203,2296.2944,1484.8297,2290.7474,1479.56,2299.0679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="142" x="1493" y="1439.7749">no active reserved lease</text>
+ </g>
+ <!--link getReservedLease to out_of_pool-->
+ <g id="link_getReservedLease_out_of_pool">
+ <path d="M1185.68,528.3679 C1161.8,548.5979 1123.9,580.7079 1098.26,602.4279 " fill="none" id="getReservedLease-to-out_of_pool" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1094.46,605.6479,1103.9138,602.8864,1098.2764,602.4176,1098.7452,596.7802,1094.46,605.6479" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link out_of_pool to check_done-->
+ <g id="link_out_of_pool_check_done">
+ <path d="M1042.32,646.4379 C1011.08,666.1379 959,696.4079 910,713.0579 C843.42,735.6829 765.5875,748.9629 703.7075,756.6167 C672.7675,760.4435 645.8156,762.8639 626.2525,764.3424 C616.4709,765.0817 608.5366,765.5856 602.8745,765.9122 C602.5206,765.9326 602.1756,765.9523 601.8395,765.9713 C601.6715,765.9808 601.5058,765.9902 601.3422,765.9994 " fill="none" id="out_of_pool-to-check_done" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="601.3422,765.9994,610.5523,769.4885,606.3344,765.719,610.1038,761.5011,601.3422,765.9994" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="269" x="957" y="708.9549">owned reservation for the requested address</text>
+ </g>
+ <!--link out_of_pool to check_done-->
+ <g id="link_out_of_pool_check_done">
+ <path d="M972.77,636.7379 C870.54,647.5379 718.2,667.4179 666,694.0579 C646.29,704.1179 628.5475,721.4104 615.49,736.5679 C612.2256,740.3573 609.2541,744.0132 606.6178,747.3894 C605.2996,749.0774 604.0653,750.6955 602.9201,752.2254 C602.3475,752.9903 601.7972,753.7332 601.2698,754.4517 C601.2039,754.5416 601.1383,754.631 601.0731,754.72 " fill="none" id="out_of_pool-to-check_done-1" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="601.0731,754.72,609.6174,749.8216,604.027,750.6859,603.1627,745.0954,601.0731,754.72" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="234" x="667" y="708.9549">requested address is in allowed an pool</text>
+ </g>
+ <!--link out_of_pool to no_lease-->
+ <g id="link_out_of_pool_no_lease">
+ <path d="M1154.92,646.5479 C1183.44,656.5979 1213.5,671.6779 1235,694.0579 C1258.67,718.7079 1261,732.2479 1261,766.4179 C1261,766.4179 1261,766.4179 1258,1494.4079 C1258,1740.6479 1335,1796.8379 1335,2043.0779 C1335,2043.0779 1335,2043.0779 1335,2194.6779 C1335,2238.8979 1374.61,2274.7179 1406.29,2296.3079 " fill="none" id="out_of_pool-to-no_lease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1410.44,2299.0679,1405.1703,2290.7474,1406.2797,2296.2944,1400.7327,2297.4038,1410.44,2299.0679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="269" x="1262" y="1498.8049">address not reserved and not in allowed pool</text>
+ </g>
+ <!--link check_done to client_lease-->
+ <g id="link_check_done_client_lease">
+ <path d="M592.8,768.1979 C590.1,772.0079 560.44,813.9479 540.95,841.4879 " fill="none" id="check_done-to-client_lease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="538.08,845.5579,546.5477,840.5282,540.9713,841.4786,540.0209,835.9022,538.08,845.5579" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="99" x="572" y="812.8149">has a client lease</text>
+ </g>
+ <!--link client_lease to renew-->
+ <g id="link_client_lease_renew">
+ <path d="M577.04,866.4479 C659.51,866.4479 819.44,866.4479 920.42,866.4479 " fill="none" id="client_lease-to-renew" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="925.18,866.4479,916.18,862.4479,920.18,866.4479,916.18,870.4479,925.18,866.4479" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="314" x="595" y="859.3449">requested address was already assigned to the client</text>
+ </g>
+ <!--link client_lease to new_lease-->
+ <g id="link_client_lease_new_lease">
+ <path d="M523.84,886.3379 C523.705,903.0729 523.5,928.0454 523.3263,949.0929 C523.3045,951.7239 523.2833,954.2935 523.2628,956.778 C523.2576,957.3991 523.2525,958.0149 523.2475,958.6251 " fill="none" id="client_lease-to-new_lease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="523.2475,958.6251,527.3218,949.6585,523.2888,953.6252,519.3221,949.5923,523.2475,958.6251" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link new_lease to allocateOrReuseLease4-->
+ <g id="link_new_lease_allocateOrReuseLease4">
+ <path d="M522.5,989.9379 C508.82,992.3279 217,1044.3579 217,1116.2079 C217,1116.2079 217,1116.2079 217,1435.3779 C217,1462.0279 220.6325,1491.7304 224.3225,1515.0242 C224.7838,1517.9359 225.2459,1520.7475 225.702,1523.4404 C225.816,1524.1137 225.9296,1524.7795 226.0428,1525.4376 C226.0993,1525.7667 226.1558,1526.0938 226.2121,1526.419 C226.2403,1526.5816 226.2684,1526.7437 226.2965,1526.9053 " fill="none" id="new_lease-to-allocateOrReuseLease4" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="226.2965,1526.9053,228.6962,1517.3533,225.4403,1521.9791,220.8144,1518.7232,226.2965,1526.9053" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="145" x="218" y="1262.6749">has a requested address</text>
+ </g>
+ <!--link new_lease to allocateUnreservedLease4-->
+ <g id="link_new_lease_allocateUnreservedLease4">
+ <path d="M523,989.9379 C523,991.8229 523,1022.4404 523,1053.0504 C523,1060.7029 523,1068.3549 523,1075.5574 C523,1079.1587 523,1082.6476 523,1085.9679 C523,1086.1754 523,1086.3823 523,1086.5885 " fill="none" id="new_lease-to-allocateUnreservedLease4" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="523,1086.5885,527,1077.5885,523,1081.5885,519,1077.5885,523,1086.5885" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="130" x="524" y="1034.7449">no requested address</text>
+ </g>
+ <!--link allocateOrReuseLease4 to candidate-->
+ <g id="link_allocateOrReuseLease4_candidate">
+ <path d="M232.01,1558.3379 C232.23,1560.3179 237.01,1602.9479 240.19,1631.3279 " fill="none" id="allocateOrReuseLease4-to-candidate" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="240.72,1635.9879,243.6928,1626.5984,240.1631,1631.019,235.7425,1627.4894,240.72,1635.9879" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link candidate to reuseExpiredLease4-->
+ <g id="link_candidate_reuseExpiredLease4">
+ <path d="M234.39,1677.2579 C223.58,1701.0179 205.02,1741.8079 193.14,1767.9179 " fill="none" id="candidate-to-reuseExpiredLease4" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="191.19,1772.1979,198.5557,1765.6598,193.259,1767.6461,191.2728,1762.3494,191.19,1772.1979" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="140" x="214" y="1739.7649">expired candidate lease</text>
+ </g>
+ <!--link candidate to no_lease-->
+ <g id="link_candidate_no_lease">
+ <path d="M281.19,1677.3079 C311.49,1691.2679 355.35,1709.1179 396,1716.8679 C414.83,1720.4679 1069.57,1715.0079 1086,1724.8679 C1115.34,1742.4879 1123,1758.1779 1123,1792.4079 C1123,1792.4079 1123,1792.4079 1123,2194.6779 C1123,2248.3879 1280.98,2287.7279 1375.63,2306.4779 " fill="none" id="candidate-to-no_lease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1380.28,2307.3879,1372.2107,2301.7411,1375.3722,2306.432,1370.6813,2309.5936,1380.28,2307.3879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="157" x="1124" y="1998.9349">conflicting candidate lease</text>
+ </g>
+ <!--link candidate to create-->
+ <g id="link_candidate_create">
+ <path d="M283.96,1677.3279 C314.26,1690.5579 356.88,1707.4379 396,1716.8679 C428.16,1724.6279 437.15,1720.9879 470,1724.8679 C628.07,1743.5579 812.53,1767.8079 915.65,1781.5779 " fill="none" id="candidate-to-create" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="920.51,1782.2279,912.1137,1777.0799,915.5534,1781.5708,911.0624,1785.0105,920.51,1782.2279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="111" x="616" y="1739.7649">no candidate lease</text>
+ </g>
+ <!--link reuseExpiredLease4 to reclaimExpiredLease-->
+ <g id="link_reuseExpiredLease4_reclaimExpiredLease">
+ <path d="M181.81,1813.3179 C181.65,1828.6479 181.43,1850.3879 181.26,1867.2079 " fill="none" id="reuseExpiredLease4-to-reclaimExpiredLease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="181.21,1872.1079,185.3126,1863.1542,181.2671,1867.1082,177.3131,1863.0628,181.21,1872.1079" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link reclaimExpiredLease to updateLease4Information-->
+ <g id="link_reclaimExpiredLease_updateLease4Information">
+ <path d="M180.04,1913.3779 C179.25,1928.7179 178.15,1950.4579 177.29,1967.2779 " fill="none" id="reclaimExpiredLease-to-updateLease4Information" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="177.04,1972.1779,181.4983,1963.3959,177.2975,1967.1846,173.5089,1962.9838,177.04,1972.1779" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link updateLease4Information to lease4_select-->
+ <g id="link_updateLease4Information_lease4_select">
+ <path d="M172.53,2013.4479 C169.69,2028.9179 165.65,2050.9079 162.56,2067.7979 " fill="none" id="updateLease4Information-to-lease4_select" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="161.73,2072.2679,167.2817,2064.1329,162.6282,2067.3493,159.4118,2062.6958,161.73,2072.2679" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link lease4_select to no_lease-->
+ <g id="link_lease4_select_no_lease">
+ <path d="M237.86,2110.8479 C399.58,2143.4479 777.6,2218.1779 1097,2270.2079 C1193.29,2285.8979 1305.08,2300.9279 1375.43,2309.9979 " fill="none" id="lease4_select-to-no_lease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1380.18,2310.6079,1371.7692,2305.4837,1375.2215,2309.9649,1370.7403,2313.4172,1380.18,2310.6079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="27" x="1041" y="2199.0749">SKIP</text>
+ </g>
+ <!--link lease4_select to updateLease4-->
+ <g id="link_lease4_select_updateLease4">
+ <path d="M168.99,2113.1879 C175.01,2122.7879 182.89,2134.4979 191,2144.1479 C198.43,2152.9879 207.33,2161.9179 215.59,2169.6579 " fill="none" id="lease4_select-to-updateLease4" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="218.99,2172.8079,215.1045,2163.7579,215.3215,2169.4106,209.6688,2169.6276,218.99,2172.8079" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link updateLease4 to old_lease-->
+ <g id="link_updateLease4_old_lease">
+ <path d="M252.95,2213.5979 C263.06,2229.1579 279.56,2250.3579 300,2261.2079 C367.18,2296.8879 583.75,2311.1079 696.81,2316.2079 " fill="none" id="updateLease4-to-old_lease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="701.6,2316.4179,692.7795,2312.0362,696.6046,2316.2038,692.437,2320.0289,701.6,2316.4179" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link allocateUnreservedLease4 to iterate-->
+ <g id="link_allocateUnreservedLease4_iterate">
+ <path d="M523,1117.9379 C523,1120.7879 523,1149.8079 523,1171.9279 " fill="none" id="allocateUnreservedLease4-to-iterate" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="523,1176.7279,527,1167.7279,523,1171.7279,519,1167.7279,523,1176.7279" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link iterate to pick-->
+ <g id="link_iterate_pick">
+ <path d="M525.44,1218.1479 C527.97,1237.7079 531.93,1268.3679 534.73,1290.0079 " fill="none" id="iterate-to-pick" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="535.36,1294.9079,538.1693,1285.4682,534.717,1289.9494,530.2358,1286.4971,535.36,1294.9079" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link pick to pick_reserved-->
+ <g id="link_pick_pick_reserved">
+ <path d="M590.31,1329.7379 C635.21,1340.9179 693.8,1356.8979 701,1365.8379 C710.4,1377.5179 711.67,1394.3079 710.59,1408.2279 " fill="none" id="pick-to-pick_reserved" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="710.1,1412.8879,715.0049,1404.3473,710.6145,1407.9145,707.0473,1403.5241,710.1,1412.8879" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--reverse link iterate to pick_reserved-->
+ <g id="link_iterate_pick_reserved">
+ <path d="M563.34,1221.4079 C608.29,1248.6779 678.52,1299.6279 708,1365.8379 C714.82,1381.1679 713.4,1400.4479 710.81,1414.4779 " fill="none" id="iterate-backto-pick_reserved" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="559.28,1218.9779,564.9395,1227.0384,563.5675,1221.5504,569.0554,1220.1784,559.28,1218.9779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="204" x="691" y="1321.7049">address reserved to another client</text>
+ </g>
+ <!--link pick_reserved to mt_in_use-->
+ <g id="link_pick_reserved_mt_in_use">
+ <path d="M693.34,1454.2679 C679.26,1475.2879 656.46,1509.3179 641.11,1532.2279 " fill="none" id="pick_reserved-to-mt_in_use" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="638.41,1536.2579,646.743,1531.008,641.1934,1532.1043,640.0972,1526.5546,638.41,1536.2579" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--reverse link iterate to mt_in_use-->
+ <g id="link_iterate_mt_in_use">
+ <path d="M499.34,1222.9279 C465.42,1259.8279 410.87,1332.2379 447,1384.8379 C468.03,1415.4579 495.69,1392.0379 525,1414.8379 C568.91,1449.0079 601.58,1507.6479 616.32,1537.5479 " fill="none" id="iterate-backto-mt_in_use" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="502.74,1219.2879,493.6669,1223.1192,499.3208,1222.936,499.5039,1228.5899,502.74,1219.2879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="244" x="448" y="1380.7349">address already in use by another thread</text>
+ </g>
+ <!--link mt_in_use to pick_lease-->
+ <g id="link_mt_in_use_pick_lease">
+ <path d="M601.09,1577.1779 C580.76,1593.2579 551.52,1616.3879 529.88,1633.4979 " fill="none" id="mt_in_use-to-pick_lease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="526.02,1636.5479,535.5588,1634.096,529.9396,1633.4437,530.592,1627.8246,526.02,1636.5479" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--reverse link iterate to pick_lease-->
+ <g id="link_iterate_pick_lease">
+ <path d="M486.95,1221.7579 C476.56,1229.2579 465.69,1238.1679 457,1247.7779 C440.02,1266.5379 434.68,1272.3679 428,1296.7779 C411.43,1357.3179 446.44,1516.8379 465,1576.8079 C471.68,1598.3779 482.78,1621.7779 490.89,1637.5779 " fill="none" id="iterate-backto-pick_lease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="490.76,1219.0579,481.1011,1220.9828,486.676,1221.9425,485.7163,1227.5173,490.76,1219.0579" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="68" x="437" y="1439.7749">active lease</text>
+ </g>
+ <!--link pick_lease to reuseExpiredLease4-->
+ <g id="link_pick_lease_reuseExpiredLease4">
+ <path d="M460.73,1677.3379 C436.84,1689.4379 406.54,1706.3079 382,1724.8679 C372.48,1732.0779 373.34,1737.8979 363,1743.8679 C354.02,1749.0579 309.1,1761.1179 266.26,1771.9079 " fill="none" id="pick_lease-to-reuseExpiredLease4" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="261.43,1773.1279,271.1314,1774.8257,266.2807,1771.9152,269.1911,1767.0645,261.43,1773.1279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="78" x="383" y="1739.7649">expired lease</text>
+ </g>
+ <!--link pick_lease to create-->
+ <g id="link_pick_lease_create">
+ <path d="M550.73,1671.8079 C637.51,1695.3079 817.1,1743.9479 919.71,1771.7479 " fill="none" id="pick_lease-to-create" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="924.29,1772.9879,916.6394,1766.7857,919.4619,1771.688,914.5596,1774.5106,924.29,1772.9879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49" x="808" y="1739.7649">no lease</text>
+ </g>
+ <!--link create to old_lease-->
+ <g id="link_create_old_lease">
+ <path d="M975.35,1813.2879 C957.65,1831.6179 935,1861.1979 935,1892.4779 C935,1892.4779 935,1892.4779 935,2194.6779 C935,2246.3179 883.01,2278.8979 836.88,2297.6379 " fill="none" id="create-to-old_lease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="832.4,2299.4179,842.2408,2299.8154,837.0473,2297.5733,839.2894,2292.3798,832.4,2299.4179" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link old_lease to return-->
+ <g id="link_old_lease_return">
+ <path d="M767,2339.6479 C767,2359.2079 767,2389.8779 767,2411.5079 " fill="none" id="old_lease-to-return" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="767,2416.3979,771,2407.3979,767,2411.3979,763,2407.3979,767,2416.3979" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--SRC=[ZLPBRzGm4BxdLrYSuB0890w80n8g1IgGA8MU8vDuTorIUqaSIeNuxyoC4tlyICkbtOpdcnb_y_0uxGObtWo3xQHJGef1oKc9lHt5smzdbrVdNwxFltuJZpygAPu_VVdqsPEcaGTbd7ZqJfjUT8F63q8z4d8IUv2UaV0JoXfZmUoe3qSdNWcGZdVYPBLnmSpPKNK_nAX-pcfogeTVanf_IQUj8OEoxq4qNP5OzG7tNhb2zoYdDroTJWBY-jJZ7QEhnJH1PpfiZnJQg3edpG6OASBZc0xF22B-D8A1CBP8P1ryKAvLl_Naj3aWN6pm4QbTjHlnjsbITUxCyvJxMldpMSdWTdOxkz-Th1qI7CXRkszHtlojq59atpFIMwFmIjcDZBhrIeHg2PMPPOK8simmV7a0sdKMBdHK8z5MGnCFCoX70t8v-pnUuGdHy88C25PGdJIzfYfXTrfb3LCc8v2HanwJ797RSo_sFb9KDqXzKyNH2KToeA_E9StQGA9lOjNxG-_j8fvn_1bUVruozQATr02i4JYH829fsAErFxYTwrD63lSDUbYOrOpC9XHccPFhS5X9ouLJ8rh08WG8p9nf_cwKynMg_IcFyL93jV4gFe4W8MEfnxHHL1-Ksp4CcnmbXr79_Wvo8B00ljz1H9PgqHtnX6pSk5QRTavJo1erEl8zWBmbFGsMAZtGaJYglOMSOR4Fd5FuYfqLoixDeu7CyeQoYrewe7zhbytd1KpjcvKiIFuNpc4esCPb3uZOxLxdRm0M9bdAvgfuZBFuITL4zMa0Gq3AfftSBTfXCv_0dJNxGNSqnTbpLCMkd0EIgGfxQq21bP8Va4qzPB33tgg52fduvkT5pCjjY_hVSXVmDDKNmg8lPXjL-QlM94ySdeQFF86-59bkoXRmaixCJSuAb-m9AuD_G3kDyLvW_i86T6GIN-SrsfNp0bRUN4WehbzAfFEpXap2R_XLYYHw-lBe8BIo6UutLFQEdd0uucZ7SI6Dg4R3hhW2CScnA67Eeu2P8ZlCESN2LedG2dmhQH8DGD3KgWP-Io-I7-zoclG1ANAMM5cgApM6rrZNaYOkA3Imgjj6xDMSupgwU1VhuwgfUoD-mkuIDviTiP2lAJI5VOTbIr06PPdZi-z-a7-wbecPQLPYyI6M6r55HhfR6z26d33fufDy5LtuzUF5PQ6Ikary-MOCB_5cFb5jtM2M_oIZZGN_XA-uaYHVOZUVeWtMJ4412ZWRaPa1WCTz9VpQDFpG0iHDhB5x5kBFqhWVSQNUGwLQHV1YMuhHIzAiX5-B6rfB_W40]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/request4-lease.uml b/doc/sphinx/uml/request4-lease.uml
new file mode 100644
index 0000000..54f0c81
--- /dev/null
+++ b/doc/sphinx/uml/request4-lease.uml
@@ -0,0 +1,118 @@
+@startuml
+
+title Allocate a lease for DHCPREQUEST (Kea 1.8.0)
+
+agent "Find client lease" as findClientLease
+note right : entry point
+
+agent "Check requested reservation" as addressReserved
+
+agent "Get reservation" as hasAddressReservation
+
+agent "Update requested address" as update_hint
+
+rectangle "Check requested address" as check_hint {
+ rectangle "Get lease for requested address" as get_existing {
+ agent "Check requested lease" as existing
+ }
+
+ agent "Check lease for reserved address" as getReservedLease
+
+ agent "Check out-of-pool address" as out_of_pool
+}
+
+rectangle "Check client lease" as check_done {
+ agent "Check renew" as client_lease
+}
+
+rectangle "Allocate a new lease" as new_lease {
+ together {
+ rectangle "Allocate or reuse lease" as allocateOrReuseLease4 {
+ agent "Get candidate lease" as candidate
+
+ agent "Reuse expired lease" as reuseExpiredLease4
+
+ agent "Reclaim expired lease" as reclaimExpiredLease
+
+ agent "update lease information" as updateLease4Information
+
+ agent "Callout lease4_select" as lease4_select
+ note right : hook
+
+ agent "Update lease" as updateLease4
+ }
+
+ rectangle "Allocate unreserved lease" as allocateUnreservedLease4 {
+ agent "Iterate pools and subnets" as iterate
+
+ agent "Pick address" as pick
+
+ agent "Check reserved addressed" as pick_reserved
+
+ agent "Check already in use by another thread" as mt_in_use
+
+ agent "Check lease" as pick_lease
+ }
+ }
+
+ agent "Create a new lease" as create
+
+ agent "Delete old lease" as old_lease
+}
+
+agent "Return no lease" as no_lease
+note right : exit point
+
+agent "Return renewed lease" as renew
+note right : exit point
+
+agent "Return lease" as return
+note right : exit point
+
+findClientLease --> addressReserved : has requested address (hint)
+findClientLease --> hasAddressReservation : no requested address (hint)
+addressReserved --> check_hint : no conflicting reservation
+addressReserved --> no_lease : reservation owned by another client
+hasAddressReservation -> update_hint : has a reservation
+update_hint --> check_hint : request reserved address
+hasAddressReservation --> check_done : no reservation
+check_hint --> get_existing
+get_existing --> existing : has requested lease
+existing ---> no_lease : not expired lease owned by another client
+get_existing --> out_of_pool : no reservation
+get_existing --> out_of_pool : has requested reservation
+get_existing --> getReservedLease : has reservation for another address
+getReservedLease ---> no_lease : no active reserved lease
+getReservedLease --> out_of_pool
+out_of_pool --> check_done : owned reservation for the requested address
+out_of_pool --> check_done : requested address is in allowed an pool
+out_of_pool --> no_lease : address not reserved and not in allowed pool
+check_done --> client_lease : has a client lease
+client_lease -> renew : requested address was already assigned to the client
+client_lease --> new_lease
+new_lease --> allocateOrReuseLease4 : has a requested address
+new_lease --> allocateUnreservedLease4 : no requested address
+allocateOrReuseLease4 --> candidate
+candidate --> reuseExpiredLease4 : expired candidate lease
+candidate --> no_lease : conflicting candidate lease
+candidate --> create : no candidate lease
+reuseExpiredLease4 --> reclaimExpiredLease
+reclaimExpiredLease --> updateLease4Information
+updateLease4Information --> lease4_select
+lease4_select ---> no_lease : SKIP
+lease4_select --> updateLease4
+updateLease4 --> old_lease
+allocateUnreservedLease4 --> iterate
+iterate --> pick
+pick --> pick_reserved
+pick_reserved -up-> iterate : address reserved to another client
+pick_reserved --> mt_in_use
+mt_in_use -up-> iterate : address already in use by another thread
+mt_in_use --> pick_lease
+pick_lease -up-> iterate : active lease
+pick_lease --> reuseExpiredLease4 : expired lease
+pick_lease --> create : no lease
+create --> old_lease
+old_lease --> return
+
+@enduml
diff --git a/doc/sphinx/uml/request4.png b/doc/sphinx/uml/request4.png
new file mode 100644
index 0000000..6818c2c
--- /dev/null
+++ b/doc/sphinx/uml/request4.png
Binary files differ
diff --git a/doc/sphinx/uml/request4.svg b/doc/sphinx/uml/request4.svg
new file mode 100644
index 0000000..78f0aaa
--- /dev/null
+++ b/doc/sphinx/uml/request4.svg
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="1851px" preserveAspectRatio="none" style="width:713px;height:1851px;background:#FFFFFF;" version="1.1" viewBox="0 0 713 1851" width="713px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="256" x="221.5" y="24.9659">DHCPREQUEST processing (Kea 1.8.0)</text>
+ <!--cluster ack-->
+ <g id="cluster_ack">
+ <rect fill="none" height="791.34" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="298" x="409" y="759.5379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="145" x="485.5" y="776.5039">A lease was assigned</text>
+ </g>
+ <!--entity setReservedClasses-->
+ <g id="elem_setReservedClasses">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="162" x="439" y="851.3979"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="142" x="449" y="876.3639">Add reserved classes</text>
+ </g>
+ <!--entity requiredClassify-->
+ <g id="elem_requiredClassify">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="186" x="437" y="951.4679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="166" x="447" y="976.4339">Classify required classes</text>
+ </g>
+ <!--entity buildCfgOptionList-->
+ <g id="elem_buildCfgOptionList">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="204" x="444" y="1051.5379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="184" x="454" y="1076.5039">Build configured option list</text>
+ </g>
+ <!--entity appendRequestedOptions-->
+ <g id="elem_appendRequestedOptions">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="200" x="453" y="1169.5979"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="180" x="463" y="1194.5639">Append requested options</text>
+ </g>
+ <!--entity appendRequestedVendorOptions-->
+ <g id="elem_appendRequestedVendorOptions">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="251" x="432.5" y="1287.6679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="231" x="442.5" y="1312.6339">Append requested vendor options</text>
+ </g>
+ <!--entity appendBasicOptions-->
+ <g id="elem_appendBasicOptions">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="166" x="474" y="1387.7379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="146" x="484" y="1412.7039">Append basic options</text>
+ </g>
+ <!--entity setFixedFields-->
+ <g id="elem_setFixedFields">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="119" x="494.5" y="1487.8079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="99" x="504.5" y="1512.7739">Set fixed fields</text>
+ </g>
+ <!--entity entry-->
+ <g id="elem_entry">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="94" x="76" y="47.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="74" x="86" y="72.0339">Entry point</text>
+ </g>
+ <!--entity selectSubnet-->
+ <g id="elem_selectSubnet">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="111" x="67.5" y="147.1279"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="91" x="77.5" y="172.0939">Select subnet</text>
+ </g>
+ <g id="elem_GMN4">
+ <path d="M213.5,152.8079 L213.5,162.6679 L178.75,166.6679 L213.5,170.6679 L213.5,180.5139 A0,0 0 0 0 213.5,180.5139 L300.5,180.5139 A0,0 0 0 0 300.5,180.5139 L300.5,162.8079 L290.5,152.8079 L213.5,152.8079 A0,0 0 0 0 213.5,152.8079 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M290.5,152.8079 L290.5,162.8079 L300.5,162.8079 L290.5,152.8079 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="66" x="219.5" y="171.7049">hook point</text>
+ </g>
+ <!--entity findReservation-->
+ <g id="elem_findReservation">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="162" x="186" y="247.1979"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="142" x="196" y="272.1639">Find host reservation</text>
+ </g>
+ <!--entity known-->
+ <g id="elem_known">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="285" x="167.5" y="347.2679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="265" x="177.5" y="372.2339">Add either KNOWN or UNKNOWN class</text>
+ </g>
+ <!--entity classify2-->
+ <g id="elem_classify2">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="144" x="244" y="447.3379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="124" x="254" y="472.3039">Classify (2nd pass)</text>
+ </g>
+ <!--entity processClientName-->
+ <g id="elem_processClientName">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="154" x="242" y="547.3979"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="134" x="252" y="572.3639">Process client name</text>
+ </g>
+ <!--entity assignLease-->
+ <g id="elem_assignLease">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="116" x="263" y="647.4679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="96" x="273" y="672.4339">Assign a lease</text>
+ </g>
+ <!--entity common-->
+ <g id="elem_common">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="161" x="470.5" y="1605.8779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="141" x="480.5" y="1630.8439">Adjust interface data</text>
+ </g>
+ <!--entity appendServerID-->
+ <g id="elem_appendServerID">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="138" x="482" y="1705.9379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="118" x="492" y="1730.9039">Append server ID</text>
+ </g>
+ <g id="elem_GMN22">
+ <path d="M301.5,1711.6279 L301.5,1739.3339 A0,0 0 0 0 301.5,1739.3339 L446.5,1739.3339 A0,0 0 0 0 446.5,1739.3339 L446.5,1729.6279 L481.9,1725.4779 L446.5,1721.6279 L446.5,1721.6279 L436.5,1711.6279 L301.5,1711.6279 A0,0 0 0 0 301.5,1711.6279 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M436.5,1711.6279 L436.5,1721.6279 L446.5,1721.6279 L436.5,1711.6279 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="124" x="307.5" y="1730.5249">on success exit point</text>
+ </g>
+ <!--entity drop-->
+ <g id="elem_drop">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="153" x="171.5" y="1806.0079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="133" x="181.5" y="1830.9739">Return no response</text>
+ </g>
+ <g id="elem_GMN26">
+ <path d="M6,1811.6879 L6,1839.3939 A0,0 0 0 0 6,1839.3939 L136,1839.3939 A0,0 0 0 0 136,1839.3939 L136,1829.6879 L171.23,1825.5479 L136,1821.6879 L136,1821.6879 L126,1811.6879 L6,1811.6879 A0,0 0 0 0 6,1811.6879 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <path d="M126,1811.6879 L126,1821.6879 L136,1821.6879 L126,1811.6879 " fill="#FEFFDD" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="109" x="12" y="1830.5849">on error exit point</text>
+ </g>
+ <!--link entry to selectSubnet-->
+ <g id="link_entry_selectSubnet">
+ <path d="M123,86.5079 C123,101.8379 123,123.5779 123,140.3979 " fill="none" id="entry-to-selectSubnet" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="123,145.2979,127,136.2979,123,140.2979,119,136.2979,123,145.2979" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link selectSubnet to findReservation-->
+ <g id="link_selectSubnet_findReservation">
+ <path d="M150.76,186.5779 C174.57,202.7879 208.91,226.1679 234.09,243.3179 " fill="none" id="selectSubnet-to-findReservation" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="238.07,246.0279,232.8864,237.6535,233.9386,243.2117,228.3804,244.2638,238.07,246.0279" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link selectSubnet to drop-->
+ <g id="link_selectSubnet_drop">
+ <path d="M116.78,186.5979 C110.91,206.1779 103,237.7579 103,265.7379 C103,265.7379 103,265.7379 103,1726.4779 C103,1764.1179 136.95,1788.2979 171.99,1803.2279 " fill="none" id="selectSubnet-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="176.22,1804.9679,169.4398,1797.8245,171.6017,1803.0519,166.3742,1805.2138,176.22,1804.9679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="90" x="104" y="976.3949">hook set DROP</text>
+ </g>
+ <!--link findReservation to known-->
+ <g id="link_findReservation_known">
+ <path d="M275.29,286.6379 C282.13,302.2479 291.89,324.4879 299.31,341.4279 " fill="none" id="findReservation-to-known" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="301.13,345.5679,301.1615,335.7191,299.1139,340.9924,293.8406,338.9449,301.13,345.5679" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link known to classify2-->
+ <g id="link_known_classify2">
+ <path d="M311.16,386.7079 C312.09,402.0379 313.43,423.7879 314.45,440.6079 " fill="none" id="known-to-classify2" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="314.75,445.5079,318.1775,436.2747,314.4363,440.5178,310.1933,436.7766,314.75,445.5079" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link classify2 to processClientName-->
+ <g id="link_classify2_processClientName">
+ <path d="M316.58,486.7779 C317.05,502.1079 317.71,523.8579 318.23,540.6779 " fill="none" id="classify2-to-processClientName" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="318.38,545.5779,322.0953,536.4567,318.2229,540.5804,314.0993,536.708,318.38,545.5779" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link processClientName to assignLease-->
+ <g id="link_processClientName_assignLease">
+ <path d="M319.39,586.8479 C319.7,602.1779 320.14,623.9179 320.48,640.7379 " fill="none" id="processClientName-to-assignLease" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="320.58,645.6379,324.3992,636.5597,320.48,640.6389,316.4008,636.7197,320.58,645.6379" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link assignLease to ack-->
+ <g id="link_assignLease_ack">
+ <path d="M343.66,686.8979 C363.73,703.6329 393.68,728.6029 418.9225,749.6479 C422.0778,752.2785 425.1596,754.8478 428.1393,757.3321 C428.8842,757.9531 429.6228,758.5688 430.3545,759.1789 " fill="none" id="assignLease-to-ack" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="430.3545,759.1789,426.0032,750.3434,426.5141,755.9771,420.8804,756.488,430.3545,759.1789" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="59" x="398" y="731.4349">DHCPACK</text>
+ </g>
+ <!--link assignLease to common-->
+ <g id="link_assignLease_common">
+ <path d="M321.25,686.8479 C321.55,710.6379 322,752.7979 322,788.8979 C322,788.8979 322,788.8979 322,1508.3379 C322,1574.6179 399.27,1603.0579 464.04,1615.2579 " fill="none" id="assignLease-to-common" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="468.77,1616.1179,460.6378,1610.5621,463.8518,1615.2172,459.1967,1618.4312,468.77,1616.1179" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="61" x="323" y="1135.4949">DHCPNAK</text>
+ </g>
+ <!--link assignLease to drop-->
+ <g id="link_assignLease_drop">
+ <path d="M300.67,686.8379 C279.07,708.9779 248,747.9579 248,788.8979 C248,788.8979 248,788.8979 248,1726.4779 C248,1751.2579 248,1779.5579 248,1799.3679 " fill="none" id="assignLease-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="248,1804.2279,252,1795.2279,248,1799.2279,244,1795.2279,248,1804.2279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="49" x="249" y="1253.5649">on error</text>
+ </g>
+ <!--link ack to setReservedClasses-->
+ <g id="link_ack_setReservedClasses">
+ <path d="M466.15,790.6279 C468.13,793.5279 488.48,823.5079 503.58,845.7479 " fill="none" id="ack-to-setReservedClasses" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="506.25,849.6779,504.5015,839.9855,503.4404,845.542,497.884,844.4809,506.25,849.6779" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link setReservedClasses to requiredClassify-->
+ <g id="link_setReservedClasses_requiredClassify">
+ <path d="M521.93,890.8379 C523.49,906.1679 525.71,927.9179 527.42,944.7379 " fill="none" id="setReservedClasses-to-requiredClassify" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="527.92,949.6479,530.9727,940.2841,527.4055,944.6745,523.0151,941.1073,527.92,949.6479" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link requiredClassify to buildCfgOptionList-->
+ <g id="link_requiredClassify_buildCfgOptionList">
+ <path d="M533.08,990.9079 C535.59,1006.2379 539.13,1027.9879 541.88,1044.8079 " fill="none" id="requiredClassify-to-buildCfgOptionList" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="542.68,1049.7279,545.1863,1040.2033,541.8789,1044.7925,537.2897,1041.4851,542.68,1049.7279" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link buildCfgOptionList to appendRequestedOptions-->
+ <g id="link_buildCfgOptionList_appendRequestedOptions">
+ <path d="M547.14,1090.9779 C548.32,1110.5379 550.17,1141.1979 551.47,1162.8379 " fill="none" id="buildCfgOptionList-to-appendRequestedOptions" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="551.77,1167.7279,555.2222,1158.5039,551.4697,1162.7369,547.2367,1158.9844,551.77,1167.7279" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link appendRequestedOptions to appendRequestedVendorOptions-->
+ <g id="link_appendRequestedOptions_appendRequestedVendorOptions">
+ <path d="M553.81,1209.0479 C554.66,1228.6079 555.98,1259.2679 556.91,1280.8979 " fill="none" id="appendRequestedOptions-to-appendRequestedVendorOptions" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="557.12,1285.7979,560.7298,1276.6345,556.9053,1280.8025,552.7372,1276.978,557.12,1285.7979" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link appendRequestedVendorOptions to appendBasicOptions-->
+ <g id="link_appendRequestedVendorOptions_appendBasicOptions">
+ <path d="M557.81,1327.1079 C557.65,1342.4479 557.43,1364.1879 557.26,1381.0079 " fill="none" id="appendRequestedVendorOptions-to-appendBasicOptions" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="557.21,1385.9079,561.3126,1376.9542,557.2671,1380.9082,553.3131,1376.8628,557.21,1385.9079" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link appendBasicOptions to setFixedFields-->
+ <g id="link_appendBasicOptions_setFixedFields">
+ <path d="M556.42,1427.1779 C555.95,1442.5079 555.29,1464.2579 554.77,1481.0779 " fill="none" id="appendBasicOptions-to-setFixedFields" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="554.62,1485.9779,558.8754,1477.0958,554.7628,1480.98,550.8787,1476.8673,554.62,1485.9779" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link setFixedFields to common-->
+ <g id="link_setFixedFields_common">
+ <path d="M553.51,1527.2479 C553.01,1546.8079 552.21,1577.4679 551.65,1599.1079 " fill="none" id="setFixedFields-to-common" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="551.53,1603.9979,555.76,1595.1037,551.6585,1598.9996,547.7627,1594.8981,551.53,1603.9979" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link common to appendServerID-->
+ <g id="link_common_appendServerID">
+ <path d="M551,1645.3179 C551,1660.6479 551,1682.3879 551,1699.2179 " fill="none" id="common-to-appendServerID" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="551,1704.1079,555,1695.1079,551,1699.1079,547,1695.1079,551,1704.1079" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link appendServerID to drop-->
+ <!--SRC=[VLJBRjim4BphAnOv9GS7RKv53WKSsKO31xOhD-sXw86MLZ9hcLH9AWyK_VUIIucI9ISNGpixiwA6EpRSb0YpB_4ccNzzd6-_GQLaYbfpKS3b4XbylFvq_U4gYbY1mi35N1Zr2fNambm0qu2k3jqjbfWQqFLEeEzhGhO4H48Q1CMBlO5Rs4jvy8E2VC55Pd5jGA56zSGCbuB6vBQJT5XGJBCCa9izAbYkrZzM81KyhlnZMZAjINqGyhdJn0xd-IjStjZNLRQu8bRQu3U1kV5Us0vtjM1796RZKKpmogBTUUo4GW234fdsP4RGWwkZI5apc2YivnTJpu7dmC7C2z83_8sWzuNU3Spy4L4tpfgamMCF1qtuGeL_Qgu6mXPiMK5sL_FIKgN8UL4xbQoSsL1oxIzpvmXnNgm9Vx1mTyogGf7HRDGcQFqR6JMJjkVb-ctjaotjHRupuZintXYqOvgdPzHt3XzgjcZiThtOj-OSoonukt3OWg3eNs_VVjTsFUtIeifPYf0nm_psoEEnlvZ-A7G_2kvdlMDi2RkV-JYKcBiqM9jrdTAsuGitWs0aQ6ebG4Yt2fKzlb-jJCbgE0MLijRrPr14OJBvV1h6Va7TOSO61CjmxskJQos2MRBUH0CPZVA1er-gksY59yB7GHeXnEjdgFViUpOkj_GNDesNeww_beQmcWu9tKUrraLkdZThbAynHCnHe8O0iSw4PmpvBpeVb_FmESLfEjvhzjGdwHX3hIVzN9oMFR-ZnlPkUbZwqn8cF_Syor3yQcuY-Wy0]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/request4.uml b/doc/sphinx/uml/request4.uml
new file mode 100644
index 0000000..a5878b9
--- /dev/null
+++ b/doc/sphinx/uml/request4.uml
@@ -0,0 +1,59 @@
+@startuml
+
+title DHCPREQUEST processing (Kea 1.8.0)
+
+agent "Entry point" as entry
+
+agent "Select subnet" as selectSubnet
+note right : hook point
+
+agent "Find host reservation" as findReservation
+
+agent "Add either KNOWN or UNKNOWN class" as known
+
+agent "Classify (2nd pass)" as classify2
+
+agent "Process client name" as processClientName
+
+agent "Assign a lease" as assignLease
+
+rectangle "A lease was assigned" as ack {
+ agent "Add reserved classes" as setReservedClasses
+ agent "Classify required classes" as requiredClassify
+ agent "Build configured option list" as buildCfgOptionList
+ agent "Append requested options" as appendRequestedOptions
+ agent "Append requested vendor options" as appendRequestedVendorOptions
+ agent "Append basic options" as appendBasicOptions
+ agent "Set fixed fields" as setFixedFields
+}
+
+agent "Adjust interface data" as common
+
+agent "Append server ID" as appendServerID
+note left : on success exit point
+
+agent "Return no response" as drop
+note left : on error exit point
+
+entry --> selectSubnet
+selectSubnet --> findReservation
+selectSubnet ---> drop : hook set DROP
+findReservation --> known
+known --> classify2
+classify2 --> processClientName
+processClientName --> assignLease
+assignLease --> ack : DHCPACK
+assignLease --> common : DHCPNAK
+assignLease ---> drop : on error
+ack --> setReservedClasses
+setReservedClasses --> requiredClassify
+requiredClassify --> buildCfgOptionList
+buildCfgOptionList --> appendRequestedOptions
+appendRequestedOptions --> appendRequestedVendorOptions
+appendRequestedVendorOptions --> appendBasicOptions
+appendBasicOptions --> setFixedFields
+setFixedFields --> common
+common --> appendServerID
+appendServerID -[hidden]-> drop
+
+@enduml \ No newline at end of file
diff --git a/doc/sphinx/uml/requestLease4.png b/doc/sphinx/uml/requestLease4.png
new file mode 100644
index 0000000..52ff01f
--- /dev/null
+++ b/doc/sphinx/uml/requestLease4.png
Binary files differ
diff --git a/doc/sphinx/uml/requestLease4.svg b/doc/sphinx/uml/requestLease4.svg
new file mode 100644
index 0000000..2b7d0a7
--- /dev/null
+++ b/doc/sphinx/uml/requestLease4.svg
@@ -0,0 +1,540 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="2628px" preserveAspectRatio="none" style="width:1543px;height:2628px;background:#FFFFFF;" version="1.1" viewBox="0 0 1543 2628" width="1543px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="248" x="646" y="34.9659">requestLease4 algorithm (Kea 1.8.0)</text>
+ <ellipse cx="611.8438" cy="60.0679" fill="#222222" rx="10" ry="10" style="stroke:#222222;stroke-width:1.0;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="144" x="539.8438" y="90.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="124" x="549.8438" y="112.896">get lease for the client</text>
+ <polygon fill="#F1F1F1" points="566.8438,146.412,656.8438,146.412,668.8438,158.412,656.8438,170.412,566.8438,170.412,554.8438,158.412,566.8438,146.412" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="90" x="566.8438" y="162.68">reserved address</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="537.8438" y="155.1891">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="668.8438" y="155.1891">no</text>
+ <polygon fill="#F1F1F1" points="218.5,180.412,315.5,180.412,327.5,192.412,315.5,204.412,218.5,204.412,206.5,192.412,218.5,180.412" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="97" x="218.5" y="196.68">requested address</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="192.5" y="189.1891">no</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="327.5" y="189.1891">yes</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="236" x="11" y="214.412"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="216" x="21" y="237.2401">requested address = reserved address</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="105" x="352.5" y="263.903"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="85" x="362.5" y="286.7311">return no lease</text>
+ <ellipse cx="405" cy="346.2471" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="405" cy="346.2471" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="279,214.412,531,214.412,543,226.412,531,238.412,279,238.412,267,226.412,279,214.412" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="409" y="250.1709">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="252" x="279" y="230.68">requested address is reserved for another client</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="543" y="223.1891">no</text>
+ <polygon fill="#F1F1F1" points="267,388.738,279,400.738,267,412.738,255,400.738,267,388.738" style="stroke:#181818;stroke-width:0.5;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="105" x="214.5" y="546.7199"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="85" x="224.5" y="569.548">return no lease</text>
+ <ellipse cx="267" cy="629.064" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="267" cy="629.064" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="175,497.229,359,497.229,371,509.229,359,521.229,175,521.229,163,509.229,175,497.229" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="271" y="532.9879">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="184" x="175" y="513.4969">active and owned by another client</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="371" y="506.006">no</text>
+ <polygon fill="#F1F1F1" points="194.5,447.738,339.5,447.738,351.5,459.738,339.5,471.738,194.5,471.738,182.5,459.738,194.5,447.738" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="271" y="483.4969">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="145" x="194.5" y="464.006">lease for requested address</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="351.5" y="456.5151">no</text>
+ <polygon fill="#F1F1F1" points="267,697.0459,279,709.0459,267,721.0459,255,709.0459,267,697.0459" style="stroke:#181818;stroke-width:0.5;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="105" x="214.5" y="904.5188"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="85" x="224.5" y="927.3468">return no lease</text>
+ <ellipse cx="267" cy="986.8629" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="267" cy="986.8629" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="252,855.0278,282,855.0278,294,867.0278,282,879.0278,252,879.0278,240,867.0278,252,855.0278" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="271" y="890.7867">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="30" x="252" y="871.2958">active</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="294" y="863.8048">no</text>
+ <polygon fill="#F1F1F1" points="194.5,805.5369,339.5,805.5369,351.5,817.5369,339.5,829.5369,194.5,829.5369,182.5,817.5369,194.5,805.5369" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="271" y="841.2958">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="145" x="194.5" y="821.8048">lease for requested address</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="351.5" y="814.3139">no</text>
+ <polygon fill="#F1F1F1" points="267,1054.8448,279,1066.8448,267,1078.8448,255,1066.8448,267,1054.8448" style="stroke:#181818;stroke-width:0.5;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="105" x="214.5" y="1163.3357"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="85" x="224.5" y="1186.1638">return no lease</text>
+ <ellipse cx="267" cy="1241.2574" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="267" cy="1241.2574" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="176.5,1113.8448,357.5,1113.8448,369.5,1125.8448,357.5,1137.8448,176.5,1137.8448,164.5,1125.8448,176.5,1113.8448" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="271" y="1149.6037">no</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="181" x="176.5" y="1130.1127">requested address in allowed pool</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="369.5" y="1122.6218">yes</text>
+ <polygon fill="#F1F1F1" points="164.5,756.0459,369.5,756.0459,381.5,768.0459,369.5,780.0459,164.5,780.0459,152.5,768.0459,164.5,756.0459" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="271" y="791.8048">no</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="205" x="164.5" y="772.3139">requested address == reserved address</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="381.5" y="764.8229">yes</text>
+ <polygon fill="#F1F1F1" points="267,1303.7484,279,1315.7484,267,1327.7484,255,1315.7484,267,1303.7484" style="stroke:#181818;stroke-width:0.5;"/>
+ <polygon fill="#F1F1F1" points="908.1875,180.412,1005.1875,180.412,1017.1875,192.412,1005.1875,204.412,908.1875,204.412,896.1875,192.412,908.1875,180.412" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="97" x="908.1875" y="196.68">requested address</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="879.1875" y="189.1891">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="1017.1875" y="189.1891">no</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="105" x="680.5" y="263.903"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="85" x="690.5" y="286.7311">return no lease</text>
+ <ellipse cx="733" cy="346.2471" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="733" cy="346.2471" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="607,214.412,859,214.412,871,226.412,859,238.412,607,238.412,595,226.412,607,214.412" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="737" y="250.1709">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="252" x="607" y="230.68">requested address is reserved for another client</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="871" y="223.1891">no</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="105" x="680.5" y="516.7199"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="85" x="690.5" y="539.548">return no lease</text>
+ <ellipse cx="733" cy="599.064" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="733" cy="599.064" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="641,467.229,825,467.229,837,479.229,825,491.229,641,491.229,629,479.229,641,467.229" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="737" y="502.9879">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="184" x="641" y="483.4969">active and owned by another client</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="837" y="476.006">no</text>
+ <polygon fill="#F1F1F1" points="660.5,417.738,805.5,417.738,817.5,429.738,805.5,441.738,660.5,441.738,648.5,429.738,660.5,417.738" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="737" y="453.4969">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="145" x="660.5" y="434.006">lease for requested address</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="817.5" y="426.5151">no</text>
+ <polygon fill="#F1F1F1" points="733,667.0459,745,679.0459,733,691.0459,721,679.0459,733,667.0459" style="stroke:#181818;stroke-width:0.5;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="105" x="680.5" y="775.5369"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="85" x="690.5" y="798.365">return no lease</text>
+ <ellipse cx="733" cy="857.881" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="733" cy="857.881" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="642.5,726.0459,823.5,726.0459,835.5,738.0459,823.5,750.0459,642.5,750.0459,630.5,738.0459,642.5,726.0459" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="737" y="761.8048">no</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="181" x="642.5" y="742.3139">requested address in allowed pool</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="835.5" y="734.8229">yes</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="148" x="1106.375" y="311.903"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="128" x="1116.375" y="334.7311">pick candidate address</text>
+ <polygon fill="#F1F1F1" points="1130.875,476.229,1229.875,476.229,1241.875,488.229,1229.875,500.229,1130.875,500.229,1118.875,488.229,1130.875,476.229" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="99" x="1130.875" y="492.4969">lease for candidate</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="1104.875" y="485.006">no</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="1241.875" y="485.006">yes</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="177" x="967" y="510.229"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="157" x="977" y="533.0571">create and return new lease</text>
+ <ellipse cx="1055.5" cy="592.5731" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="1055.5" cy="592.5731" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="140" x="1235.25" y="559.7199"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="120" x="1245.25" y="582.548">reclaim expired lease</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="160" x="1225.25" y="631.064"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="140" x="1235.25" y="653.8921">update lease information</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="135" x="1237.75" y="702.4081"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="115" x="1247.75" y="725.2362">callout lease4_select</text>
+ <polygon fill="#F1F1F1" points="1269.25,773.7523,1341.25,773.7523,1353.25,785.7523,1341.25,797.7523,1269.25,797.7523,1257.25,785.7523,1269.25,773.7523" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="72" x="1269.25" y="790.0202">callout return</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="24" x="1233.25" y="782.5293">SKIP</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="56" x="1353.25" y="782.5293">CONTINUE</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="105" x="1184" y="807.7523"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="85" x="1194" y="830.5804">return no lease</text>
+ <ellipse cx="1236.5" cy="890.0964" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="1236.5" cy="890.0964" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="92" x="1328" y="807.7523"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="72" x="1338" y="830.5804">update lease</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="130" x="1309" y="879.0964"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="110" x="1319" y="901.9245">return reused lease</text>
+ <ellipse cx="1374" cy="961.4405" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="1374" cy="961.4405" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="1285.25,510.229,1325.25,510.229,1337.25,522.229,1325.25,534.229,1285.25,534.229,1273.25,522.229,1285.25,510.229" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="1309.25" y="545.9879">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="40" x="1285.25" y="526.4969">expired</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="1337.25" y="519.006">no</text>
+ <polygon fill="#F1F1F1" points="1085.875,426.738,1274.875,426.738,1286.875,438.738,1274.875,450.738,1085.875,450.738,1073.875,438.738,1085.875,426.738" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="1184.375" y="462.4969">no</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="189" x="1085.875" y="443.006">candidate is used by another thread</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="1286.875" y="435.5151">yes</text>
+ <polygon fill="#F1F1F1" points="1180.375,1041.4224,1192.375,1053.4224,1180.375,1065.4224,1168.375,1053.4224,1180.375,1041.4224" style="stroke:#181818;stroke-width:0.5;"/>
+ <polygon fill="#F1F1F1" points="1077.375,377.2471,1283.375,377.2471,1295.375,389.2471,1283.375,401.2471,1077.375,401.2471,1065.375,389.2471,1077.375,377.2471" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="1184.375" y="413.006">no</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="206" x="1077.375" y="393.5151">candidate is reserved for another client</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="1295.375" y="386.0241">yes</text>
+ <polygon fill="#F1F1F1" points="1180.375,1090.9133,1192.375,1102.9133,1180.375,1114.9133,1168.375,1102.9133,1180.375,1090.9133" style="stroke:#181818;stroke-width:0.5;"/>
+ <polygon fill="#F1F1F1" points="1100.375,263.903,1260.375,263.903,1272.375,275.903,1260.375,287.903,1100.375,287.903,1088.375,275.903,1100.375,263.903" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="160" x="1100.375" y="280.1709">iterate over pools and subnets</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="129" x="1115.875" y="1173.9133"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="109" x="1125.875" y="1196.7414">maximum attempts</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="105" x="1127.875" y="1240.8351"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="85" x="1137.875" y="1263.6632">return no lease</text>
+ <ellipse cx="1180.375" cy="1317.6882" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="1180.375" cy="1317.6882" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="1061.875,214.412,1298.875,214.412,1310.875,226.412,1298.875,238.412,1061.875,238.412,1049.875,226.412,1061.875,214.412" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="1184.375" y="250.1709">no</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="237" x="1061.875" y="230.68">client lease and lease address in allowed pool</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="1310.875" y="223.1891">yes</text>
+ <polygon fill="#F1F1F1" points="956.6875,1356.6882,968.6875,1368.6882,956.6875,1380.6882,944.6875,1368.6882,956.6875,1356.6882" style="stroke:#181818;stroke-width:0.5;"/>
+ <polygon fill="#F1F1F1" points="611.8438,1386.6882,623.8438,1398.6882,611.8438,1410.6882,599.8438,1398.6882,611.8438,1386.6882" style="stroke:#181818;stroke-width:0.5;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="160" x="531.8438" y="1579.1611"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="140" x="541.8438" y="1601.9892">update lease information</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="140" x="541.8438" y="1684.9961"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="120" x="551.8438" y="1707.8242">reclaim expired lease</text>
+ <polygon fill="#F1F1F1" points="567.3438,1635.5052,656.3438,1635.5052,668.3438,1647.5052,656.3438,1659.5052,567.3438,1659.5052,555.3438,1647.5052,567.3438,1635.5052" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="615.8438" y="1671.2641">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="89" x="567.3438" y="1651.7731">old lease expired</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="668.3438" y="1644.2822">no</text>
+ <polygon fill="#F1F1F1" points="611.8438,1741.3402,623.8438,1753.3402,611.8438,1765.3402,599.8438,1753.3402,611.8438,1741.3402" style="stroke:#181818;stroke-width:0.5;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="137" x="543.3438" y="1785.3402"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="117" x="553.3438" y="1808.1683">callout lease4_renew</text>
+ <polygon fill="#F1F1F1" points="575.8438,1841.6844,647.8438,1841.6844,659.8438,1853.6844,647.8438,1865.6844,575.8438,1865.6844,563.8438,1853.6844,575.8438,1841.6844" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="72" x="575.8438" y="1857.9523">callout return</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="24" x="539.8438" y="1850.4614">SKIP</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="56" x="659.8438" y="1850.4614">CONTINUE</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="141" x="452.8438" y="1875.6844"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="121" x="462.8438" y="1898.5124">return old client lease</text>
+ <ellipse cx="523.3438" cy="1943.0285" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="523.3438" cy="1943.0285" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="92" x="654.3438" y="1875.6844"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="72" x="664.3438" y="1898.5124">update lease</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="173" x="613.8438" y="1932.0285"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="153" x="623.8438" y="1954.8566">return renewed client lease</text>
+ <ellipse cx="700.3438" cy="1999.3726" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="700.3438" cy="1999.3726" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <polygon fill="#F1F1F1" points="454.8438,1529.6701,768.8438,1529.6701,780.8438,1541.6701,768.8438,1553.6701,454.8438,1553.6701,442.8438,1541.6701,454.8438,1529.6701" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="615.8438" y="1565.429">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="314" x="454.8438" y="1545.9381">has reserved address or client lease address in allowed pool</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="780.8438" y="1538.4471">no</text>
+ <polygon fill="#F1F1F1" points="437.8438,1480.1792,785.8438,1480.1792,797.8438,1492.1792,785.8438,1504.1792,437.8438,1504.1792,425.8438,1492.1792,437.8438,1480.1792" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="615.8438" y="1515.9381">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="348" x="437.8438" y="1496.4471">no requested address or requested address == client lease address</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="797.8438" y="1488.9562">no</text>
+ <polygon fill="#F1F1F1" points="611.8438,2052.3726,623.8438,2064.3726,611.8438,2076.3726,599.8438,2064.3726,611.8438,2052.3726" style="stroke:#181818;stroke-width:0.5;"/>
+ <polygon fill="#F1F1F1" points="583.3438,1430.6882,640.3438,1430.6882,652.3438,1442.6882,640.3438,1454.6882,583.3438,1454.6882,571.3438,1442.6882,583.3438,1430.6882" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="615.8438" y="1466.4471">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="57" x="583.3438" y="1446.9562">client lease</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="652.3438" y="1439.4653">no</text>
+ <polygon fill="#F1F1F1" points="611.8438,2096.3726,623.8438,2108.3726,611.8438,2120.3726,599.8438,2108.3726,611.8438,2096.3726" style="stroke:#181818;stroke-width:0.5;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="198" x="512.8438" y="2140.3726"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="178" x="522.8438" y="2163.2007">get lease for requested address</text>
+ <polygon fill="#F1F1F1" points="570.8438,2196.7167,652.8438,2196.7167,664.8438,2208.7167,652.8438,2220.7167,570.8438,2220.7167,558.8438,2208.7167,570.8438,2196.7167" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="82" x="570.8438" y="2212.9847">requested lease</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="541.8438" y="2205.4937">yes</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="664.8438" y="2205.4937">no</text>
+ <polygon fill="#F1F1F1" points="409.7813,2230.7167,449.7813,2230.7167,461.7813,2242.7167,449.7813,2254.7167,409.7813,2254.7167,397.7813,2242.7167,409.7813,2230.7167" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="40" x="409.7813" y="2246.9847">expired</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="14" x="383.7813" y="2239.4937">no</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="17" x="461.7813" y="2239.4937">yes</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="105" x="275.4063" y="2264.7167"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="85" x="285.4063" y="2287.5448">return no lease</text>
+ <ellipse cx="327.9063" cy="2347.0608" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="327.9063" cy="2347.0608" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="140" x="461.6563" y="2264.7167"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="120" x="471.6563" y="2287.5448">reclaim expired lease</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="160" x="451.6563" y="2336.0608"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="140" x="461.6563" y="2358.8889">update lease information</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="135" x="464.1563" y="2392.4049"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="115" x="474.1563" y="2415.233">callout lease4_select</text>
+ <polygon fill="#F1F1F1" points="495.6563,2448.749,567.6563,2448.749,579.6563,2460.749,567.6563,2472.749,495.6563,2472.749,483.6563,2460.749,495.6563,2448.749" style="stroke:#181818;stroke-width:0.5;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="72" x="495.6563" y="2465.017">callout return</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="24" x="459.6563" y="2457.5261">SKIP</text>
+ <text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacing" textLength="56" x="579.6563" y="2457.5261">CONTINUE</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="105" x="410.4063" y="2482.749"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="85" x="420.4063" y="2505.5771">return no lease</text>
+ <ellipse cx="462.9063" cy="2550.0932" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="462.9063" cy="2550.0932" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="92" x="554.4063" y="2482.749"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="72" x="564.4063" y="2505.5771">update lease</text>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="130" x="535.4063" y="2539.0932"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="110" x="545.4063" y="2561.9212">return reused lease</text>
+ <ellipse cx="600.4063" cy="2606.4373" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="600.4063" cy="2606.4373" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <rect fill="#F1F1F1" height="36.3441" rx="12.5" ry="12.5" style="stroke:#181818;stroke-width:0.5;" width="177" x="705.4063" y="2230.7167"/>
+ <text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="157" x="715.4063" y="2253.5448">create and return new lease</text>
+ <ellipse cx="793.9063" cy="2313.0608" fill="none" rx="11" ry="11" style="stroke:#222222;stroke-width:1.0;"/>
+ <ellipse cx="793.9063" cy="2313.0608" fill="#222222" rx="6" ry="6" style="stroke:#111111;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="70.0679" y2="90.0679"/>
+ <polygon fill="#181818" points="607.8438,80.0679,611.8438,90.0679,615.8438,80.0679,611.8438,84.0679" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="405" x2="405" y1="300.2471" y2="335.2471"/>
+ <polygon fill="#181818" points="401,325.2471,405,335.2471,409,325.2471,405,329.2471" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="405" x2="405" y1="238.412" y2="263.903"/>
+ <polygon fill="#181818" points="401,253.903,405,263.903,409,253.903,405,257.903" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="543" x2="555" y1="226.412" y2="226.412"/>
+ <polygon fill="#181818" points="551,306.575,555,316.575,559,306.575,555,310.575" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="555" x2="555" y1="226.412" y2="400.738"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="555" x2="279" y1="400.738" y2="400.738"/>
+ <polygon fill="#181818" points="289,396.738,279,400.738,289,404.738,285,400.738" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="206.5" x2="129" y1="192.412" y2="192.412"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="129" x2="129" y1="192.412" y2="214.412"/>
+ <polygon fill="#181818" points="125,204.412,129,214.412,133,204.412,129,208.412" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="327.5" x2="405" y1="192.412" y2="192.412"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="405" x2="405" y1="192.412" y2="214.412"/>
+ <polygon fill="#181818" points="401,204.412,405,214.412,409,204.412,405,208.412" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="129" x2="129" y1="250.7561" y2="400.738"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="129" x2="255" y1="400.738" y2="400.738"/>
+ <polygon fill="#181818" points="245,396.738,255,400.738,245,404.738,249,400.738" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="583.064" y2="618.064"/>
+ <polygon fill="#181818" points="263,608.064,267,618.064,271,608.064,267,612.064" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="521.229" y2="546.7199"/>
+ <polygon fill="#181818" points="263,536.7199,267,546.7199,271,536.7199,267,540.7199" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="371" x2="383" y1="509.229" y2="509.229"/>
+ <polygon fill="#181818" points="379,580.392,383,590.392,387,580.392,383,584.392" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="383" x2="383" y1="509.229" y2="671.555"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="383" x2="267" y1="671.555" y2="671.555"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="671.555" y2="697.0459"/>
+ <polygon fill="#181818" points="263,687.0459,267,697.0459,271,687.0459,267,691.0459" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="471.738" y2="497.229"/>
+ <polygon fill="#181818" points="263,487.229,267,497.229,271,487.229,267,491.229" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="351.5" x2="393" y1="459.738" y2="459.738"/>
+ <polygon fill="#181818" points="389,574.392,393,584.392,397,574.392,393,578.392" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="393" x2="393" y1="459.738" y2="709.0459"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="393" x2="279" y1="709.0459" y2="709.0459"/>
+ <polygon fill="#181818" points="289,705.0459,279,709.0459,289,713.0459,285,709.0459" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="412.738" y2="447.738"/>
+ <polygon fill="#181818" points="263,437.738,267,447.738,271,437.738,267,441.738" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="940.8629" y2="975.8629"/>
+ <polygon fill="#181818" points="263,965.8629,267,975.8629,271,965.8629,267,969.8629" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="879.0278" y2="904.5188"/>
+ <polygon fill="#181818" points="263,894.5188,267,904.5188,271,894.5188,267,898.5188" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="294" x2="329.5" y1="867.0278" y2="867.0278"/>
+ <polygon fill="#181818" points="325.5,938.1908,329.5,948.1908,333.5,938.1908,329.5,942.1908" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="329.5" x2="329.5" y1="867.0278" y2="1029.3538"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="329.5" x2="267" y1="1029.3538" y2="1029.3538"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="1029.3538" y2="1054.8448"/>
+ <polygon fill="#181818" points="263,1044.8448,267,1054.8448,271,1044.8448,267,1048.8448" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="829.5369" y2="855.0278"/>
+ <polygon fill="#181818" points="263,845.0278,267,855.0278,271,845.0278,267,849.0278" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="351.5" x2="363.5" y1="817.5369" y2="817.5369"/>
+ <polygon fill="#181818" points="359.5,932.1908,363.5,942.1908,367.5,932.1908,363.5,936.1908" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="363.5" x2="363.5" y1="817.5369" y2="1066.8448"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="363.5" x2="279" y1="1066.8448" y2="1066.8448"/>
+ <polygon fill="#181818" points="289,1062.8448,279,1066.8448,289,1070.8448,285,1066.8448" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="1199.6798" y2="1230.2574"/>
+ <polygon fill="#181818" points="263,1220.2574,267,1230.2574,271,1220.2574,267,1224.2574" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="1137.8448" y2="1163.3357"/>
+ <polygon fill="#181818" points="263,1153.3357,267,1163.3357,271,1153.3357,267,1157.3357" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="369.5" x2="381.5" y1="1125.8448" y2="1125.8448"/>
+ <polygon fill="#181818" points="377.5,1197.0078,381.5,1207.0078,385.5,1197.0078,381.5,1201.0078" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="381.5" x2="381.5" y1="1125.8448" y2="1283.7484"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="381.5" x2="267" y1="1283.7484" y2="1283.7484"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="1283.7484" y2="1303.7484"/>
+ <polygon fill="#181818" points="263,1293.7484,267,1303.7484,271,1293.7484,267,1297.7484" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="1078.8448" y2="1113.8448"/>
+ <polygon fill="#181818" points="263,1103.8448,267,1113.8448,271,1103.8448,267,1107.8448" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="780.0459" y2="805.5369"/>
+ <polygon fill="#181818" points="263,795.5369,267,805.5369,271,795.5369,267,799.5369" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="381.5" x2="393.5" y1="768.0459" y2="768.0459"/>
+ <polygon fill="#181818" points="389.5,1036.8538,393.5,1046.8538,397.5,1036.8538,393.5,1040.8538" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="393.5" x2="393.5" y1="768.0459" y2="1315.7484"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="393.5" x2="279" y1="1315.7484" y2="1315.7484"/>
+ <polygon fill="#181818" points="289,1311.7484,279,1315.7484,289,1319.7484,285,1315.7484" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="721.0459" y2="756.0459"/>
+ <polygon fill="#181818" points="263,746.0459,267,756.0459,271,746.0459,267,750.0459" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="733" x2="733" y1="300.2471" y2="335.2471"/>
+ <polygon fill="#181818" points="729,325.2471,733,335.2471,737,325.2471,733,329.2471" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="733" x2="733" y1="238.412" y2="263.903"/>
+ <polygon fill="#181818" points="729,253.903,733,263.903,737,253.903,733,257.903" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="871" x2="883" y1="226.412" y2="226.412"/>
+ <polygon fill="#181818" points="879,297.575,883,307.575,887,297.575,883,301.575" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="883" x2="883" y1="226.412" y2="382.738"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="883" x2="733" y1="382.738" y2="382.738"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="733" x2="733" y1="382.738" y2="417.738"/>
+ <polygon fill="#181818" points="729,407.738,733,417.738,737,407.738,733,411.738" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="733" x2="733" y1="553.064" y2="588.064"/>
+ <polygon fill="#181818" points="729,578.064,733,588.064,737,578.064,733,582.064" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="733" x2="733" y1="491.229" y2="516.7199"/>
+ <polygon fill="#181818" points="729,506.7199,733,516.7199,737,506.7199,733,510.7199" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="837" x2="849" y1="479.229" y2="479.229"/>
+ <polygon fill="#181818" points="845,550.392,849,560.392,853,550.392,849,554.392" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="849" x2="849" y1="479.229" y2="641.555"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="849" x2="733" y1="641.555" y2="641.555"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="733" x2="733" y1="641.555" y2="667.0459"/>
+ <polygon fill="#181818" points="729,657.0459,733,667.0459,737,657.0459,733,661.0459" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="733" x2="733" y1="441.738" y2="467.229"/>
+ <polygon fill="#181818" points="729,457.229,733,467.229,737,457.229,733,461.229" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="817.5" x2="859" y1="429.738" y2="429.738"/>
+ <polygon fill="#181818" points="855,544.392,859,554.392,863,544.392,859,548.392" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="859" x2="859" y1="429.738" y2="679.0459"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="859" x2="745" y1="679.0459" y2="679.0459"/>
+ <polygon fill="#181818" points="755,675.0459,745,679.0459,755,683.0459,751,679.0459" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="733" x2="733" y1="811.881" y2="846.881"/>
+ <polygon fill="#181818" points="729,836.881,733,846.881,737,836.881,733,840.881" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="733" x2="733" y1="750.0459" y2="775.5369"/>
+ <polygon fill="#181818" points="729,765.5369,733,775.5369,737,765.5369,733,769.5369" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="835.5" x2="847.5" y1="738.0459" y2="738.0459"/>
+ <polygon fill="#181818" points="843.5,1053.0692,847.5,1063.0692,851.5,1053.0692,847.5,1057.0692" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="847.5" x2="847.5" y1="738.0459" y2="1368.6882"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="847.5" x2="944.6875" y1="1368.6882" y2="1368.6882"/>
+ <polygon fill="#181818" points="934.6875,1364.6882,944.6875,1368.6882,934.6875,1372.6882,938.6875,1368.6882" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="733" x2="733" y1="691.0459" y2="726.0459"/>
+ <polygon fill="#181818" points="729,716.0459,733,726.0459,737,716.0459,733,720.0459" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1055.5" x2="1055.5" y1="546.5731" y2="581.5731"/>
+ <polygon fill="#181818" points="1051.5,571.5731,1055.5,581.5731,1059.5,571.5731,1055.5,575.5731" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1305.25" x2="1305.25" y1="596.064" y2="631.064"/>
+ <polygon fill="#181818" points="1301.25,621.064,1305.25,631.064,1309.25,621.064,1305.25,625.064" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1305.25" x2="1305.25" y1="667.4081" y2="702.4081"/>
+ <polygon fill="#181818" points="1301.25,692.4081,1305.25,702.4081,1309.25,692.4081,1305.25,696.4081" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1236.5" x2="1236.5" y1="844.0964" y2="879.0964"/>
+ <polygon fill="#181818" points="1232.5,869.0964,1236.5,879.0964,1240.5,869.0964,1236.5,873.0964" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1374" x2="1374" y1="844.0964" y2="879.0964"/>
+ <polygon fill="#181818" points="1370,869.0964,1374,879.0964,1378,869.0964,1374,873.0964" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1374" x2="1374" y1="915.4405" y2="950.4405"/>
+ <polygon fill="#181818" points="1370,940.4405,1374,950.4405,1378,940.4405,1374,944.4405" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1257.25" x2="1236.5" y1="785.7523" y2="785.7523"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1236.5" x2="1236.5" y1="785.7523" y2="807.7523"/>
+ <polygon fill="#181818" points="1232.5,797.7523,1236.5,807.7523,1240.5,797.7523,1236.5,801.7523" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1353.25" x2="1374" y1="785.7523" y2="785.7523"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1374" x2="1374" y1="785.7523" y2="807.7523"/>
+ <polygon fill="#181818" points="1370,797.7523,1374,807.7523,1378,797.7523,1374,801.7523" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1305.25" x2="1305.25" y1="738.7523" y2="773.7523"/>
+ <polygon fill="#181818" points="1301.25,763.7523,1305.25,773.7523,1309.25,763.7523,1305.25,767.7523" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1305.25" x2="1305.25" y1="534.229" y2="559.7199"/>
+ <polygon fill="#181818" points="1301.25,549.7199,1305.25,559.7199,1309.25,549.7199,1305.25,553.7199" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1337.25" x2="1453" y1="522.229" y2="522.229"/>
+ <polygon fill="#181818" points="1449,759.0802,1453,769.0802,1457,759.0802,1453,763.0802" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1453" x2="1453" y1="522.229" y2="1015.9314"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1453" x2="1180.375" y1="1015.9314" y2="1015.9314"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1180.375" x2="1180.375" y1="1015.9314" y2="1041.4224"/>
+ <polygon fill="#181818" points="1176.375,1031.4224,1180.375,1041.4224,1184.375,1031.4224,1180.375,1035.4224" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1118.875" x2="1055.5" y1="488.229" y2="488.229"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1055.5" x2="1055.5" y1="488.229" y2="510.229"/>
+ <polygon fill="#181818" points="1051.5,500.229,1055.5,510.229,1059.5,500.229,1055.5,504.229" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1241.875" x2="1305.25" y1="488.229" y2="488.229"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1305.25" x2="1305.25" y1="488.229" y2="510.229"/>
+ <polygon fill="#181818" points="1301.25,500.229,1305.25,510.229,1309.25,500.229,1305.25,504.229" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1180.375" x2="1180.375" y1="450.738" y2="476.229"/>
+ <polygon fill="#181818" points="1176.375,466.229,1180.375,476.229,1184.375,466.229,1180.375,470.229" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1286.875" x2="1471" y1="438.738" y2="438.738"/>
+ <polygon fill="#181818" points="1467,736.0802,1471,746.0802,1475,736.0802,1471,740.0802" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1471" x2="1471" y1="438.738" y2="1053.4224"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1471" x2="1192.375" y1="1053.4224" y2="1053.4224"/>
+ <polygon fill="#181818" points="1202.375,1049.4224,1192.375,1053.4224,1202.375,1057.4224,1198.375,1053.4224" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1180.375" x2="1180.375" y1="401.2471" y2="426.738"/>
+ <polygon fill="#181818" points="1176.375,416.738,1180.375,426.738,1184.375,416.738,1180.375,420.738" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1295.375" x2="1489" y1="389.2471" y2="389.2471"/>
+ <polygon fill="#181818" points="1485,736.0802,1489,746.0802,1493,736.0802,1489,740.0802" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1489" x2="1489" y1="389.2471" y2="1102.9133"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1489" x2="1192.375" y1="1102.9133" y2="1102.9133"/>
+ <polygon fill="#181818" points="1202.375,1098.9133,1192.375,1102.9133,1202.375,1106.9133,1198.375,1102.9133" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1180.375" x2="1180.375" y1="1065.4224" y2="1090.9133"/>
+ <polygon fill="#181818" points="1176.375,1080.9133,1180.375,1090.9133,1184.375,1080.9133,1180.375,1084.9133" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1180.375" x2="1180.375" y1="348.2471" y2="377.2471"/>
+ <polygon fill="#181818" points="1176.375,367.2471,1180.375,377.2471,1184.375,367.2471,1180.375,371.2471" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1180.375" x2="1180.375" y1="287.903" y2="311.903"/>
+ <polygon fill="#181818" points="1176.375,301.903,1180.375,311.903,1184.375,301.903,1180.375,305.903" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1180.375" x2="1180.375" y1="1114.9133" y2="1126.9133"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1180.375" x2="1507" y1="1126.9133" y2="1126.9133"/>
+ <polygon fill="#181818" points="1503,708.4081,1507,698.4081,1511,708.4081,1507,704.4081" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1507" x2="1507" y1="275.903" y2="1126.9133"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1507" x2="1272.375" y1="275.903" y2="275.903"/>
+ <polygon fill="#181818" points="1282.375,271.903,1272.375,275.903,1282.375,279.903,1278.375,275.903" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1088.375" x2="925" y1="275.903" y2="275.903"/>
+ <polygon fill="#181818" points="921,694.4081,925,704.4081,929,694.4081,925,698.4081" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="925" x2="925" y1="275.903" y2="1138.9133"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="925" x2="1180.375" y1="1138.9133" y2="1138.9133"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1180.375" x2="1180.375" y1="1138.9133" y2="1173.9133"/>
+ <polygon fill="#181818" points="1176.375,1163.9133,1180.375,1173.9133,1184.375,1163.9133,1180.375,1167.9133" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1180.375" x2="1180.375" y1="1210.2574" y2="1240.8351"/>
+ <polygon fill="#181818" points="1176.375,1230.8351,1180.375,1240.8351,1184.375,1230.8351,1180.375,1234.8351" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1180.375" x2="1180.375" y1="1277.1792" y2="1306.6882"/>
+ <polygon fill="#181818" points="1176.375,1296.6882,1180.375,1306.6882,1184.375,1296.6882,1180.375,1300.6882" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1180.375" x2="1180.375" y1="238.412" y2="263.903"/>
+ <polygon fill="#181818" points="1176.375,253.903,1180.375,263.903,1184.375,253.903,1180.375,257.903" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1310.875" x2="1517" y1="226.412" y2="226.412"/>
+ <polygon fill="#181818" points="1513,794.2523,1517,804.2523,1521,794.2523,1517,798.2523" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1517" x2="1517" y1="226.412" y2="1368.6882"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1517" x2="968.6875" y1="1368.6882" y2="1368.6882"/>
+ <polygon fill="#181818" points="978.6875,1364.6882,968.6875,1368.6882,978.6875,1372.6882,974.6875,1368.6882" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="896.1875" x2="733" y1="192.412" y2="192.412"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="733" x2="733" y1="192.412" y2="214.412"/>
+ <polygon fill="#181818" points="729,204.412,733,214.412,737,204.412,733,208.412" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1017.1875" x2="1180.375" y1="192.412" y2="192.412"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="1180.375" x2="1180.375" y1="192.412" y2="214.412"/>
+ <polygon fill="#181818" points="1176.375,204.412,1180.375,214.412,1184.375,204.412,1180.375,208.412" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="554.8438" x2="267" y1="158.412" y2="158.412"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="158.412" y2="180.412"/>
+ <polygon fill="#181818" points="263,170.412,267,180.412,271,170.412,267,174.412" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="668.8438" x2="956.6875" y1="158.412" y2="158.412"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="956.6875" x2="956.6875" y1="158.412" y2="180.412"/>
+ <polygon fill="#181818" points="952.6875,170.412,956.6875,180.412,960.6875,170.412,956.6875,174.412" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="267" y1="1327.7484" y2="1398.6882"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="267" x2="599.8438" y1="1398.6882" y2="1398.6882"/>
+ <polygon fill="#181818" points="589.8438,1394.6882,599.8438,1398.6882,589.8438,1402.6882,593.8438,1398.6882" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="956.6875" x2="956.6875" y1="1380.6882" y2="1398.6882"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="956.6875" x2="623.8438" y1="1398.6882" y2="1398.6882"/>
+ <polygon fill="#181818" points="633.8438,1394.6882,623.8438,1398.6882,633.8438,1402.6882,629.8438,1398.6882" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="126.412" y2="146.412"/>
+ <polygon fill="#181818" points="607.8438,136.412,611.8438,146.412,615.8438,136.412,611.8438,140.412" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="1659.5052" y2="1684.9961"/>
+ <polygon fill="#181818" points="607.8438,1674.9961,611.8438,1684.9961,615.8438,1674.9961,611.8438,1678.9961" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="668.3438" x2="691.8438" y1="1647.5052" y2="1647.5052"/>
+ <polygon fill="#181818" points="687.8438,1693.1682,691.8438,1703.1682,695.8438,1693.1682,691.8438,1697.1682" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="691.8438" x2="691.8438" y1="1647.5052" y2="1753.3402"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="691.8438" x2="623.8438" y1="1753.3402" y2="1753.3402"/>
+ <polygon fill="#181818" points="633.8438,1749.3402,623.8438,1753.3402,633.8438,1757.3402,629.8438,1753.3402" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="1721.3402" y2="1741.3402"/>
+ <polygon fill="#181818" points="607.8438,1731.3402,611.8438,1741.3402,615.8438,1731.3402,611.8438,1735.3402" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="1615.5052" y2="1635.5052"/>
+ <polygon fill="#181818" points="607.8438,1625.5052,611.8438,1635.5052,615.8438,1625.5052,611.8438,1629.5052" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="1765.3402" y2="1785.3402"/>
+ <polygon fill="#181818" points="607.8438,1775.3402,611.8438,1785.3402,615.8438,1775.3402,611.8438,1779.3402" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="523.3438" x2="523.3438" y1="1912.0285" y2="1932.0285"/>
+ <polygon fill="#181818" points="519.3438,1922.0285,523.3438,1932.0285,527.3438,1922.0285,523.3438,1926.0285" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="700.3438" x2="700.3438" y1="1912.0285" y2="1932.0285"/>
+ <polygon fill="#181818" points="696.3438,1922.0285,700.3438,1932.0285,704.3438,1922.0285,700.3438,1926.0285" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="700.3438" x2="700.3438" y1="1968.3726" y2="1988.3726"/>
+ <polygon fill="#181818" points="696.3438,1978.3726,700.3438,1988.3726,704.3438,1978.3726,700.3438,1982.3726" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="563.8438" x2="523.3438" y1="1853.6844" y2="1853.6844"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="523.3438" x2="523.3438" y1="1853.6844" y2="1875.6844"/>
+ <polygon fill="#181818" points="519.3438,1865.6844,523.3438,1875.6844,527.3438,1865.6844,523.3438,1869.6844" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="659.8438" x2="700.3438" y1="1853.6844" y2="1853.6844"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="700.3438" x2="700.3438" y1="1853.6844" y2="1875.6844"/>
+ <polygon fill="#181818" points="696.3438,1865.6844,700.3438,1875.6844,704.3438,1865.6844,700.3438,1869.6844" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="1821.6844" y2="1841.6844"/>
+ <polygon fill="#181818" points="607.8438,1831.6844,611.8438,1841.6844,615.8438,1831.6844,611.8438,1835.6844" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="1553.6701" y2="1579.1611"/>
+ <polygon fill="#181818" points="607.8438,1569.1611,611.8438,1579.1611,615.8438,1569.1611,611.8438,1573.1611" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="780.8438" x2="806.8438" y1="1541.6701" y2="1541.6701"/>
+ <polygon fill="#181818" points="802.8438,1789.5213,806.8438,1799.5213,810.8438,1789.5213,806.8438,1793.5213" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="806.8438" x2="806.8438" y1="1541.6701" y2="2032.3726"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="806.8438" x2="611.8438" y1="2032.3726" y2="2032.3726"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="2032.3726" y2="2052.3726"/>
+ <polygon fill="#181818" points="607.8438,2042.3726,611.8438,2052.3726,615.8438,2042.3726,611.8438,2046.3726" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="1504.1792" y2="1529.6701"/>
+ <polygon fill="#181818" points="607.8438,1519.6701,611.8438,1529.6701,615.8438,1519.6701,611.8438,1523.6701" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="797.8438" x2="828.8438" y1="1492.1792" y2="1492.1792"/>
+ <polygon fill="#181818" points="824.8438,1783.5213,828.8438,1793.5213,832.8438,1783.5213,828.8438,1787.5213" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="828.8438" x2="828.8438" y1="1492.1792" y2="2064.3726"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="828.8438" x2="623.8438" y1="2064.3726" y2="2064.3726"/>
+ <polygon fill="#181818" points="633.8438,2060.3726,623.8438,2064.3726,633.8438,2068.3726,629.8438,2064.3726" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="1454.6882" y2="1480.1792"/>
+ <polygon fill="#181818" points="607.8438,1470.1792,611.8438,1480.1792,615.8438,1470.1792,611.8438,1474.1792" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="652.3438" x2="850.8438" y1="1442.6882" y2="1442.6882"/>
+ <polygon fill="#181818" points="846.8438,1783.5213,850.8438,1793.5213,854.8438,1783.5213,850.8438,1787.5213" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="850.8438" x2="850.8438" y1="1442.6882" y2="2108.3726"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="850.8438" x2="623.8438" y1="2108.3726" y2="2108.3726"/>
+ <polygon fill="#181818" points="633.8438,2104.3726,623.8438,2108.3726,633.8438,2112.3726,629.8438,2108.3726" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="2076.3726" y2="2096.3726"/>
+ <polygon fill="#181818" points="607.8438,2086.3726,611.8438,2096.3726,615.8438,2086.3726,611.8438,2090.3726" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="1410.6882" y2="1430.6882"/>
+ <polygon fill="#181818" points="607.8438,1420.6882,611.8438,1430.6882,615.8438,1420.6882,611.8438,1424.6882" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="2120.3726" y2="2140.3726"/>
+ <polygon fill="#181818" points="607.8438,2130.3726,611.8438,2140.3726,615.8438,2130.3726,611.8438,2134.3726" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="327.9063" x2="327.9063" y1="2301.0608" y2="2336.0608"/>
+ <polygon fill="#181818" points="323.9063,2326.0608,327.9063,2336.0608,331.9063,2326.0608,327.9063,2330.0608" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="531.6563" x2="531.6563" y1="2301.0608" y2="2336.0608"/>
+ <polygon fill="#181818" points="527.6563,2326.0608,531.6563,2336.0608,535.6563,2326.0608,531.6563,2330.0608" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="531.6563" x2="531.6563" y1="2372.4049" y2="2392.4049"/>
+ <polygon fill="#181818" points="527.6563,2382.4049,531.6563,2392.4049,535.6563,2382.4049,531.6563,2386.4049" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="462.9063" x2="462.9063" y1="2519.0932" y2="2539.0932"/>
+ <polygon fill="#181818" points="458.9063,2529.0932,462.9063,2539.0932,466.9063,2529.0932,462.9063,2533.0932" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="600.4063" x2="600.4063" y1="2519.0932" y2="2539.0932"/>
+ <polygon fill="#181818" points="596.4063,2529.0932,600.4063,2539.0932,604.4063,2529.0932,600.4063,2533.0932" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="600.4063" x2="600.4063" y1="2575.4373" y2="2595.4373"/>
+ <polygon fill="#181818" points="596.4063,2585.4373,600.4063,2595.4373,604.4063,2585.4373,600.4063,2589.4373" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="483.6563" x2="462.9063" y1="2460.749" y2="2460.749"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="462.9063" x2="462.9063" y1="2460.749" y2="2482.749"/>
+ <polygon fill="#181818" points="458.9063,2472.749,462.9063,2482.749,466.9063,2472.749,462.9063,2476.749" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="579.6563" x2="600.4063" y1="2460.749" y2="2460.749"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="600.4063" x2="600.4063" y1="2460.749" y2="2482.749"/>
+ <polygon fill="#181818" points="596.4063,2472.749,600.4063,2482.749,604.4063,2472.749,600.4063,2476.749" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="531.6563" x2="531.6563" y1="2428.749" y2="2448.749"/>
+ <polygon fill="#181818" points="527.6563,2438.749,531.6563,2448.749,535.6563,2438.749,531.6563,2442.749" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="397.7813" x2="327.9063" y1="2242.7167" y2="2242.7167"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="327.9063" x2="327.9063" y1="2242.7167" y2="2264.7167"/>
+ <polygon fill="#181818" points="323.9063,2254.7167,327.9063,2264.7167,331.9063,2254.7167,327.9063,2258.7167" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="461.7813" x2="531.6563" y1="2242.7167" y2="2242.7167"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="531.6563" x2="531.6563" y1="2242.7167" y2="2264.7167"/>
+ <polygon fill="#181818" points="527.6563,2254.7167,531.6563,2264.7167,535.6563,2254.7167,531.6563,2258.7167" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="793.9063" x2="793.9063" y1="2267.0608" y2="2302.0608"/>
+ <polygon fill="#181818" points="789.9063,2292.0608,793.9063,2302.0608,797.9063,2292.0608,793.9063,2296.0608" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="558.8438" x2="429.7813" y1="2208.7167" y2="2208.7167"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="429.7813" x2="429.7813" y1="2208.7167" y2="2230.7167"/>
+ <polygon fill="#181818" points="425.7813,2220.7167,429.7813,2230.7167,433.7813,2220.7167,429.7813,2224.7167" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="664.8438" x2="793.9063" y1="2208.7167" y2="2208.7167"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="793.9063" x2="793.9063" y1="2208.7167" y2="2230.7167"/>
+ <polygon fill="#181818" points="789.9063,2220.7167,793.9063,2230.7167,797.9063,2220.7167,793.9063,2224.7167" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="611.8438" x2="611.8438" y1="2176.7167" y2="2196.7167"/>
+ <polygon fill="#181818" points="607.8438,2186.7167,611.8438,2196.7167,615.8438,2186.7167,611.8438,2190.7167" style="stroke:#181818;stroke-width:1.0;"/>
+ <!--SRC=[tLPDRzim3BtxL_2sv59iG0z3Wvs67OeEtO1jvq6rcLgeBNaIdRJ_Vf8_8bcI7QV3W66d6F8BzKY-HveuLGWAVxMewJCoZTV0gaUfE9KrREwGmRkhzrTljrccYIdARXwHeB90s4i5L2Ba5KT1kopZUzWerAWEM00h2lEijnOYOFE2Ufi1z93kjXWZf8K0t4G8-01Xu9s1OcLeZA5dWWFNxh-MCXFINAS6sW6_uNfgbG0X-qHtmxacsNIF_RKZMnG5tsVkrx9m1PfDTiAQvSGFQAWL88_2W1zU_XvFxzJdd2XwNFMmLPScwwURU3cVb9TMc9XBOoxbMMa88_PA7irH8sKL9hUwnASCfZKUZll3p54xvuD_HD1_rjqBnVn_TTwtHv_oC3DjKOQdjKoE9JSZUiC95IC4UJ1bj4ZT1TFjWq3oozZm_0boyuuN5k-DIzSYztgTc1oRP8HMJxjCfK9MfFySgkeKQ0vkKifDEEgBDxO5Zw5A8gtuYfaepT70vuOhB99wz7IPLupNC83JznfSstJrw5FZmYHNC-9I9A2vRNaxYEBwfyOASufnVQ5xQ9_uoFJRtUtNcEgIYHPAvChqySlzzzlx7v-IeVtyTWjtA-pqSE7z9yE6d68znDZm9DdqAIW1yG7ckJFUE0vgziphjWP6X7L39p-zUavqlyDgU0DiJzPq9UPFMJWq4fzGvhPufgL6jDtXqm6qi6fAfgE5RwEc0ipDBN_jd3E4lLDMuq1SykAo1-UNGcmrXMPq-0JM-6jiiYNh5sDXHSLEcl6FSuqXXclYdrbvyVUTEndq5dp4HwhPPTEjEIV3e6NUztl24qCAWH-MEdj6G4jJT5rdLvetx6MoaqlJpyMRkjzrwioAwmBqV_eD]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/requestLease4.uml b/doc/sphinx/uml/requestLease4.uml
new file mode 100644
index 0000000..4cb55b5
--- /dev/null
+++ b/doc/sphinx/uml/requestLease4.uml
@@ -0,0 +1,150 @@
+@startuml
+
+title requestLease4 algorithm (Kea 1.8.0)
+
+start
+:get lease for the client;
+
+if (reserved address) then (yes)
+ if (requested address) then (no)
+ :requested address = reserved address;
+ else (yes)
+ if (requested address is reserved for another client) then (yes)
+ :return no lease;
+ stop
+ else (no)
+ endif
+ endif
+ if (lease for requested address) then (yes)
+ if (active and owned by another client) then (yes)
+ :return no lease;
+ stop
+ else (no)
+ endif
+ else (no)
+ endif
+ if (requested address == reserved address) then (no)
+ if (lease for requested address) then (yes)
+ if (active) then (yes)
+ :return no lease;
+ stop
+ else (no)
+ endif
+ else (no)
+ endif
+ if (requested address in allowed pool) then (no)
+ :return no lease;
+ stop
+ else (yes)
+ endif
+ else (yes)
+ endif
+
+else (no)
+
+ if (requested address) then (yes)
+ if (requested address is reserved for another client) then (yes)
+ :return no lease;
+ stop
+ else (no)
+ endif
+ if (lease for requested address) then (yes)
+ if (active and owned by another client) then (yes)
+ :return no lease;
+ stop
+ else (no)
+ endif
+ else (no)
+ endif
+ if (requested address in allowed pool) then (no)
+ :return no lease;
+ stop
+ else (yes)
+ endif
+ else (no)
+ if (client lease and lease address in allowed pool) then (no)
+ while (iterate over pools and subnets)
+ :pick candidate address;
+ if (candidate is reserved for another client) then (no)
+ if (candidate is used by another thread) then (no)
+ if (lease for candidate) then (no)
+ :create and return new lease;
+ stop
+ else (yes)
+ if (expired) then (yes)
+ :reclaim expired lease;
+ :update lease information;
+ :callout lease4_select;
+ if (callout return) then (SKIP)
+ :return no lease;
+ stop
+ else (CONTINUE)
+ :update lease;
+ :return reused lease;
+ stop
+ endif
+ else (no)
+ endif
+ endif
+ else (yes)
+ endif
+ else (yes)
+ endif
+ endwhile
+ :maximum attempts;
+ :return no lease;
+ stop
+ else (yes)
+ endif
+ endif
+endif
+
+' after check
+if (client lease) then (yes)
+ if (no requested address or requested address == client lease address) then (yes)
+ if (has reserved address or client lease address in allowed pool) then (yes)
+ :update lease information;
+ if (old lease expired) then (yes)
+ :reclaim expired lease;
+ else (no)
+ endif
+ :callout lease4_renew;
+ if (callout return) then (SKIP)
+ :return old client lease;
+ stop
+ else (CONTINUE)
+ :update lease;
+ :return renewed client lease;
+ stop
+ endif
+ else (no)
+ endif
+ else (no)
+ endif
+else (no)
+endif
+
+:get lease for requested address;
+if (requested lease) then (yes)
+ if (expired) then (no)
+ :return no lease;
+ stop
+ else (yes)
+ :reclaim expired lease;
+ :update lease information;
+ :callout lease4_select;
+ if (callout return) then (SKIP)
+ :return no lease;
+ stop
+ else (CONTINUE)
+ :update lease;
+ :return reused lease;
+ stop
+ endif
+ endif
+else (no)
+ :create and return new lease;
+ stop
+endif
+
+@enduml
diff --git a/doc/sphinx/uml/select4.png b/doc/sphinx/uml/select4.png
new file mode 100644
index 0000000..a89f54c
--- /dev/null
+++ b/doc/sphinx/uml/select4.png
Binary files differ
diff --git a/doc/sphinx/uml/select4.svg b/doc/sphinx/uml/select4.svg
new file mode 100644
index 0000000..b733a70
--- /dev/null
+++ b/doc/sphinx/uml/select4.svg
@@ -0,0 +1,345 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="2308px" preserveAspectRatio="none" style="width:1429px;height:2308px;background:#FFFFFF;" version="1.1" viewBox="0 0 1429 2308" width="1429px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="248" x="583.5" y="24.9659">DHCPv4 subnet selection (Kea 1.8.0)</text>
+ <!--cluster relayed-->
+ <g id="cluster_relayed">
+ <rect fill="none" height="391.06" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="442" x="7" y="359.2779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="54" x="201" y="376.2439">Relayed</text>
+ </g>
+ <!--cluster set_address-->
+ <g id="cluster_set_address">
+ <rect fill="none" height="172.93" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="792" x="226" y="817.3379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="157" x="543.5" y="834.3039">Set address for lookup</text>
+ </g>
+ <!--cluster interface-->
+ <g id="cluster_interface">
+ <rect fill="none" height="509.27" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="392" x="919" y="1049.2679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="156" x="1037" y="1066.2339">Try incoming interface</text>
+ </g>
+ <!--cluster address-->
+ <g id="cluster_address">
+ <rect fill="none" height="309.14" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:1.0;" width="186" x="737" y="1609.5379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="80" x="790" y="1626.5039">Try address</text>
+ </g>
+ <!--entity relay_subnet-->
+ <g id="elem_relay_subnet">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="226" x="170" y="451.1379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="206" x="180" y="476.1039">Relay address matches subnet</text>
+ </g>
+ <!--entity relay_network-->
+ <g id="elem_relay_network">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="284" x="141" y="569.1979"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="264" x="151" y="594.1639">Relay address matches shared network</text>
+ </g>
+ <!--entity relay_class-->
+ <g id="elem_relay_class">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="138" x="113" y="687.2679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="118" x="123" y="712.2339">Check client class</text>
+ </g>
+ <!--entity relay_address-->
+ <g id="elem_relay_address">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="136" x="250" y="927.1979"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="116" x="260" y="952.1639">Set relay address</text>
+ </g>
+ <!--entity client_address-->
+ <g id="elem_client_address">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="140" x="544" y="927.1979"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="120" x="554" y="952.1639">Set client address</text>
+ </g>
+ <!--entity source_address-->
+ <g id="elem_source_address">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="148" x="846" y="927.1979"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="128" x="856" y="952.1639">Set source address</text>
+ </g>
+ <!--entity interface_subnet-->
+ <g id="elem_interface_subnet">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="193" x="970.5" y="1087.2679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="173" x="980.5" y="1112.2339">Interface matches subnet</text>
+ </g>
+ <!--entity interface_network-->
+ <g id="elem_interface_network">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="251" x="1044.5" y="1205.3379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="231" x="1054.5" y="1230.3039">Interface matches shared network</text>
+ </g>
+ <!--entity interface_class-->
+ <g id="elem_interface_class">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="138" x="995" y="1323.3979"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="118" x="1005" y="1348.3639">Check client class</text>
+ </g>
+ <!--entity interface_address-->
+ <g id="elem_interface_address">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="163" x="935.5" y="1503.4679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="143" x="945.5" y="1528.4339">Set interface address</text>
+ </g>
+ <!--entity inRange-->
+ <g id="elem_inRange">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="154" x="753" y="1647.5379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="134" x="763" y="1672.5039">Check subnet prefix</text>
+ </g>
+ <!--entity address_class-->
+ <g id="elem_address_class">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="138" x="769" y="1863.6079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="118" x="779" y="1888.5739">Check client class</text>
+ </g>
+ <!--entity entry-->
+ <g id="elem_entry">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="94" x="348" y="47.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="74" x="358" y="72.0339">Entry point</text>
+ </g>
+ <!--entity rai_link_select-->
+ <g id="elem_rai_link_select">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="139" x="325.5" y="147.1379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="119" x="335.5" y="172.1039">Try RAI link select</text>
+ </g>
+ <!--entity subnet_select-->
+ <g id="elem_subnet_select">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="203" x="293.5" y="247.2079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="183" x="303.5" y="272.1739">Try subnet selection option</text>
+ </g>
+ <!--entity found-->
+ <g id="elem_found">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="125" x="696.5" y="2045.6779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="105" x="706.5" y="2070.6439">Found a subnet</text>
+ </g>
+ <!--entity not_found-->
+ <g id="elem_not_found">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="134" x="856" y="2045.6779"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="114" x="866" y="2070.6439">Found no subnet</text>
+ </g>
+ <!--entity subnet4_select-->
+ <g id="elem_subnet4_select">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="171" x="754.5" y="2145.7379"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="151" x="764.5" y="2170.7039">Callout subnet4_select</text>
+ </g>
+ <!--entity success-->
+ <g id="elem_success">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="129" x="575.5" y="2263.8079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="109" x="585.5" y="2288.7739">Return a subnet</text>
+ </g>
+ <!--entity no_subnet-->
+ <g id="elem_no_subnet">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="138" x="771" y="2263.8079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="118" x="781" y="2288.7739">Return no subnet</text>
+ </g>
+ <!--entity drop-->
+ <g id="elem_drop">
+ <rect fill="#F1F1F1" height="39.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="96" x="967" y="2263.8079"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="76" x="977" y="2288.7739">Drop query</text>
+ </g>
+ <!--link entry to rai_link_select-->
+ <g id="link_entry_rai_link_select">
+ <path d="M395,86.5179 C395,101.8479 395,123.5879 395,140.4079 " fill="none" id="entry-to-rai_link_select" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="395,145.3079,399,136.3079,395,140.3079,391,136.3079,395,145.3079" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link rai_link_select to subnet_select-->
+ <g id="link_rai_link_select_subnet_select">
+ <path d="M395,186.5779 C395,201.9079 395,223.6579 395,240.4779 " fill="none" id="rai_link_select-to-subnet_select" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="395,245.3779,399,236.3779,395,240.3779,391,236.3779,395,245.3779" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link subnet_select to relayed-->
+ <g id="link_subnet_select_relayed">
+ <path d="M386.56,286.6279 C379.085,303.3629 367.9325,328.3354 358.5325,349.3829 C357.3575,352.0139 356.2099,354.5835 355.1003,357.068 C354.8228,357.6891 354.5478,358.3049 354.2753,358.9151 " fill="none" id="subnet_select-to-relayed" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="354.2753,358.9151,361.5978,352.3286,356.3143,354.3497,354.2932,349.0662,354.2753,358.9151" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="44" x="373" y="331.1749">relayed</text>
+ </g>
+ <!--link subnet_select to set_address-->
+ <g id="link_subnet_select_set_address">
+ <path d="M430.75,286.5979 C463.86,306.9379 508,342.6679 508,388.6379 C508,388.6379 508,388.6379 508,707.8079 C508,756.6779 465.6075,791.0879 422.5525,813.4329 C421.207,814.1312 419.8609,814.8177 418.5154,815.4925 C417.8427,815.8299 417.1701,816.1643 416.4978,816.4959 C416.1617,816.6617 415.8256,816.8267 415.4897,816.991 " fill="none" id="subnet_select-to-set_address" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="415.4897,816.991,425.3319,816.63,419.9812,814.7942,421.817,809.4435,415.4897,816.991" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="68" x="509" y="535.0949">not relayed</text>
+ </g>
+ <!--link relayed to relay_subnet-->
+ <g id="link_relayed_relay_subnet">
+ <path d="M340.83,390.3679 C338.72,393.2679 316.86,423.2379 300.64,445.4879 " fill="none" id="relayed-to-relay_subnet" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="297.75,449.4479,306.2806,444.5256,300.6926,445.4055,299.8126,439.8175,297.75,449.4479" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link relay_subnet to relay_network-->
+ <g id="link_relay_subnet_relay_network">
+ <path d="M283,490.5779 C283,510.1379 283,540.7979 283,562.4379 " fill="none" id="relay_subnet-to-relay_network" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="283,567.3279,287,558.3279,283,562.3279,279,558.3279,283,567.3279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="57" x="284" y="535.0949">no match</text>
+ </g>
+ <!--link relay_subnet to relay_class-->
+ <g id="link_relay_subnet_relay_class">
+ <path d="M198.2,490.6879 C156.15,504.8679 109.09,528.9679 84,569.1979 C57.99,610.9179 108.63,656.8779 146.32,683.4379 " fill="none" id="relay_subnet-to-relay_class" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="150.06,686.0279,144.9432,677.6125,145.9511,683.1789,140.3848,684.1868,150.06,686.0279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="38" x="85" y="594.1349">match</text>
+ </g>
+ <!--link relay_network to set_address-->
+ <g id="link_relay_network_set_address">
+ <path d="M286.68,608.6279 C292.36,637.8379 303.67,695.9654 313.6913,747.4692 C318.7019,773.221 323.3903,797.317 326.8917,815.3143 C327.0011,815.8767 327.1094,816.4332 327.2165,816.9836 " fill="none" id="relay_network-to-set_address" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="327.2165,816.9836,329.4241,807.3853,326.2616,812.0756,321.5714,808.9131,327.2165,816.9836" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="57" x="317" y="712.2049">no match</text>
+ </g>
+ <!--link relay_network to relay_class-->
+ <g id="link_relay_network_relay_class">
+ <path d="M266.55,608.6479 C249.09,628.7079 221.46,660.4579 202.57,682.1679 " fill="none" id="relay_network-to-relay_class" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="199.44,685.7679,208.3589,681.5903,202.7163,681.9909,202.3157,676.3482,199.44,685.7679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="38" x="240" y="653.1649">match</text>
+ </g>
+ <!--link relay_class to found-->
+ <g id="link_relay_class_found">
+ <path d="M178.01,726.5779 C172.7,753.1779 164,803.4179 164,846.6979 C164,846.6979 164,846.6979 164,1976.1779 C164,2028.7979 532.92,2053.1279 689.77,2061.0979 " fill="none" id="relay_class-to-found" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="694.66,2061.3479,685.8653,2056.9148,689.6659,2061.1047,685.476,2064.9053,694.66,2061.3479" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="67" x="165" y="1438.3649">compatible</text>
+ </g>
+ <!--link relay_class to set_address-->
+ <g id="link_relay_class_set_address">
+ <path d="M186.01,726.6779 C190.9,745.6379 200.85,774.6479 219,793.3379 C223.795,798.2767 229.1359,802.8079 234.8159,806.9572 C237.6558,809.0318 240.5806,811.0109 243.5642,812.8978 C245.056,813.8412 246.5625,814.7615 248.0805,815.6592 C248.8395,816.108 249.6014,816.5511 250.3657,816.9887 C250.5568,817.0981 250.7481,817.2071 250.9395,817.3158 " fill="none" id="relay_class-to-set_address" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="250.9395,817.3158,245.0885,809.3933,246.5916,814.8468,241.1381,816.3499,250.9395,817.3158" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="91" x="220" y="789.2349">not compatible</text>
+ </g>
+ <!--link set_address to relay_address-->
+ <g id="link_set_address_relay_address">
+ <path d="M332.96,848.4779 C332.38,852.2479 326.08,893.1779 321.85,920.7279 " fill="none" id="set_address-to-relay_address" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="321.1,925.5779,326.4166,917.2873,321.8571,920.6356,318.5088,916.076,321.1,925.5779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="122" x="329" y="893.0949">try the relay address</text>
+ </g>
+ <!--link relay_address to client_address-->
+ <g id="link_relay_address_client_address">
+ <path d="M386.28,946.7279 C431.37,946.7279 490.52,946.7279 537.24,946.7279 " fill="none" id="relay_address-to-client_address" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="542.24,946.7279,533.24,942.7279,537.24,946.7279,533.24,950.7279,542.24,946.7279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="122" x="404" y="939.6249">has no relay address</text>
+ </g>
+ <!--link client_address to source_address-->
+ <g id="link_client_address_source_address">
+ <path d="M684.2,946.7279 C730.39,946.7279 790.98,946.7279 839.17,946.7279 " fill="none" id="client_address-to-source_address" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="844.07,946.7279,835.07,942.7279,839.07,946.7279,835.07,950.7279,844.07,946.7279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="125" x="702.5" y="939.6249">has no client address</text>
+ </g>
+ <!--link source_address to interface_subnet-->
+ <g id="link_source_address_interface_subnet">
+ <path d="M937.44,966.4879 C964.41,995.4779 1016.16,1051.1279 1045.43,1082.6079 " fill="none" id="source_address-to-interface_subnet" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1048.55,1085.9679,1045.3321,1076.6596,1045.1379,1082.3131,1039.4844,1082.119,1048.55,1085.9679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="116" x="998" y="1029.1649">no suitable address</text>
+ </g>
+ <!--link relay_address to inRange-->
+ <g id="link_relay_address_inRange">
+ <path d="M318,966.7079 C318,996.3879 318,1055.5779 318,1105.7979 C318,1105.7979 318,1105.7979 318,1524.0079 C318,1611.7779 600.91,1647.5579 746.13,1660.1479 " fill="none" id="relay_address-to-inRange" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="751.01,1660.5679,742.3868,1655.8097,746.0285,1660.1385,741.6997,1663.7801,751.01,1660.5679" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="113" x="319" y="1348.3349">has a relay address</text>
+ </g>
+ <!--link client_address to inRange-->
+ <g id="link_client_address_inRange">
+ <path d="M588.81,966.6479 C556.06,993.6079 503,1046.4479 503,1105.7979 C503,1105.7979 503,1105.7979 503,1524.0079 C503,1629.9979 649.31,1657.7579 746.2,1664.5179 " fill="none" id="client_address-to-inRange" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="751,1664.8379,742.2817,1660.2564,746.0108,1664.5101,741.7571,1668.2392,751,1664.8379" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="116" x="504" y="1348.3349">has a client address</text>
+ </g>
+ <!--link source_address to inRange-->
+ <g id="link_source_address_inRange">
+ <path d="M845.65,965.7379 C778.78,986.8879 691,1029.2279 691,1105.7979 C691,1105.7979 691,1105.7979 691,1524.0079 C691,1578.9879 744.85,1620.5679 785.52,1644.1279 " fill="none" id="source_address-to-inRange" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="789.59,1646.4379,783.7463,1638.51,785.2444,1643.9649,779.7895,1645.463,789.59,1646.4379" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="210" x="692" y="1348.3349">has source address and use unicast</text>
+ </g>
+ <!--link interface_subnet to not_found-->
+ <g id="link_interface_subnet_not_found">
+ <path d="M1163.65,1109.3879 C1245.29,1116.4879 1349,1141.6779 1349,1223.8679 C1349,1223.8679 1349,1223.8679 1349,1976.1779 C1349,2047.3079 1118.33,2061.4179 996.47,2063.9179 " fill="none" id="interface_subnet-to-not_found" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="991.54,2064.0079,1000.6182,2067.8272,996.539,2063.9079,1000.4582,2059.8288,991.54,2064.0079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="71" x="1350" y="1589.4349">no interface</text>
+ </g>
+ <!--link interface_subnet to interface_class-->
+ <g id="link_interface_subnet_interface_class">
+ <path d="M1044.43,1126.7879 C1025,1144.7379 998.36,1173.5779 987,1205.3379 C971.83,1247.7379 1008.42,1292.3879 1036.21,1318.6679 " fill="none" id="interface_subnet-to-interface_class" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1039.82,1322.0079,1035.9155,1312.9661,1036.1443,1318.6183,1030.4921,1318.8471,1039.82,1322.0079" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="38" x="988" y="1230.2649">match</text>
+ </g>
+ <!--link interface_subnet to interface_network-->
+ <g id="link_interface_subnet_interface_network">
+ <path d="M1083.78,1126.7079 C1101.58,1146.7779 1129.76,1178.5179 1149.02,1200.2279 " fill="none" id="interface_subnet-to-interface_network" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1152.22,1203.8379,1149.2304,1194.4538,1148.8982,1200.1009,1143.2511,1199.7687,1152.22,1203.8379" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="57" x="1125" y="1171.2349">no match</text>
+ </g>
+ <!--link interface_network to interface_class-->
+ <g id="link_interface_network_interface_class">
+ <path d="M1152.73,1244.7779 C1134.41,1264.8379 1105.41,1296.5879 1085.59,1318.2979 " fill="none" id="interface_network-to-interface_class" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1082.28,1321.9179,1091.306,1317.9769,1085.6547,1318.2286,1085.4031,1312.5773,1082.28,1321.9179" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="38" x="1124" y="1289.2949">march</text>
+ </g>
+ <!--link interface_network to not_found-->
+ <g id="link_interface_network_not_found">
+ <path d="M1189.38,1244.8879 C1209.17,1266.4279 1237,1303.5479 1237,1341.9379 C1237,1341.9379 1237,1341.9379 1237,1976.1779 C1237,2025.8479 1089.48,2048.7879 996.67,2058.2979 " fill="none" id="interface_network-to-not_found" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="991.87,2058.7879,1001.2242,2061.8699,996.845,2058.289,1000.4259,2053.9098,991.87,2058.7879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="57" x="1238" y="1672.4649">no match</text>
+ </g>
+ <!--link interface_class to found-->
+ <g id="link_interface_class_found">
+ <path d="M1085.86,1362.7379 C1101.31,1377.4979 1120.83,1399.5079 1130,1423.4679 C1179.63,1553.1279 1133.94,1597.0079 1142,1735.6079 C1143,1752.7379 1145,1756.9479 1145,1774.1079 C1145,1774.1079 1145,1774.1079 1145,1976.1779 C1145,2045.9079 906.97,2030.0779 839,2045.6779 C835.44,2046.4879 831.79,2047.3279 828.1,2048.1779 " fill="none" id="interface_class-to-found" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="823.35,2049.2779,833.0206,2051.1435,828.221,2048.1494,831.215,2043.3499,823.35,2049.2779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="67" x="1143" y="1731.5049">compatible</text>
+ </g>
+ <!--link interface_class to interface_address-->
+ <g id="link_interface_class_interface_address">
+ <path d="M1055.33,1362.8379 C1048.36,1378.7179 1038.81,1402.1679 1033,1423.4679 C1026.32,1447.9579 1022.05,1476.6679 1019.61,1496.7679 " fill="none" id="interface_class-to-interface_address" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1019.03,1501.7279,1024.0584,1493.2594,1019.6167,1496.7625,1016.1137,1492.3207,1019.03,1501.7279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="91" x="1034" y="1438.3649">not compatible</text>
+ </g>
+ <!--link interface_address to inRange-->
+ <g id="link_interface_address_inRange">
+ <path d="M935.09,1535.7079 C890.06,1543.9979 840.63,1556.8179 827,1574.5379 C812.66,1593.1879 815.9,1620.8679 821.19,1640.8979 " fill="none" id="interface_address-to-inRange" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="822.54,1645.6579,823.9213,1635.9064,821.1702,1640.8492,816.2273,1638.0981,822.54,1645.6579" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="144" x="828" y="1589.4349">has an interface address</text>
+ </g>
+ <!--link interface_address to not_found-->
+ <g id="link_interface_address_not_found">
+ <path d="M1013.09,1542.7479 C1007.8,1569.8479 999,1621.5679 999,1666.0679 C999,1666.0679 999,1666.0679 999,1976.1779 C999,2003.1679 978.28,2025.9679 958.48,2041.6079 " fill="none" id="interface_address-to-not_found" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="954.68,2044.4979,964.2621,2042.2211,958.6558,2041.466,959.411,2035.8597,954.68,2044.4979" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="121" x="1000" y="1829.5049">no interface address</text>
+ </g>
+ <!--link inRange to address_class-->
+ <g id="link_inRange_address_class">
+ <path d="M830.71,1687.0679 C832.15,1725.7079 835.43,1813.4579 837.06,1856.8679 " fill="none" id="inRange-to-address_class" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="837.24,1861.6779,840.9032,1852.5356,837.0544,1856.6814,832.9087,1852.8326,837.24,1861.6779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="38" x="835" y="1780.5049">match</text>
+ </g>
+ <!--link inRange to not_found-->
+ <g id="link_inRange_not_found">
+ <path d="M759.3,1687.0679 C718.42,1702.8079 675,1729.9279 675,1774.1079 C675,1774.1079 675,1774.1079 675,1976.1779 C675,1994.8679 776.34,2025.6379 849.46,2045.3979 " fill="none" id="inRange-to-not_found" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="854.1,2046.6479,846.4494,2040.4457,849.2719,2045.348,844.3696,2048.1706,854.1,2046.6479" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="57" x="676" y="1888.5349">no match</text>
+ </g>
+ <!--link address_class to found-->
+ <g id="link_address_class_found">
+ <path d="M803.16,1902.9579 C753.3,1930.6979 665.77,1982.7179 649,2014.6779 C637.19,2037.1879 661.3,2049.4879 689.93,2056.1979 " fill="none" id="address_class-to-found" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="694.62,2057.2179,686.6857,2051.3829,689.7361,2056.1469,684.9721,2059.1972,694.62,2057.2179" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="67" x="701" y="1980.5749">compatible</text>
+ </g>
+ <!--link address_class to not_found-->
+ <g id="link_address_class_not_found">
+ <path d="M845.19,1902.9179 C853.15,1923.2979 866.63,1956.6779 880,1984.6779 C888.98,2003.4679 900.2,2024.1479 908.93,2039.7079 " fill="none" id="address_class-to-not_found" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="911.28,2043.8779,910.3433,2034.0737,908.8239,2039.5227,903.3749,2038.0034,911.28,2043.8779" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="91" x="881" y="1980.5749">not compatible</text>
+ </g>
+ <!--link found to subnet4_select-->
+ <g id="link_found_subnet4_select">
+ <path d="M774.62,2085.1179 C787.73,2100.9879 806.52,2123.7379 820.59,2140.7779 " fill="none" id="found-to-subnet4_select" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="823.47,2144.2579,820.8228,2134.7715,820.286,2140.4028,814.6546,2139.8659,823.47,2144.2579" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link not_found to subnet4_select-->
+ <g id="link_not_found_subnet4_select">
+ <path d="M907,2085.1179 C893.56,2100.9879 874.31,2123.7379 859.89,2140.7779 " fill="none" id="not_found-to-subnet4_select" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="856.93,2144.2679,865.7977,2139.9827,860.1604,2140.4515,859.6915,2134.8141,856.93,2144.2679" style="stroke:#181818;stroke-width:1.0;"/>
+ </g>
+ <!--link found to not_found-->
+ <!--link subnet4_select to success-->
+ <g id="link_subnet4_select_success">
+ <path d="M754.28,2174.9179 C724.09,2181.6979 691.77,2193.6879 668,2214.8079 C655.63,2225.7979 648.6,2243.1079 644.67,2257.4579 " fill="none" id="subnet4_select-to-success" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="643.55,2261.9279,649.6348,2254.1836,644.7761,2257.0806,641.8791,2252.2218,643.55,2261.9279" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="158" x="669" y="2229.7049">CONTINUE and subnet set</text>
+ </g>
+ <!--link subnet4_select to no_subnet-->
+ <g id="link_subnet4_select_no_subnet">
+ <path d="M838.56,2185.1679 C837.95,2194.1379 837.3,2205.0179 837,2214.8079 C836.57,2228.7979 837.19,2244.3979 837.98,2257.0879 " fill="none" id="subnet4_select-to-no_subnet" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="838.3,2261.9179,841.6731,2252.6647,837.957,2256.9297,833.692,2253.2135,838.3,2261.9179" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="133" x="838" y="2229.7049">SKIP or subnet not set</text>
+ </g>
+ <!--link subnet4_select to drop-->
+ <g id="link_subnet4_select_drop">
+ <path d="M922.26,2185.2079 C941.3,2192.2879 960.5,2201.8879 976,2214.8079 C989.56,2226.1179 999.42,2243.4179 1005.84,2257.6779 " fill="none" id="subnet4_select-to-drop" style="stroke:#181818;stroke-width:1.0;"/>
+ <polygon fill="#181818" points="1007.78,2262.1879,1007.9113,2252.3399,1005.8104,2257.5922,1000.5581,2255.4913,1007.78,2262.1879" style="stroke:#181818;stroke-width:1.0;"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="35" x="992" y="2229.7049">DROP</text>
+ </g>
+ <!--SRC=[ZPPDRzim38Rl_XL2JziXnGRqCFHGO4WwB2ZG1cbt6eP0jPbOY2jvahmj6FRVfo_hotBJIskHpqjHf4n74ja1MdrTRdvT8J4yKv18G0Ur98oYTtU0qSVBJvSVtbSLFW2LwEAMIdv2FIDKNY0i4EYrzpuft_Rp6dM47bqWGt5CTjgsixQ4dspBUltFwApFgxZwY-b1fNonXGwVeB7HxJFwMo4NrtWHRXeEGg0NBEiMncX1ihE6SweMSnLSWRyPFqPgP_7oPGlr4TKTqQkwmq94i5bN_v8pFAfZZxlj6KSTOyUXjmTNfnvTuLXQmECaemsS9M5TBZ5iJKLQi87NaD3MvEaqVTqyGclsGkX1FKZWUro3qVbLbFpQsmhzy8Ay9mNLj1z1_SQU187jIro6a7jSYQ2OBqRCJvjdKt6tlUUm9tzSvAsA0UTITW73HNBu5pRG1k6uc7jjoW3AOe8okKkf9UuwDaW7NUsYbpSrUSKMvC1fkhCOwbhN9cFopSScZzYAinxz782V3D2ePLMPoO8MYvl9zCZMXaadHRAoCToCk1wV2apyqbth6dbsLFj8umdYHUHrrz14iHTtZZJjL5mCZUfIJhFWV3XhMsZ8z5ovrKlROqcUEyYOKX4YE7Q6JGEkEoPRI8TKbL8BheJf65BArboCJ9XHHfcEfA1CPrsLOJhPVCROUeg1IApEDfUj4PetrEs5poHP4BmbjrYHJcA4LSy60MYWfCP2LfE3cDxwTzgUoqCb_2QVWFxAd84B5tlgd8lFP-XfyddarwzmaPiCxEbjdYA5vj7f1w4eVAK3aSw6DdGoqAFwHqYnAgbkhYO5AesMLCAQmnprmpve2axd-DwIfW7w8zgXIaadDjy6jVdouVvfVV_jrbni__DFbaJ-Mw5aZtVh3L8_avn17s56fJyWIh3QFcog_m00]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/select4.uml b/doc/sphinx/uml/select4.uml
new file mode 100644
index 0000000..ed8b65c
--- /dev/null
+++ b/doc/sphinx/uml/select4.uml
@@ -0,0 +1,85 @@
+@startuml
+
+title DHCPv4 subnet selection (Kea 1.8.0)
+
+agent "Entry point" as entry
+
+agent "Try RAI link select" as rai_link_select
+
+agent "Try subnet selection option" as subnet_select
+
+rectangle "Relayed" as relayed {
+ agent "Relay address matches subnet" as relay_subnet
+ agent "Relay address matches shared network" as relay_network
+ agent "Check client class" as relay_class
+}
+
+rectangle "Set address for lookup" as set_address {
+ agent "Set relay address" as relay_address
+ agent "Set client address" as client_address
+ agent "Set source address" as source_address
+}
+
+rectangle "Try incoming interface" as interface {
+ agent "Interface matches subnet" as interface_subnet
+ agent "Interface matches shared network" as interface_network
+ agent "Check client class" as interface_class
+ agent "Set interface address" as interface_address
+}
+
+rectangle "Try address" as address {
+ agent "Check subnet prefix" as inRange
+ agent "Check client class" as address_class
+}
+
+agent "Found a subnet" as found
+
+agent "Found no subnet" as not_found
+
+agent "Callout subnet4_select" as subnet4_select
+
+agent "Return a subnet" as success
+
+agent "Return no subnet" as no_subnet
+
+agent "Drop query" as drop
+
+entry --> rai_link_select
+rai_link_select --> subnet_select
+subnet_select --> relayed : relayed
+subnet_select --> set_address : not relayed
+relayed --> relay_subnet
+relay_subnet --> relay_network : no match
+relay_subnet --> relay_class : match
+relay_network --> set_address : no match
+relay_network --> relay_class : match
+relay_class ---> found : compatible
+relay_class --> set_address : not compatible
+set_address --> relay_address : try the relay address
+relay_address -r-> client_address : has no relay address
+client_address -r-> source_address : has no client address
+source_address --> interface_subnet : no suitable address
+relay_address --> inRange : has a relay address
+client_address --> inRange : has a client address
+source_address --> inRange : has source address and use unicast
+interface_subnet ---> not_found : no interface
+interface_subnet --> interface_class : match
+interface_subnet --> interface_network : no match
+interface_network --> interface_class : march
+interface_network ---> not_found : no match
+interface_class ---> found : compatible
+interface_class ---> interface_address : not compatible
+interface_address --> inRange : has an interface address
+interface_address ---> not_found : no interface address
+inRange ---> address_class : match
+inRange ---> not_found : no match
+address_class ---> found : compatible
+address_class ---> not_found : not compatible
+found --> subnet4_select
+not_found --> subnet4_select
+found -[hidden]> not_found
+subnet4_select --> success : CONTINUE and subnet set
+subnet4_select --> no_subnet : SKIP or subnet not set
+subnet4_select --> drop : DROP
+
+@enduml
diff --git a/doc/sphinx/uml/tkey.atxt b/doc/sphinx/uml/tkey.atxt
new file mode 100644
index 0000000..f348aee
--- /dev/null
+++ b/doc/sphinx/uml/tkey.atxt
@@ -0,0 +1,13 @@
+ TKEY Exchange (GSS-TSIG hook)
+
+ ,-------------. ,----------.
+ |Kea D2 server| |DNS server|
+ `------+------' `----+-----'
+ | TKEY request |
+ |---------------------->
+ | |
+ |TKEY response (signed)|
+ |<----------------------
+ ,------+------. ,----+-----.
+ |Kea D2 server| |DNS server|
+ `-------------' `----------'
diff --git a/doc/sphinx/uml/tkey.png b/doc/sphinx/uml/tkey.png
new file mode 100644
index 0000000..c10ec7c
--- /dev/null
+++ b/doc/sphinx/uml/tkey.png
Binary files differ
diff --git a/doc/sphinx/uml/tkey.svg b/doc/sphinx/uml/tkey.svg
new file mode 100644
index 0000000..25bd4c6
--- /dev/null
+++ b/doc/sphinx/uml/tkey.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="200px" preserveAspectRatio="none" style="width:271px;height:200px;background:#FFFFFF;" version="1.1" viewBox="0 0 271 200" width="271px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="220" x="24.5" y="29.9659">TKEY Exchange (GSS-TSIG hook)</text>
+ <line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="58" x2="58" y1="79.1358" y2="162.5479"/>
+ <line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="220" x2="220" y1="79.1358" y2="162.5479"/>
+ <rect fill="#E2E2F0" height="33.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="107" x="5" y="45.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="93" x="12" y="67.0339">Kea D2 server</text>
+ <rect fill="#E2E2F0" height="33.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="107" x="5" y="161.5479"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="93" x="12" y="183.5138">Kea D2 server</text>
+ <rect fill="#E2E2F0" height="33.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="89" x="176" y="45.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="75" x="183" y="67.0339">DNS server</text>
+ <rect fill="#E2E2F0" height="33.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="89" x="176" y="161.5479"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="75" x="183" y="183.5138">DNS server</text>
+ <polygon fill="#181818" points="208.5,108.8419,218.5,112.8419,208.5,116.8419,212.5,112.8419" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="58.5" x2="214.5" y1="112.8419" y2="112.8419"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="78" x="65.5" y="107.0328">TKEY request</text>
+ <polygon fill="#181818" points="69.5,140.5479,59.5,144.5479,69.5,148.5479,65.5,144.5479" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="63.5" x2="219.5" y1="144.5479" y2="144.5479"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="138" x="75.5" y="138.7389">TKEY response (signed)</text>
+ <!--SRC=[AyaioKbL2F5sZLHmhKZEICnBJrNGS0yErWq9zdHNoCZFpzRauYf8B2h9JCuiICmhKL3oJarKS35IA4ujAaijKb98B5O0Yg0gSV4BHfO7ShcuGDfqxK1iAuXrHQc5fQd59LmWnK09e3nSehWWFwyOw8xYpFIyr1HDBW00]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/tkey.uml b/doc/sphinx/uml/tkey.uml
new file mode 100644
index 0000000..4cc33e5
--- /dev/null
+++ b/doc/sphinx/uml/tkey.uml
@@ -0,0 +1,11 @@
+@startuml
+
+title TKEY Exchange (GSS-TSIG hook)
+
+participant "Kea D2 server" as Kea
+participant "DNS server" as DNS
+
+Kea -> DNS: TKEY request
+DNS -> Kea: TKEY response (signed)
+
+@enduml
diff --git a/doc/sphinx/uml/update.atxt b/doc/sphinx/uml/update.atxt
new file mode 100644
index 0000000..f5d1b02
--- /dev/null
+++ b/doc/sphinx/uml/update.atxt
@@ -0,0 +1,13 @@
+ DNS Update Exchange (GSS-TSIG hook)
+
+ ,-------------. ,----------.
+ |Kea D2 server| |DNS server|
+ `------+------' `----+-----'
+ |DNS update request (signed) |
+ |---------------------------->
+ | |
+ |DNS update response (signed)|
+ |<----------------------------
+ ,------+------. ,----+-----.
+ |Kea D2 server| |DNS server|
+ `-------------' `----------'
diff --git a/doc/sphinx/uml/update.png b/doc/sphinx/uml/update.png
new file mode 100644
index 0000000..9b0520e
--- /dev/null
+++ b/doc/sphinx/uml/update.png
Binary files differ
diff --git a/doc/sphinx/uml/update.svg b/doc/sphinx/uml/update.svg
new file mode 100644
index 0000000..fef2e38
--- /dev/null
+++ b/doc/sphinx/uml/update.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="us-ascii" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="200px" preserveAspectRatio="none" style="width:314px;height:200px;background:#FFFFFF;" version="1.1" viewBox="0 0 314 200" width="314px" zoomAndPan="magnify">
+ <defs/>
+ <g>
+ <text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="270" x="21" y="29.9659">DNS Update Exchange (GSS-TSIG hook)</text>
+ <line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="58" x2="58" y1="79.1358" y2="162.5479"/>
+ <line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="263" x2="263" y1="79.1358" y2="162.5479"/>
+ <rect fill="#E2E2F0" height="33.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="107" x="5" y="45.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="93" x="12" y="67.0339">Kea D2 server</text>
+ <rect fill="#E2E2F0" height="33.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="107" x="5" y="161.5479"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="93" x="12" y="183.5138">Kea D2 server</text>
+ <rect fill="#E2E2F0" height="33.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="89" x="219" y="45.0679"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="75" x="226" y="67.0339">DNS server</text>
+ <rect fill="#E2E2F0" height="33.0679" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="89" x="219" y="161.5479"/>
+ <text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="75" x="226" y="183.5138">DNS server</text>
+ <polygon fill="#181818" points="251.5,108.8419,261.5,112.8419,251.5,116.8419,255.5,112.8419" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="58.5" x2="257.5" y1="112.8419" y2="112.8419"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="172" x="65.5" y="107.0328">DNS update request (signed)</text>
+ <polygon fill="#181818" points="69.5,140.5479,59.5,144.5479,69.5,148.5479,65.5,144.5479" style="stroke:#181818;stroke-width:1.0;"/>
+ <line style="stroke:#181818;stroke-width:1.0;" x1="63.5" x2="262.5" y1="144.5479" y2="144.5479"/>
+ <text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="181" x="75.5" y="138.7389">DNS update response (signed)</text>
+ <!--SRC=[NOsz3i8m38JtFCMfKpjqOMHWAgeG4alA0rYjrKQW9CGkul59OE1dy_c-krEdLqPtiZZ7YPInVumB-PbHzzQsWptqM4AuDCP4Ikf65yahgYCJkWs4qvrJ1HBapozIIh_yB8qfiNPNxkrhT7sF9hwjB8fQtEnvQapnCfVnFqvYyC8Vy0a0]-->
+ </g>
+</svg>
diff --git a/doc/sphinx/uml/update.uml b/doc/sphinx/uml/update.uml
new file mode 100644
index 0000000..9738f30
--- /dev/null
+++ b/doc/sphinx/uml/update.uml
@@ -0,0 +1,11 @@
+@startuml
+
+title DNS Update Exchange (GSS-TSIG hook)
+
+participant "Kea D2 server" as Kea
+participant "DNS server" as DNS
+
+Kea -> DNS: DNS update request (signed)
+DNS -> Kea: DNS update response (signed)
+
+@enduml
diff --git a/doc/sphinx/umls.rst b/doc/sphinx/umls.rst
new file mode 100644
index 0000000..e5c7a94
--- /dev/null
+++ b/doc/sphinx/umls.rst
@@ -0,0 +1,159 @@
+..
+ Copyright (C) 2020-2023 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ See the COPYRIGHT file distributed with this work for additional
+ information regarding copyright ownership.
+
+.. _umls:
+
+Kea Flow Diagrams
+=================
+
+These flow diagrams describe Kea's DHCPv4 server implementation, and they may be useful for system administrators. To design a configuration that results in clients getting the intended addresses and options, it is important to understand the sequence of request-processing steps. For example, Kea iterates looking for a suitable address, and conditionally accepts the first available address, so the order in which addresses are evaluated matters.
+
+It is also useful to understand Kea's processing logic because there are configuration choices which can make the process far more efficient. Kea is very flexible, so it can be applied to very different use cases and in different environments. In an environment where throughput and efficiency are a priority, the administrator can choose to limit some of the processing steps. For example, it is possible to limit the number of different client identifiers Kea evaluates in looking for a host reservation, or even to skip the step of checking for host reservations.
+
+These diagrams are focused on those aspects of Kea processing that will be most useful to operators. The diagrams illustrate DHCPv4 request processing, but most of the logic applies equally to DHCPv6. Following the title of each diagram is a Kea version number. Kea behavior has evolved over time, and the diagrams document the behavior as of the Kea version indicated. These diagrams are provided in the Kea source tree in UML (source), PNG, and SVG formats.
+
+Main Loop
+^^^^^^^^^
+
+The main loop is common to both DHCPv4 and DHPCv6 servers.
+
+.. figure:: uml/main-loop.*
+
+ The DHCP server main loop
+
+.. _uml_packet4:
+
+DHCPv4 Packet Processing
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+DHCPv4 packet processing evaluates the type DHCP message: Discover, Request, Release, Decline, or Inform. This diagram shows the general, high-level flow for processing an inbound client DHCP packet from receipt to the server's response.
+
+.. figure:: uml/packet4.*
+
+ DHCPv4 packet processing
+
+.. _uml_request4:
+
+DHCPREQUEST Processing
+^^^^^^^^^^^^^^^^^^^^^^
+
+The following diagrams focus on DHCPREQUEST processing. This chart gives an overview of the process, from subnet selection to checking for host reservations to evaluating client classes. Finally, before acknowledging the lease, the options are evaluated and added to the message.
+
+.. figure:: uml/request4.*
+
+ DHCPREQUEST processing
+
+.. _uml_select4:
+
+DHCPv4 Subnet Selection
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Subnet selection is the process of choosing a subnet that is topologically appropriate for the client. When the selected subnet is a member of a shared network, the whole shared network is selected. During subnet selection the client class may be checked more than once while iterating through subnets, to determine whether it is permitted in the selected subnet.
+
+.. figure:: uml/select4.*
+
+ DHCPv4 subnet selection
+
+.. _uml_assign-lease4:
+
+DHCPv4 Special Case of Double-Booting
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+After subnet selection and before lease allocation, the DHCPv4 server handles the special case of clients restarting with an image provided by PXE boot or bootp. The Lease Request box is expanded below.
+
+.. figure:: uml/assign-lease4.*
+
+ DHCPv4 lease assignment
+
+.. _uml_request4-lease:
+
+DHCPv4 Lease Allocation
+^^^^^^^^^^^^^^^^^^^^^^^
+
+The first diagram below illustrates the details of processing the client request, showing the renewal of an existing lease, the assignment of a reserved lease, and the allocation of an unreserved lease.
+
+.. figure:: uml/request4-lease.*
+
+ DHCPREQUEST lease allocation
+
+The second diagram shows the algorithm used to validate a requested lease or select a new address to offer. The right-hand side of the diagram shows how a new address is selected, when a new lease is required and the client has neither a requested address nor a reservation. When a new lease is required and Kea iterates over pools and subnets, it starts with the subnet selected above in the subnet selection process.
+
+.. figure:: uml/requestLease4.*
+
+ The requestLease4 algorithm
+
+.. note::
+
+ Declined addresses are included in the statistic for assigned addresses,
+ so the :math:`assigned + free = total` equation is true.
+
+.. _uml_lease-states:
+
+Lease States
+^^^^^^^^^^^^
+
+This diagram illustrates the different lease states, including the ``free`` one, where no lease object exists.
+
+.. figure:: uml/lease-states.*
+
+ Lease states
+
+.. _uml_currentHost4:
+
+Checking for Host Reservations
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The allocation engine checks for host reservations after selecting a subnet. The following diagram shows the details of that operation. Subnet selection is based on network topology. Host reservations are primarily for assigning options, and options are evaluated after subnet selection. However, if client classes are added in the host reservation, those are also evaluated against the selected subnet in a further check. Kea includes several options to skip checking for host reservations, which can make this process much more efficient if reservations are not being used.
+
+.. note::
+
+ To find a free lease, the allocation engine begins by evaluating the most recently used subnet.
+ The current subnet depends on the history of prior queries.
+
+.. figure:: uml/currentHost4.*
+
+ Host reservation evaluation
+
+.. _uml_CfgOptionList:
+
+Building the Options List
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Before sending a response, options are added:
+ - evaluate required client classes
+ - build the configured option list
+ - append requested options
+ - append requested vendor options
+ - append basic options
+
+.. figure:: uml/buildCfgOptionList.*
+
+ The buildCfgOptionList (build configured option list) algorithm
+
+.. figure:: uml/appendRequestedOptions.*
+
+ The appendRequestedOptions (append requested options) algorithm
+
+.. figure:: uml/appendRequestedVendorOptions.*
+
+ The appendRequestedVendorOptions (append vendor requested options) algorithm
+
+
+.. _uml-recognizing-same-client:
+
+How Kea Recognizes the Same Client In Different DHCP Messages
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. figure:: uml/recognizing-same-client.*
+
+RADIUS workflows for lease allocation
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. figure:: uml/radius.*