summaryrefslogtreecommitdiffstats
path: root/doc
diff options
context:
space:
mode:
Diffstat (limited to 'doc')
-rw-r--r--doc/.gitignore1
-rw-r--r--doc/ChangeLog189
-rw-r--r--doc/Makefile.sphinx95
-rw-r--r--doc/README181
-rw-r--r--doc/all.mk55
-rw-r--r--doc/antora/antora.yml18
-rw-r--r--doc/antora/modules/ROOT/assets/images/favicon.pngbin0 -> 4105 bytes
-rw-r--r--doc/antora/modules/ROOT/assets/images/favicon.svg104
-rw-r--r--doc/antora/modules/ROOT/assets/images/networkradius.pngbin0 -> 7126 bytes
-rw-r--r--doc/antora/modules/ROOT/nav.adoc1
-rw-r--r--doc/antora/modules/ROOT/pages/directories.adoc69
-rw-r--r--doc/antora/modules/ROOT/pages/index.adoc137
-rw-r--r--doc/antora/modules/howto/nav.adoc19
-rw-r--r--doc/antora/modules/howto/pages/index.adoc17
-rw-r--r--doc/antora/modules/howto/pages/protocols/dhcp/enable.adoc213
-rw-r--r--doc/antora/modules/howto/pages/protocols/dhcp/index.adoc35
-rw-r--r--doc/antora/modules/howto/pages/protocols/dhcp/policy.adoc14
-rw-r--r--doc/antora/modules/howto/pages/protocols/dhcp/policy_common_options.adoc80
-rw-r--r--doc/antora/modules/howto/pages/protocols/dhcp/policy_device_options.adoc310
-rw-r--r--doc/antora/modules/howto/pages/protocols/dhcp/policy_ippool_access.adoc54
-rw-r--r--doc/antora/modules/howto/pages/protocols/dhcp/policy_ippool_creation.adoc112
-rw-r--r--doc/antora/modules/howto/pages/protocols/dhcp/policy_network_options.adoc237
-rw-r--r--doc/antora/modules/howto/pages/protocols/dhcp/policy_subnet_options.adoc184
-rw-r--r--doc/antora/modules/howto/pages/protocols/dhcp/prepare.adoc59
-rw-r--r--doc/antora/modules/howto/pages/protocols/dhcp/test.adoc143
-rw-r--r--doc/antora/modules/howto/pages/protocols/proxy/enable_proxy_protocol.adoc114
-rw-r--r--doc/antora/modules/howto/pages/protocols/proxy/enable_radsec.adoc188
-rw-r--r--doc/antora/modules/howto/pages/protocols/proxy/index.adoc126
-rw-r--r--doc/antora/modules/howto/pages/protocols/proxy/radsec_client.adoc181
-rw-r--r--doc/antora/modules/howto/pages/protocols/proxy/radsec_with_haproxy.adoc134
-rw-r--r--doc/antora/modules/howto/pages/protocols/proxy/radsec_with_traefik.adoc128
-rw-r--r--doc/antora/modules/installation/nav.adoc5
-rw-r--r--doc/antora/modules/installation/pages/dependencies.adoc58
-rw-r--r--doc/antora/modules/installation/pages/index.adoc15
-rw-r--r--doc/antora/modules/installation/pages/packages.adoc22
-rw-r--r--doc/antora/modules/installation/pages/source.adoc199
-rw-r--r--doc/antora/modules/installation/pages/upgrade.adoc737
-rw-r--r--doc/antora/modules/unlang/.gitignore1
-rw-r--r--doc/antora/modules/unlang/nav.adoc51
-rw-r--r--doc/antora/modules/unlang/pages/attr.adoc77
-rw-r--r--doc/antora/modules/unlang/pages/break.adoc28
-rw-r--r--doc/antora/modules/unlang/pages/case.adoc44
-rw-r--r--doc/antora/modules/unlang/pages/condition/and.adoc21
-rw-r--r--doc/antora/modules/unlang/pages/condition/cmp.adoc104
-rw-r--r--doc/antora/modules/unlang/pages/condition/eq.adoc30
-rw-r--r--doc/antora/modules/unlang/pages/condition/index.adoc85
-rw-r--r--doc/antora/modules/unlang/pages/condition/not.adoc19
-rw-r--r--doc/antora/modules/unlang/pages/condition/operands.adoc37
-rw-r--r--doc/antora/modules/unlang/pages/condition/or.adoc21
-rw-r--r--doc/antora/modules/unlang/pages/condition/para.adoc19
-rw-r--r--doc/antora/modules/unlang/pages/condition/regex.adoc180
-rw-r--r--doc/antora/modules/unlang/pages/condition/return_codes.adoc35
-rw-r--r--doc/antora/modules/unlang/pages/default.adoc47
-rw-r--r--doc/antora/modules/unlang/pages/else.adoc30
-rw-r--r--doc/antora/modules/unlang/pages/elsif.adoc43
-rw-r--r--doc/antora/modules/unlang/pages/foreach.adoc40
-rw-r--r--doc/antora/modules/unlang/pages/group.adoc39
-rw-r--r--doc/antora/modules/unlang/pages/if.adoc29
-rw-r--r--doc/antora/modules/unlang/pages/index.adoc162
-rw-r--r--doc/antora/modules/unlang/pages/keywords.adoc78
-rw-r--r--doc/antora/modules/unlang/pages/list.adoc72
-rw-r--r--doc/antora/modules/unlang/pages/load-balance.adoc32
-rw-r--r--doc/antora/modules/unlang/pages/module.adoc86
-rw-r--r--doc/antora/modules/unlang/pages/module_builtin.adoc42
-rw-r--r--doc/antora/modules/unlang/pages/module_method.adoc27
-rw-r--r--doc/antora/modules/unlang/pages/redundant-load-balance.adoc39
-rw-r--r--doc/antora/modules/unlang/pages/redundant.adoc42
-rw-r--r--doc/antora/modules/unlang/pages/return.adoc36
-rw-r--r--doc/antora/modules/unlang/pages/return_codes.adoc17
-rw-r--r--doc/antora/modules/unlang/pages/switch.adoc83
-rw-r--r--doc/antora/modules/unlang/pages/type/all_types.adoc80
-rw-r--r--doc/antora/modules/unlang/pages/type/double.adoc39
-rw-r--r--doc/antora/modules/unlang/pages/type/index.adoc117
-rw-r--r--doc/antora/modules/unlang/pages/type/ip.adoc15
-rw-r--r--doc/antora/modules/unlang/pages/type/numb.adoc11
-rw-r--r--doc/antora/modules/unlang/pages/type/string/backticks.adoc38
-rw-r--r--doc/antora/modules/unlang/pages/type/string/double.adoc68
-rw-r--r--doc/antora/modules/unlang/pages/type/string/escaping.adoc14
-rw-r--r--doc/antora/modules/unlang/pages/type/string/single.adoc19
-rw-r--r--doc/antora/modules/unlang/pages/type/string/unquoted.adoc21
-rw-r--r--doc/antora/modules/unlang/pages/update.adoc160
-rw-r--r--doc/antora/modules/unlang/pages/xlat/alternation.adoc24
-rw-r--r--doc/antora/modules/unlang/pages/xlat/attribute.adoc54
-rw-r--r--doc/antora/modules/unlang/pages/xlat/builtin.adoc891
-rw-r--r--doc/antora/modules/unlang/pages/xlat/character.adoc80
-rw-r--r--doc/antora/modules/unlang/pages/xlat/index.adoc56
-rw-r--r--doc/antora/modules/unlang/pages/xlat/module.adoc18
-rw-r--r--doc/antora/modules/unlang/partials/rcode_table.adoc39
-rw-r--r--doc/bugs175
-rw-r--r--doc/concepts/aaa.rst99
-rw-r--r--doc/concepts/proxy.rst118
-rw-r--r--doc/configuration/acct_type.rst71
-rw-r--r--doc/configuration/autz_type.rst88
-rw-r--r--doc/configuration/configurable_failover.rst476
-rw-r--r--doc/configuration/dynamic_home_servers.md238
-rw-r--r--doc/configuration/load_balance.rst172
-rw-r--r--doc/configuration/post_auth_type44
-rw-r--r--doc/configuration/session_type10
-rw-r--r--doc/configuration/simultaneous_use173
-rw-r--r--doc/configuration/snmp58
-rw-r--r--doc/configuration/variables.rst151
-rw-r--r--doc/deployment/CYGWIN.rst283
-rw-r--r--doc/deployment/MACOSX12
-rw-r--r--doc/deployment/OS222
-rw-r--r--doc/deployment/performance-testing168
-rw-r--r--doc/deployment/supervise-radiusd.rst163
-rw-r--r--doc/deployment/tuning_guide58
-rw-r--r--doc/developer/autotools.md179
-rw-r--r--doc/developer/coding-methods.rst242
-rw-r--r--doc/developer/contributing.rst127
-rw-r--r--doc/developer/module_interface.rst186
-rw-r--r--doc/developer/release-method.rst42
-rw-r--r--doc/index.rst29
-rw-r--r--doc/modules/RADIUS-LDAP-eDirectory18
-rw-r--r--doc/modules/ldap_howto.rst1648
-rw-r--r--doc/modules/mschap.rst196
-rw-r--r--doc/modules/rlm_dbm195
-rw-r--r--doc/modules/rlm_eap395
-rw-r--r--doc/modules/rlm_expiration23
-rw-r--r--doc/modules/rlm_krb547
-rw-r--r--doc/modules/rlm_pam108
-rw-r--r--doc/modules/rlm_passwd50
-rw-r--r--doc/modules/rlm_python76
-rw-r--r--doc/modules/rlm_soh183
-rw-r--r--doc/modules/rlm_sql283
-rw-r--r--doc/modules/rlm_sqlcounter182
-rw-r--r--doc/modules/rlm_sqlippool40
-rw-r--r--doc/schemas/ldap/edir/freeradius-clients.ldif69
-rw-r--r--doc/schemas/ldap/iplanet/freeradius.ldif73
-rw-r--r--doc/schemas/ldap/iplanet/freeradius.schema78
-rw-r--r--doc/schemas/ldap/openldap/freeradius-clients.ldif17
-rw-r--r--doc/schemas/ldap/openldap/freeradius-clients.schema70
-rw-r--r--doc/schemas/ldap/openldap/freeradius.ldif76
-rw-r--r--doc/schemas/ldap/openldap/freeradius.schema477
-rw-r--r--doc/schemas/ldap/samba/README.txt11
-rw-r--r--doc/schemas/ldap/samba/freeradius-attrs.ldif806
-rw-r--r--doc/schemas/ldap/samba/freeradius-classes.ldif91
-rw-r--r--doc/schemas/ldap/samba/freeradius-clients-attrs.ldif91
-rw-r--r--doc/schemas/ldap/samba/freeradius-clients-classes.ldif19
-rw-r--r--doc/schemas/ldap/samba/freeradius-user.ldif10
-rw-r--r--doc/schemas/logstash/README95
-rw-r--r--doc/schemas/logstash/kibana4-dashboard.json123
-rw-r--r--doc/schemas/logstash/log-courier.conf56
-rw-r--r--doc/schemas/logstash/logstash-radius.conf256
-rwxr-xr-xdoc/schemas/logstash/radius-mapping.sh100
-rw-r--r--doc/schemas/sql8
-rw-r--r--doc/source/.gitignore4
-rw-r--r--doc/source/Doxyfile2313
-rw-r--r--doc/source/extra/client.c12
-rw-r--r--doc/source/extra/core.c31
-rw-r--r--doc/source/extra/freeradius.pngbin0 -> 14616 bytes
-rw-r--r--doc/source/extra/module.c17
-rw-r--r--doc/source/extra/toc.c102
-rw-r--r--doc/vendor/ascend57
-rw-r--r--doc/vendor/bay11
-rw-r--r--doc/vendor/cisco.rst168
-rw-r--r--doc/vendor/proxim12
157 files changed, 19755 insertions, 0 deletions
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 0000000..e35d885
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1 @@
+_build
diff --git a/doc/ChangeLog b/doc/ChangeLog
new file mode 100644
index 0000000..6b7006e
--- /dev/null
+++ b/doc/ChangeLog
@@ -0,0 +1,189 @@
+FreeRADIUS 3.2.3 Fri 26 May 2023 12:00:00 EDT urgency=low
+ Configuration changes
+ * The rlm_ldap and rlm_sql modules now have a "max_retries" configuration
+ item in the pool section. This sets a limit on how many times an operation
+ will be retried if it fails indicating a connection issue.
+ * Added "check_crl" configuration to rlm_ldap. This only works with OpenSSL.
+ Many Linux distributions use other TLS libraries, which won't work.
+ * Note that rlm_ldap does not support "-=" operators. The documentation
+ disagreed with the code, so we fixed the documentation.
+ * If checkrad is called from SQL Simultaneous-Use checks it will now be
+ passed NAS-Port-Id (as stored in the database), rather than NAS-Port.
+
+ Feature improvements
+ * Add "max_retries" for connection pools. Fixes #4908. Patch from Nick Porter.
+ * Update dictionary.ciena, dictionary.huawei, dictionary.wifialliance and
+ dictionary.wispr; add dictionary.eleven.
+ * You can now list "eap" in the "pre-proxy" section. If the packet
+ contains a malformed EAP message, then the request will be rejected.
+ The home server will either reject (or discard) this packet anyways,
+ so this change can only help with large proxy scenarios.
+ * Show warnings if libldap is not using OpenSSL.
+ * Support RADIUS/1.1. See https://datatracker.ietf.org/doc/draft-dekok-radext-radiusv11/
+ Disabled by default, can be enabled by passing `--with-radiusv11` to the
+ configure script. For now, this is for testing interoperability.
+ * Add extra sanity checks for malformed EAP attributes.
+ * More TLS debugging output
+ * Clear old module instance data before HUP reload. Avoids burst memory use
+ when e.g. using large data files with rlm_files. Patch from Nick Porter.
+ * `rlm_cache_redis` is now included in the freeradius-redis packages.
+ * Separate out python2/python3 in Debian Packages. Previously python 2 or 3
+ was built depending on the system default which led to confusion. We now build
+ both freeradius-python2 and freeradius-python3 packages where possible.
+
+ Bug fixes
+ * Don't leak MD contexts with OpenSSL 3.0.
+ * Increase internal buffer size for TLS connections, which
+ can help with high-load proxies.
+ * Send Status-Server checks for TLS connections
+ * Give descriptive error if "update CoA" is used with "fake" packets,
+ as it won't work. i.e. inner-tunnel and virtual home servers.
+ * Many small ASAN / LSAN fixes from Jorge Pereira.
+ * Close inbound RADIUS/TLS socket on TLS errors. When a home server
+ sees a TLS error, it will now close the socket, so proxies do not
+ have an open (but dead) TLS connection.
+ * Fix mutex locking issues on inbound RADIUS/TLS connections.
+ This change avoids random issues with "bad record mac".
+ * Improve REST encoding loop. Patch from Herwin Weststrate. Closes #4950
+ * Correctly report the LDAP group a user was found in. Fixes #3084.
+ Patch from Nick Porter.
+ * Force correct packet type when running Post-Auth-Type. Helps with #4980
+ * Fix small leak in Client-Lost code. Patch from Terry Burton. PR #4996
+ * Fix TCP socket statistics. Closes #4990
+ * Use NAS-Port-Id instead of NAS-Port during SQL simultaneous-use
+ checks. Helps with #5010
+
+FreeRADIUS 3.2.2 Thu 16 Feb 2023 12:00:00 EDT urgency=low
+ Configuration changes
+ * The linelog module now has a "header" configuration item,
+ which places a header in any new file it creates.
+ * The ldap module now supports setting "cipher_list". See
+ mods-available/ldap.
+ * Add "connect_timeout" for outgoing TLS sockets. Helps with #3501.
+ * Add config section for xlats in rlm_rest and an option to
+ control REST body data encoding. Patches by Nick Porter.
+ * Allow Operator-Name and Called-Station-Id in attr_filter when
+ proxying. Helps with less work in Eduroam configurations.
+ * Ensure that the AcctUpdateTime field in SQL is always updated.
+ This is so that we can track when the last packet arrived.
+ * Update the default configuration to reply to NAS when accounting
+ proxying fails, but we still write to the detail file.
+
+ Feature improvements
+ * The "configure" process now gives a much clearer report
+ when it's finished. Patches by Matthew Newton.
+ * Fallback to "uname -n" on missing "hostname". Fixes #4771
+ * Export thread details in radmin "stats threads". Fixes #4770
+ * Improve queries for processing radacct into periodic usage data.
+ Fix from Nick Porter.
+ * Update dictionary.juniper
+ * Add dictionary.calix
+ * Fix dictionary.rfc6519 DS-Lite-Tunnel-Name to be "octets"
+ * Update documentation for robust-proxy-accounting, and be more
+ aggressive about sending packets.
+ * Add per-module README.md files in the source.
+ * Add default Visual Studio configuration for developers.
+ * Postgres can now automatically use alternate queries for errors
+ other than duplicate keys.
+ * %{listen:TLS-PSK-Identity} is now set when using PSK and psk_query
+ This helps the server track the identity of the client which is
+ connecting.
+ * Include thread stats in Status-Server attributes. Fixes #4870.
+ * Mark rlm_unbound stable and add to packages. Patches by Nick Porter.
+ * Remove broken/unsupported Dockerfiles for centos8 and
+ debian9.
+ * Ensure Docker containers have stable uid/gid. Patches
+ from Terry Burton.
+
+ Bug fixes
+ * Preliminary support for non-blocking TLS sockets. Helps with #3501.
+ * Fix support for partial certificate chains after adding reload
+ support. Fixes #4753
+ * Fix handling of debug_condition.
+ * Clean up home server states, and re-sync with the dictionaries.
+ * Correct certificate order when creating TLS-* attributes.
+ Fixes #4785
+ * Update use of isalpha() etc. so broken configurations have less
+ impact on the server.
+ * Outgoing TLS sockets now set SNI correctly from the "hostname"
+ configuration item.
+ * Support Apple Homebrew on the M1. Fixes #4754
+ * Better error messages when %{listen:TLS-...} is used.
+ * Getting statistics via Status-Server can now be done within a
+ virtual server. Fixes #4868
+ * Make TTLS+MS-CHAP work with TLS 1.3. Fixes #4878.
+ * Fix md5 xlat memory leak when using OpenSSL 3. Fix by Terry Burton.
+
+FreeRADIUS 3.2.1 Mon 03 Oct 2022 12:00:00 EDT urgency=low
+ Feature improvements
+ * Add dictionary.ciena, dictionary.nile, and DHCPv4 dictionaries.
+ * Add simultaneous-use queries for MS SQL.
+ * Add radmin command for "stats pool <module-name>"
+ Which prints out statistics about the connection pools
+ * Client statistics now shows "conflicts", to count conflicting
+ packets.
+ * New optional "lightweight accounting-on/off" strategy. When
+ refreshing queries.conf you should also add the new nasreload table
+ and corresponding GRANTs to your DB schema.
+ * Add TLS-Client-Cert-X509v3-Certificate-Policies, which helps with
+ Eduroam. Suggested by Stefan Winter.
+ * Allow auth+acct for TCP sockets, too.
+ * Add rlm_cache_redis. See raddb/mods-available/cache for details
+ * Allow radmin to look up home servers by name, too.
+ * Ensure that dynamic clients don't create loops on duplicates.
+ Reported by Sam Yee.
+ * Removed rlm_sqlhpwippool. There was no documentation, no configuration,
+ and the module was ~15 years old with no one using it.
+ * Marked rlm_python3 as stable.
+ * Add sigalgs_list. See raddb/mods-available/eap. Patch from
+ Boris Lytochkin.
+ * For rlm_linelog, when opening files in /dev, look at "permissions" to see
+ whether to open them r/w.
+ * More flexibility for dynamic home servers. See doc/configuration/dynamic_home_servers.md
+ and raddb/home_servers/README.md
+ * Allow setting of application_name for PostgreSQL. See mods-available/sql.
+
+ Bug fixes
+ * Correct test for open sessions in radacct for MS SQL.
+ * The linelog module now opens /dev/stdout in "write-only" mode
+ if the permissions are set to "u+w" (0002).
+ * Various fixes to rlm_unbound from Nick Porter.
+ * PEAP now correctly runs Post-Auth-Type Accept
+ * Create "TLS-Cert-*" for outbound Radsec, instead of TLS-Client-Cert-*
+ Fixes #4698. See sites-available/tls, and fix_cert_order.
+ * Minor updates and fixes to CI, Dockerfiles and packaging.
+ * Fix rlm_python3 build with python >= 3.10. Fixes #4441
+
+FreeRADIUS 3.2.0 Thu 21 Apr 2022 12:00:00 EDT urgency=low
+ Configuration changes
+ * "correct_escapes" has been removed, and is always set to "true"
+ internally. Configuration changes may be required if you are
+ using configurations from before 3.0.5. Other than this
+ difference, 3.2.x is compatible with 3.0.x, and configurations
+ from 3.0.x can be simply copied into a system running 3.2.x.
+
+ Feature improvements
+ * All features from 3.0.x are included in the 3.2.x releases. In addition:
+ * Support PEAP and TTLS with TLS 1.3. This has been
+ tested with wpa_supplicant and Windows 11.
+ * Add 'reset_day' and '%%r' parameter for rlm_sqlcounter to specify which
+ day of the month the counter should be reset.
+ * Partial backport of rlm_json from v4, providing the json_encode xlat.
+ See mods-available/json for documentation.
+ * Support for haproxy "PROXY" protocol.
+ See sites-available/tls, "proxy_protocol" and doc/antora/modules/howto/pages/protocols/proxy/
+ * Support for sending CoA-Request and Disconnect-Request packets
+ in "reverse" down RadSec tunnels. Experimental for now, and
+ undocumented.
+ * It is now possible to run a virtual server when saving / loading
+ TLS cache attributes. See sites-available/tls-cache for
+ more information.
+ * Removed the "cram" module. It was undocumented, and used old
+ and insecure authentication methods.
+ * Remove the "otp" module. The "otpd" program it needs is no longer available,
+ and the module has not been usable since at least 2015.
+ * All features from 3.0.x are included in the 3.2.x releases.
+ * 3.2.0 requires OpenSSL 1.0.2 or greater.
+
+ Bug fixes
+ * All bug fixes from 3.0.x are included in the 3.2.x releases.
diff --git a/doc/Makefile.sphinx b/doc/Makefile.sphinx
new file mode 100644
index 0000000..235ba9d
--- /dev/null
+++ b/doc/Makefile.sphinx
@@ -0,0 +1,95 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " pdf to make standalone PDF files"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+pdf:
+ $(SPHINXBUILD) -b pdf $(ALLSPHINXOPTS) _build/pdf
+ @echo
+ @echo "Build finished. The PDFs are in _build/pdf."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/FreeRADIUS.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/FreeRADIUS.qhc"
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+ "run these through (pdf)latex."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/doc/README b/doc/README
new file mode 100644
index 0000000..7801c90
--- /dev/null
+++ b/doc/README
@@ -0,0 +1,181 @@
+1. INTRO
+
+ The FreeRADIUS Server Project is a high performance and highly
+ configurable multi-protocol policy server, supporting RADIUS, DHCPv4
+ and VMPS. It is available under the terms of the GNU GPLv2.
+
+ All code in this server was written for this project.
+
+
+2. INSTALLATION
+
+ See the INSTALL file, in the parent directory.
+
+
+3. CONFIGURATION FILES
+
+ Much of the server documentation is included only in the comments in the
+ configuration files. Reading the configuration files is REQUIRED to fully
+ understand how to create complex configurations of the server.
+
+3a. 'clients.conf'
+
+ Make sure the clients (NAS, switches, access points etc) are set up to
+ use the host radiusd is running on as authentication and accounting host.
+ Configure these clients with a "radius secret", which should also be
+ entered into the client definition in /etc/raddb/clients.conf.
+ See also the manual page for clients.conf(5).
+
+3b. 'users'
+
+ Users may be defined in the "users" file (raddb/mods-config/files/authorize).
+ All entries are processed in the order as they appear in the file.
+ If an entry matches the username, radiusd will stop scanning the users
+ file (unless the attribute "Fall-Through = Yes" is set).
+
+ You can uses spaces in usernames by escaping them with \ or by using
+ quotes. For example, "joe user" or joe\ user.
+
+ The 'users' file is read by the "rlm_files" module.
+
+3c. NEW RADIUS ATTRIBUTES (to be used in the USERS file).
+
+ Name Type Descr.
+ ---- ---- ------
+ Simultaneous-Use integer Max. number of concurrent logins
+ Fall-Through integer Yes/No
+ Login-Time string Defines when user may login.
+ Current-Time string Allows you to perform time-based
+ checks when a request is received.
+
+ Login-Time defines the time span a user may login to the system. The
+ format of a so-called time string is like the format used by UUCP.
+ A time string may be a list of simple time strings separated by "|" or ",".
+
+ Each simple time string must begin with a day definition. That can be just
+ one day, multiple days, or a range of days separated by a hyphen. A
+ day is Mo, Tu, We, Th, Fr, Sa or Su, or Wk for Mo-Fr. "Any" or "Al"
+ means all days.
+
+ After that a range of hours follows in hhmm-hhmm format.
+
+ For example, "Wk2305-0855,Sa,Su2305-1655".
+
+ radiusd calculates the number of seconds left in the time span, and
+ sets the Session-Timeout to that number of seconds. So if someones
+ Login-Time is "Al0800-1800" and she logs in at 17:30, Session-Timeout
+ is set to 1800 seconds so that she is kicked off at 18:00.
+
+
+4. LOG FILES
+
+4a. /var/log/radius/radutmp
+
+ In this file the currently logged in users are held. The program "radwho"
+ reads this file and gives you a summary. Rogue sessions can be deleted
+ from this file with the "radzap" program.
+
+4b. /var/log/radius/radwtmp
+
+ This file is "wtmp" compatible and keeps a history of all radius logins/
+ logouts. This file can be read with the "last" program, and other Unix
+ accounting programs (such as "ac" and "sac") can be used to produce a
+ summary.
+
+4c. /var/log/radius/radius.log
+
+ All RADIUS informational, diagnostic and error messages are logged in
+ this file, including all login attempts.
+
+4d. /var/log/radius/radacct/<client_ip>/detail
+
+ This is the original radius logfile, as written by all the Livingston
+ radius servers. It's only created if the directory
+ /var/log/radius/radacct exists.
+
+ For more configuration options on the detail file please see
+ raddb/mods-available/detail as it expands upon this greatly.
+
+
+5. MORE INFO, SUPPORT
+
+ The latest version of FreeRADIUS is always available from
+ the git repository hosted on GitHub at
+
+ https://github.com/FreeRADIUS/freeradius-server
+
+ or see
+
+ http://freeradius.org/git/
+
+ for more information.
+
+ There are two mailing lists for users and developers. General
+ user, administrator and configuration issues should be discussed
+ on the users list at:
+
+ http://lists.freeradius.org/mailman/listinfo/freeradius-users
+
+ When asking for help on the users list, be sure the include a
+ detailed and clear description of the problem, together with
+ full debug output from FreeRADIUS, obtained by running
+
+ radiusd -X
+
+ Developers only discussion is to be had on the devel list:
+
+ http://lists.freeradius.org/mailman/listinfo/freeradius-devel
+
+ Please do not raise general configuration issues here.
+
+
+6. OTHER INFORMATION
+
+ The files in other directories are:
+
+ debian/ Files to build Debian Linux packages.
+
+ doc/ Various snippets of documentation
+ doc/rfc/ Copies of the RFC's. If you have Perl, do a 'make' in
+ that directory, and look at the HTML output.
+
+ man/ Unix Manual pages for the server, configuration files,
+ and associated utilities.
+
+ mibs/ SNMP Mibs for the server.
+
+ raddb/ Default configuration files for the server.
+
+ redhat/ Files to build RedHat RPM packages.
+
+ scripts/ Sample scripts for startup and maintenance.
+
+ share/ Attribute dictionaries.
+
+ src/ Source code
+ src/main source code for the daemon and associated utilities
+ src/lib source code for the RADIUS library
+ src/include header files
+ src/modules dynamic plug-in modules
+ src/tests test harness used by "make test"
+
+ suse/ Files to build SuSE RPM packages.
+
+
+ If you have ANY problems, concerns, or surprises when running
+ the server, then run it in debugging mode, as root, from the
+ command line:
+
+ # radiusd -X
+
+ It will produce a large number of messages. The answers to many
+ questions, and the solution to many problems, can usually be found in
+ these messages.
+
+ For further details, see:
+
+ https://freeradius.org/documentation/
+
+ and the 'bugs' file, in this directory.
+
+$Date$
diff --git a/doc/all.mk b/doc/all.mk
new file mode 100644
index 0000000..1a88f52
--- /dev/null
+++ b/doc/all.mk
@@ -0,0 +1,55 @@
+ifneq "$(docdir)" "no"
+install: install.doc
+
+clean: clean.doc
+
+DOCDIRS := $(patsubst doc/%,$(R)$(docdir)/%,$(filter-out doc/source%,$(shell find doc -type d)))
+DOCFILES := $(filter-out %~ %/all.mk %.gitignore doc/rfc/update.sh doc/source/%,$(shell find doc -type f))
+DOCINSTALL := $(patsubst doc/%,$(R)$(docdir)/%,$(DOCFILES))
+
+# Create the directories
+$(DOCDIRS):
+ @echo INSTALL $(patsubst $(R)$(docdir)/%,doc/%,$@)
+ @$(INSTALL) -d -m 755 $@
+
+# Files depend on directories (order only).
+# We don't care if the directories change.
+$(DOCINSTALL): | $(DOCDIRS)
+
+# Wildcard installation rule
+$(R)$(docdir)/%: doc/% | $(dir $@)
+ @echo INSTALL $<
+ @$(INSTALL) -m 644 $< $@
+
+install.doc: $(DOCINSTALL)
+
+.PHONY: clean.doc
+clean.doc:
+ @rm -rf doc/*~ doc/rfc/*~ build/docsite
+
+#
+# Deal with these later
+#
+DOCRST := $(wildcard *.rst)
+%.html: %.rst
+ @rst2html.py $^ > $@
+
+.PHONY: html
+html: $(DOCRST:.rst=.html)
+
+#
+# antora rebuilds the entire documentation site on each run
+# so we need to pick a single file to compare dependency
+# timestamps against.
+#
+# we use sitemap.xml as it'll be regenerated on every antora
+# run.
+#
+build/docsite/sitemap.xml: $(ADOC_FILES)
+ @echo ANTORA site.yml
+ ${Q}$(ANTORA) $(ANTORA_FLAGS) site.yml
+
+
+docsite: build/docsite/sitemap.xml
+
+endif
diff --git a/doc/antora/antora.yml b/doc/antora/antora.yml
new file mode 100644
index 0000000..e345e9c
--- /dev/null
+++ b/doc/antora/antora.yml
@@ -0,0 +1,18 @@
+#
+# Metadata for the freeradius-server component
+# Examples of other components are the PAM module,
+# apache module, and the client library.
+#
+name: freeradius-server
+title: The FreeRADIUS Server
+version: '3.2.3'
+start_page: ROOT:index.adoc
+nav:
+- modules/ROOT/nav.adoc
+- modules/installation/nav.adoc
+- modules/concepts/nav.adoc
+- modules/howto/nav.adoc
+- modules/tutorials/nav.adoc
+- modules/unlang/nav.adoc
+- modules/developers/nav.adoc
+- modules/raddb/nav.adoc
diff --git a/doc/antora/modules/ROOT/assets/images/favicon.png b/doc/antora/modules/ROOT/assets/images/favicon.png
new file mode 100644
index 0000000..8c71104
--- /dev/null
+++ b/doc/antora/modules/ROOT/assets/images/favicon.png
Binary files differ
diff --git a/doc/antora/modules/ROOT/assets/images/favicon.svg b/doc/antora/modules/ROOT/assets/images/favicon.svg
new file mode 100644
index 0000000..7476355
--- /dev/null
+++ b/doc/antora/modules/ROOT/assets/images/favicon.svg
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 160 160" style="enable-background:new 0 0 160 160;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:none;stroke:#FFD62B;stroke-width:3;stroke-miterlimit:10;}
+ .st1{fill:#B0C3C6;}
+ .st2{fill:none;stroke:#DEE7E8;stroke-miterlimit:10;}
+ .st3{fill:#00A6E2;}
+ .st4{fill:none;stroke:#B0C3C6;stroke-width:2;stroke-miterlimit:10;}
+ .st5{fill:#666666;}
+ .st6{fill:none;stroke:#B0C3C6;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:10;}
+ .st7{fill:#00C9ED;}
+ .st8{fill:#FFFFFF;stroke:#B0C3C6;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:10;}
+ .st9{fill:#303030;}
+ .st10{opacity:0.4;fill:#F6F6F6;}
+ .st11{fill:none;stroke:#999999;stroke-width:2;stroke-miterlimit:10;}
+ .st12{fill:#999999;}
+ .st13{fill:#FFFFFF;}
+ .st14{clip-path:url(#SVGID_2_);fill:#2F3537;}
+ .st15{opacity:0.3;fill:none;stroke:#9FB1B3;stroke-width:2;stroke-miterlimit:10;}
+ .st16{fill:none;stroke:#FFFFFF;stroke-miterlimit:10;}
+ .st17{fill:#FFFFFF;stroke:#FFFFFF;stroke-miterlimit:10;}
+ .st18{fill:none;stroke:#303030;stroke-miterlimit:10;}
+ .st19{opacity:0.8;fill:#B0C3C6;}
+ .st20{opacity:0.7;}
+ .st21{opacity:0.8;clip-path:url(#SVGID_4_);fill:#00A6E2;}
+ .st22{opacity:0.8;fill:#00A6E2;}
+ .st23{opacity:0.8;clip-path:url(#SVGID_6_);fill:#00A6E2;}
+ .st24{clip-path:url(#SVGID_8_);}
+ .st25{clip-path:url(#SVGID_10_);}
+ .st26{fill:none;stroke:#B0C3C6;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:10;}
+ .st27{opacity:0.3;fill:none;stroke:#9FB1B3;stroke-width:3;stroke-miterlimit:10;}
+ .st28{fill:#FFFFFF;stroke:#B0C3C6;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:10;}
+ .st29{clip-path:url(#SVGID_12_);fill:#2F3537;}
+ .st30{clip-path:url(#SVGID_14_);fill:#B0C3C6;}
+ .st31{fill:#33B8E8;}
+ .st32{fill:#238DB4;}
+ .st33{fill:#E2E7E8;}
+ .st34{clip-path:url(#SVGID_16_);}
+ .st35{fill:#FFFFFF;stroke:#B0C3C6;stroke-width:2;stroke-miterlimit:10;}
+ .st36{fill:#B4CBCE;}
+ .st37{fill:#003147;}
+ .st38{fill:#FFD62B;}
+ .st39{fill:#00B78E;}
+ .st40{fill:#FF7824;}
+ .st41{fill:#FF3223;}
+ .st42{fill:#7955DF;}
+ .st43{fill:none;stroke:#FF3223;stroke-width:2;stroke-miterlimit:10;}
+ .st44{fill:none;stroke:#00A6E2;stroke-width:2;stroke-miterlimit:10;}
+ .st45{clip-path:url(#SVGID_18_);fill:#303030;}
+ .st46{fill:#F5C81F;}
+ .st47{fill:#F49F90;}
+ .st48{fill:#F3EEDE;}
+ .st49{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
+ .st50{fill:#00131F;}
+ .st51{clip-path:url(#SVGID_20_);fill:#303030;}
+ .st52{fill:none;stroke:#FFFFFF;stroke-width:2;stroke-miterlimit:10;}
+ .st53{clip-path:url(#SVGID_22_);fill:#303030;}
+ .st54{clip-path:url(#SVGID_24_);fill:#303030;}
+</style>
+<path class="st0" d="M72.3,25.6c0,0-2.3-3.2-6-2.3"/>
+<g>
+ <polygon class="st50" points="79.2,73 20.3,37.9 86.2,19.3 145.1,54.4 "/>
+ <g>
+ <g>
+ <g>
+ <g>
+ <path class="st3" d="M126.2,113.6c-4.4,0-8.2-3-9.3-7.1c2.8,1,5.7,1.6,8.8,1.6c4.4,0,7.9-3.5,7.9-7.9c0-4.4-3.5-7.9-7.9-7.9
+ c-5.3,0-9.9-4.3-9.9-9.6c0-0.7,0-2.1,0-2.1v-6.1c0-19.4-15.7-35.2-35.2-35.2c-19.4,0-35.2,15.7-35.2,35.2v6.1c0,0,0,1.3,0,2.1
+ c0,5.3-4.6,9.6-9.9,9.6c-4.4,0-7.9,3.5-7.9,7.9c0,4.4,3.5,7.9,7.9,7.9c3.5,0,6.8-0.7,9.8-2c-1,4.3-4.8,7.5-9.4,7.5
+ c-4.4,0-7.9,3.5-7.9,7.9c0,4.4,3.5,7.9,7.9,7.9c6.9,0,13.2-2.8,17.8-7.3v10.4c0,4.4,3.5,7.9,7.9,7.9c4.4,0,7.9-3.5,7.9-7.9
+ v-14.8h3.1v18.7c0,4.4,3.5,7.9,7.9,7.9c4.4,0,7.9-3.5,7.9-7.9v-18.7h3.1v14.8c0,4.4,3.5,7.9,7.9,7.9c4.4,0,7.9-3.5,7.9-7.9
+ v-11.4c4.7,5.1,11.3,8.2,18.7,8.2c4.4,0,7.9-3.5,7.9-7.9C134.1,117.1,130.5,113.6,126.2,113.6z"/>
+ <g>
+ <circle class="st13" cx="80" cy="76.9" r="17.5"/>
+ <g>
+ <defs>
+ <circle id="SVGID_19_" cx="80" cy="76.9" r="17.5"/>
+ </defs>
+ <use xlink:href="#SVGID_19_" style="overflow:visible;fill:#FFFFFF;"/>
+ <clipPath id="SVGID_2_">
+ <use xlink:href="#SVGID_19_" style="overflow:visible;"/>
+ </clipPath>
+ <path style="clip-path:url(#SVGID_2_);fill:#303030;" d="M73.3,85.5c-6.7,0-12.2-5.5-12.2-12.2c0-6.7,5.5-12.2,12.2-12.2
+ s12.2,5.5,12.2,12.2C85.5,80,80.1,85.5,73.3,85.5z"/>
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+ <g>
+ <path class="st50" d="M46,65.8c0-19.1,15.5-29.7,34.6-29.7s34.6,10.6,34.6,29.7c0,0-9.6-9.8-34.2-9.8S46,65.8,46,65.8z"/>
+ </g>
+ <g>
+ <path class="st0" d="M66.3,23.3c0,0-10.7,2.8-10.2,9.7v20.8"/>
+ <line class="st0" x1="56" y1="53.7" x2="59.3" y2="66"/>
+ <line class="st0" x1="56" y1="53.7" x2="54" y2="64.7"/>
+ <line class="st0" x1="56" y1="53.7" x2="62" y2="59.2"/>
+ <line class="st0" x1="56" y1="53.7" x2="50.8" y2="60.4"/>
+ </g>
+</g>
+</svg>
diff --git a/doc/antora/modules/ROOT/assets/images/networkradius.png b/doc/antora/modules/ROOT/assets/images/networkradius.png
new file mode 100644
index 0000000..bf1ea80
--- /dev/null
+++ b/doc/antora/modules/ROOT/assets/images/networkradius.png
Binary files differ
diff --git a/doc/antora/modules/ROOT/nav.adoc b/doc/antora/modules/ROOT/nav.adoc
new file mode 100644
index 0000000..3d92412
--- /dev/null
+++ b/doc/antora/modules/ROOT/nav.adoc
@@ -0,0 +1 @@
+* xref:index.adoc[Introduction]
diff --git a/doc/antora/modules/ROOT/pages/directories.adoc b/doc/antora/modules/ROOT/pages/directories.adoc
new file mode 100644
index 0000000..9b16249
--- /dev/null
+++ b/doc/antora/modules/ROOT/pages/directories.adoc
@@ -0,0 +1,69 @@
+= Directories
+
+The directories in the server source are laid out ad follows:
+
+== Documentation
+
+[width="100%",cols="50%,50%",options="header",]
+|===
+| Directory | Description
+| `doc/` | Various snippets of documentation.
+| `doc/introduction/` | Concepts and introduction to FreeRADIUS.
+| `doc/raddb/` | HTML versions of the configuration files.
+| `doc/developers/` | Developer documentation for internal APIs
+| `doc/unlang/` | The unlang processing language.
+| `doc/upgrade/` | How to upgrade from version 3 to version 4.
+| `doc/rfc/` | Copies of the RFC’s. If you have Perl, do a `make` in
+ that directory, and look at the HTML output.
+| `doc/antora/` | Metadata and documentation source files to build
+ an Antora based documentation site.
+| `doc/doxygen/` | Files to build a Doxygen site from the source code.
+| `man/` | Unix Manual pages for the server, configuration files,
+ and associated utilities.
+|===
+
+== Utility
+
+[cols=",",options="header",]
+|===
+|Directory | Description
+| `mibs/` | SNMP Mibs for the server.
+| `scripts/` | Sample scripts for startup and maintenance.
+|===
+
+== Configuration
+
+[width="100%",cols="50%,50%",options="header",]
+|===
+| Directory | Description
+| `raddb/` | Sample configuration files for the server.
+| `raddb/mods-available` | Module configuration files.
+| `raddb/mods-enabled` | Directory containing symlinks to `raddb/mods-available`.
+ Controls which modules are enabled.
+| `raddb/sites-available` | Virtual servers.
+| `raddb/sites-enabled` | Directory containing symlinks to `raddb/sites-available`.
+ Control which virtual servers are enabled.
+|===
+
+== Packaging
+
+[cols=",",options="header",]
+|===
+|Directory | Description
+| `debian/` | Files to build a `freeradius` Debian Linux package.
+| `redhat/` | Additional files for a RedHat Linux system.
+| `suse/` | Additional files for a SuSE (UnitedLinux) system.
+|===
+
+== Source
+
+[cols=",",options="header",]
+|===
+|Directory | Description
+| `src/` | Source code.
+| `src/bin/` | Source code for the daemon and associated utilities.
+| `src/lib/` | Source code for various utility libraries.
+| `src/include/` | Header files.
+| `src/protocols/` | Dynamic frontend plug-in modules.
+| `src/modules/` | Dynamic backend plug-in modules.
+|===
diff --git a/doc/antora/modules/ROOT/pages/index.adoc b/doc/antora/modules/ROOT/pages/index.adoc
new file mode 100644
index 0000000..e9bc7a0
--- /dev/null
+++ b/doc/antora/modules/ROOT/pages/index.adoc
@@ -0,0 +1,137 @@
+= Introduction
+
+This is the documentation for FreeRADIUS, version 3. The documentation
+is available under the Creative Commons Non-Commercial license, as given
+in the `LICENSE` file in this directory.
+
+FreeRADIUS is a complex piece of software with many configuration
+options. However, we have taken great care to make the default
+configuration work in most circumstances. The result is that for most
+simple systems, it is trivial to install and configure the server. For
+those situations, this documentation will serve to answer basic
+questions about functionality, configuration, etc.
+
+For more complex requirements, FreeRADIUS can be difficult to
+configure. The reason for this difficulty is that the server can do
+almost anything, which means that there are a near-infinite number of
+ways to configure it. The question for an administrator, then, is what
+piece of the configuration to change, and how to change it.
+
+This documentation will answer those questions. The FreeRADIUS team has
+put substantial effort into writing the documentation for this release.
+Everything in the server is fully documented, and there are many
+`how-to` guides available.
+
+The documentation is split into sections by subject area, oganized by
+desired outcome. At a high level, the subject areas describe:
+
+* xref:concepts:index.adoc[Concepts] and introduction for newcomers.
+* xref:installation:index.adoc[Installing] and xref:installation:upgrade.adoc[upgrading] FreeRADIUS.
+* The syntax of the xref:unlang:index.adoc[unlang] processing language.
+* The xref:raddb:index.adoc[configuration files] located in `/etc/raddb/`, or `/etc/freeradius/`
+* Various xref:howto:index.adoc[how-to] guides.
+* xref:developers:index.adoc[Developer documentation].
+
+This organization means that for example, the `ldap` module will have
+documention located in multiple places. We feel that organizing the
+documentation by desired _goal_ is better than the alternatives.
+
+Within each section, the documentation is split into small pages, which
+are generally no more than a few screens worth of information. We feel
+that having multiple small pages with cross-links is more helpful than
+having a smaller number of enormous pages. This division ensures that
+(for example) the `how-to` guides are split into a series of small
+steps, each of which can be performed quickly.
+
+We hope that this extended documentation will address any lingering
+concerns about the quality of the FreeRADIUS documentation.
+
+== Changes From Earlier Versions
+
+Administrators who have version 2 and wish to upgrade to version 3
+should read the xref:installation:upgrade.adoc[upgrading] documentation.
+That documentation explains the differences between the two versions, and
+how an existing configuration can be reproduced in the latest
+release. We do _not_ recommend using version 2 configuration files
+with version 3. The configuration files are _not_ compatible across a
+major version upgrade.
+
+== Getting Started with FreeRADIUS
+
+FreeRADIUS can be installed using the pre-built packages available
+from http://packages.networkradius.com[Network RADIUS,
+window="_blank"]. That page contains packages for all common OS
+distributions. New packages are available as soon as a new version
+has been released. Packages for older releases are also available for
+historical purposes.
+
+FreeRADIUS can also be installed from the source code. Please see the
+xref:installation:index.adoc[installation guide] for instructions.
+
+WARNING: Many Operating System distributions ship versions of FreeRADIUS
+which are years out of date. Those versions may contain bugs which have
+been fixed in newer releases. We recommend using the
+http://packages.networkradius.com[Network RADIUS, window="_blank"] packages where
+possible.
+
+Administrators who are new to FreeRADIUS should read the
+xref:concepts:index.adoc[concepts section] as it describes the concepts behind
+FreeRADIUS. It is vital for newcomers to understand these concepts, as the rest
+of the documentation assumes familiarity with them.
+
+A detailed xref:unlang:index.adoc[unlang] reference guide is also available.
+This section describes the syntax and functionality of the keywords,
+data types, etc. used in the `unlang` processing language.
+
+All of the xref:raddb:index.adoc[configuration files] are available in
+hypertext format. In can often be easier to read the configuration files
+in a nicely formatted version, instead of as a fixed-width font in a
+text editor.
+
+For specific problem solving, we recommend the xref:howto:index.adoc[how-to]
+guides. These guides give instructions for reaching high-level goals, or
+for configuring and testing individual xref:howto:modules/index.adoc[modules].
+
+There is also xref:developers:index.adoc[developer documentation]. This section
+documents the APIs for developers. Most people can ignore it.
+
+== Debugging
+
+If you have ANY problems, concerns, or surprises when running the
+server, the the server should be run in debugging mode as root, from the
+command line:
+
+```
+# radiusd -X
+```
+
+It will produce a large number of messages. The answers to many
+questions, and the solution to many problems, can usually be found in
+these messages. When run in a terminal window, error messages will be
+shown in red text, and warning messages will be shown in yellow text.
+
+For other use-cases, please look for `ERROR` or `WARNING` in the
+debug output. In many cases, those messages describe exactly what is
+going wrong, and how to fix it.
+
+For further details, about the debug output see the
+http://wiki.freeradius.org/radiusd-X[radiusd-X, window="_blank"] page on the
+http://wiki.freeradius.org[wiki, window="_blank"].
+
+== Getting Help
+
+We also recommend joining the
+http://lists.freeradius.org/mailman/listinfo/freeradius-users[mailing
+list] in order to ask questions and receive answers. The developers are
+not on Stack Overflow, IRC, or other web sites. While the FreeRADIUS
+source is available on
+https://github.com/FreeRADIUS/freeradius-server/[GitHub, window="_blank"], questions
+posted there will not be answered.
+
+Before posting to the list, please read the
+http://wiki.freeradius.org/list-help[list help, window="_blank"] page. That page explains
+how to run the server in debugging mode; how to understand the debug
+output; and what information to post to the list.
+
+Commercial support for FreeRADIUS is available from
+https://networkradius.com/freeradius-support/[Network RADIUS, window="_blank"].
diff --git a/doc/antora/modules/howto/nav.adoc b/doc/antora/modules/howto/nav.adoc
new file mode 100644
index 0000000..351200b
--- /dev/null
+++ b/doc/antora/modules/howto/nav.adoc
@@ -0,0 +1,19 @@
+* xref:index.adoc[Howto Guides]
+** Protocols
+**** xref:protocols/dhcp/index.adoc[DHCP]
+***** xref:protocols/dhcp/prepare.adoc[Preparation]
+***** xref:protocols/dhcp/enable.adoc[Enabling the DHCP service]
+***** xref:protocols/dhcp/test.adoc[Testing the DHCP service]
+***** xref:protocols/dhcp/policy.adoc[Defining the DHCP policy]
+****** xref:protocols/dhcp/policy_ippool_creation.adoc[IP pool creation]
+****** xref:protocols/dhcp/policy_common_options.adoc[Common options]
+****** xref:protocols/dhcp/policy_network_options.adoc[Network options and IP pool selection]
+****** xref:protocols/dhcp/policy_subnet_options.adoc[Subnet options]
+****** xref:protocols/dhcp/policy_device_options.adoc[Device, class and group options]
+****** xref:protocols/dhcp/policy_ippool_access.adoc[IP pool access restriction]
+**** xref:protocols/proxy/index.adoc[PROXY Protocol]
+***** xref:protocols/proxy/enable_radsec.adoc[Enabling RadSec]
+***** xref:protocols/proxy/radsec_client.adoc[Configuring a test RadSec client]
+***** xref:protocols/proxy/radsec_with_haproxy.adoc[Proxying RadSec with HAproxy]
+***** xref:protocols/proxy/radsec_with_traefik.adoc[Proxying RadSec with Traefik]
+***** xref:protocols/proxy/enable_proxy_protocol.adoc[Enabling PROXY Protocol for RadSec]
diff --git a/doc/antora/modules/howto/pages/index.adoc b/doc/antora/modules/howto/pages/index.adoc
new file mode 100644
index 0000000..47a5146
--- /dev/null
+++ b/doc/antora/modules/howto/pages/index.adoc
@@ -0,0 +1,17 @@
+= Howto Guides
+
+The documents in this section describe how to perform various common tasks with
+FreeRADIUS. They also provide worked examples on using the various modules in
+common deployment scenarions.
+
+If you have a topic you'd like to see included in the list of howtos, contact
+the developers on the
+link:http://lists.freeradius.org/mailman/listinfo/freeradius-users[User's
+mailing list].
+
+Some of the documents here started life as pages on
+link:http://wiki.freeradius.org[wiki.freeradius.org]. If you've just been
+through a particularly arduous service configuration and deployment, and would
+like to help your fellow users, then please create a new how to on the wiki.
+If it's popular enough, we'll include it in the official documentation for the
+next release.
diff --git a/doc/antora/modules/howto/pages/protocols/dhcp/enable.adoc b/doc/antora/modules/howto/pages/protocols/dhcp/enable.adoc
new file mode 100644
index 0000000..2824bd0
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/dhcp/enable.adoc
@@ -0,0 +1,213 @@
+== Enabling the DHCP service
+
+A major difference between configuring FreeRADIUS as a DHCP server versus most
+other DHCP software such as ISC DHCP is that other software typically uses a
+single monolithic configuration file whereas FreeRADIUS has a collection of
+configuration files. This reflects the modularity of FreeRADIUS; attempting to
+put the entire configuration in a single file would result in a very difficult
+to read configuration.
+
+The root of the FreeRADIUS configuration may be in a different location on the
+filesystem depending on how FreeRADIUS has been installed. This directory will
+be referred to as `<raddb>` below. The sample configuration files are well
+commented describing what each configuration option does.
+
+FreeRADIUS compiled from source will default to `/usr/local/etc/raddb`.
+Pre-built packages will default to either `/etc/raddb` or
+`/etc/freeradius`.
+
+
+=== Enable the DHCP virtual server
+
+The FreeRADIUS configuration separates each network service that it provides
+into "virtual servers". A number of sample virtual server definitions are
+provided in `<raddb>/sites-available`, one of which is the sample
+configuration for a DHCP service.
+
+Sites may be added to the working configuration by either creating a symlink to
+them or copying them to `<conf>/sites-enabled` depending on how you wish to
+manage future upgrades.
+
+[TIP]
+====
+As with other package-managed configuration files, package upgrades will not
+automatically replace files that you have edited but you will need to resolve
+any local differences. Creating copies avoids the need to resolve conflicts
+during a package upgrade.
+====
+
+Add the DHCP virtual server to the active configuration:
+
+[source,shell]
+----
+cd <raddb>/sites-enabled
+ln -s ../sites-available/dhcp .
+----
+
+or:
+
+[source,shell]
+----
+cd <raddb>/sites-enabled
+cp ../sites-available/dhcp .
+----
+
+The sample configuration has been set up in such a way that it is initially
+safe. It will not actually take over live DHCP serving on the network when it
+is simply enabled until it is configured to do so. Rather is set up for testing
+prior to going live.
+
+The virtual server begins with a `listen` section. In this section your need to
+modify the following configuration items:
+
+`ipaddr`:: The IP address to listen on.
+`src_ipaddr`:: The source IP for unicast packets.
+`port`:: The port to listen on. Setting this to `67` will make the DHCP service live on the network.
+`interface`:: The network interface to listen on.
+`broadcast`:: Allow broadcast packets. For most live systems this will need to be set to `yes`.
+
+Below the `listen` section, there are sections that define how to respond to
+each of the DHCP packet types. Most installations will require that you review
+the settings for `DHCP-Discover` and `DHCP-Request`.
+
+Their contents contain directives in the FreeRADIUS policy language, "unlang".
+Many examples are provided which have been carefully described.
+
+
+=== Enable SQL and IP pool modules
+
+FreeRADIUS has many modules to support different aspects of the functionality
+required for the network protocols it can process. The two of most significance
+for DHCP are `dhcp_sql` and `dhcp_sqlippool`. As with virtual servers, a
+number of example module configurations are available in
+`<raddb>/mods-available`.
+These should be symlinked or copied into `<raddb>/mods-enabled` in order to
+enable them.
+
+
+==== Configure the `dhcp_sql` module
+
+Add the `dhcp_sql` module to the active configuration:
+
+[source,shell]
+----
+cd <raddb>/mods-enabled
+ln -s ../mods-available/dhcp_sql .
+----
+
+or:
+
+[source,shell]
+----
+cd <raddb>/mods-enabled
+cp ../mods-available/dhcp_sql .
+----
+
+The `dhcp_sql` module should be configured with the connection parameters for
+whichever database is to be used. The key configuration items are:
+
+`dialect`:: Which SQL dialect is in use.
+`driver`:: Which driver to use to access the database. For most databases this
+ is `rlm_sql_<dialect>`, however Microsoft SQL Server has a choice of
+ drivers.
+
+Then, there are configuration options that are unique to each database,
+including connection details. For most databases these are:
+
+`server`:: The host name or IP address of the database server.
+`port`:: The port to connect to the database server on.
+`login`:: The user name used to connect to the database.
+`password`:: The password for authenticating to the database.
+`radius_db`:: The name of the database.
+
+[NOTE]
+====
+SQLite does not use these connection options, rather the `filename`
+option within the `sqlite` section is used to determine where the database
+will be stored.
+====
+
+
+==== Configure the `dhcp_sqlippool` module
+
+Add the `dhcp_sqlippool` module to the active configuration:
+
+[source,shell]
+----
+cd <raddb>/mods-enabled
+ln -s ../mods-available/dhcp_sqlippool .
+----
+
+or
+
+[source,shell]
+----
+cd <raddb>/mods-enabled
+cp ../mods-available/dhcp_sqlippool .
+----
+
+The `dhcp_sqlippool` module must be configured. The key configuration
+items are:
+
+`dialect`:: Set this to the same SQL dialect as in the `sql` module.
+`offer_duration`:: How long an IP is offered to the client in a DHCP OFFER.
+`lease_duration`:: How long an IP is leased to the client in a DHCP ACK.
+
+
+=== Provision the database
+
+You should provision your database by creating a user for FreeRADIUS (matching
+the configuration that you have previously provided) and then loading the
+schema. The procedure for doing this will vary according to the database
+server.
+
+The schema, stored procedure definition and any additional setup scripts for
+your database are in `<raddb>/mods-config/sql/ippool-dhcp/{dialect}/`.
+
+=== Test FreeRADIUS startup
+
+Once you have provisioned your schema, created a user account and granted
+access to the user, you should be able to start FreeRADIUS.
+
+If FreeRADIUS has been configured correctly then the output of `ss` will
+contain a line showing that FreeRADIUS is listening for DHCP packets on the
+designated interface on port 67:
+
+.Example of FreeRADIUS listening on `<interface>` for DHCP packets
+==================================================================
+ # ss -lunp
+ Netid Recv-Q Send-Q Local Address:Port ...
+ udp 0 0 0.0.0.0%<interface>:67 ... users:(("radiusd",...))
+==================================================================
+
+Note that if the database is inaccessible then FreeRADIUS will normally refuse
+to start.
+
+The FreeRADIUS wiki contains extensive information about debugging FreeRADIUS
+startup issues that we do not repeat in any detail here.
+
+Essentially, stop your init system from repeatedly trying to launch FreeRADIUS:
+
+[source,shell]
+----
+service radiusd stop
+----
+
+Then start FreeRADIUS manually in debug mode:
+
+[source,shell]
+----
+radiusd -X
+----
+
+Carefully read the output since this will tell you why FreeRADIUS was unable to
+start.
+
+Once you have fixed the issue start FreeRADIUS as normal:
+
+[source,shell]
+----
+service radiusd start
+----
+
+Now xref:protocols/dhcp/test.adoc[test the DHCP service] to ensure that it is responding to requests.
diff --git a/doc/antora/modules/howto/pages/protocols/dhcp/index.adoc b/doc/antora/modules/howto/pages/protocols/dhcp/index.adoc
new file mode 100644
index 0000000..fde2202
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/dhcp/index.adoc
@@ -0,0 +1,35 @@
+= FreeRADIUS DHCP server
+
+This guide describes how FreeRADIUS can be used in place of ISC DHCP or ISC Kea
+to provide a significantly more performant and, above all, more flexible DHCP
+server.
+
+This guide provides a suggested configuration that should be somewhat familiar
+to anyone who has previously implemented DHCP using the most frequently used
+features of other DHCP server software.
+
+The modular design of FreeRADIUS means that there is no one "right" way to
+implement the DHCP service. FreeRADIUS allows you to put together a "mix and
+match" approach.
+
+For example you can manage the leases in an SQL database. You might then hard
+code certain DHCP reply parameters within configuration and then look up
+additional parameters using a datastore such as:
+
+ * a local file such as a structured text file or an SQLite database
+ * an organisational LDAP directory
+ * an SQL or "no SQL" database
+ * a remote endpoint such as a RESTful HTTP API
+
+The policy language and modular configuration of FreeRADIUS is sufficiently
+powerful and that almost any aspect of the server's behaviour can be customised
+to implement even the most sophisticated DHCP configurations.
+
+== Sections in this guide
+
+This guide is organised into four parts that should be read in order:
+
+1. xref:protocols/dhcp/prepare.adoc[Preparation]
+2. xref:protocols/dhcp/enable.adoc[Enabling the DHCP service]
+3. xref:protocols/dhcp/test.adoc[Testing the DHCP service]
+4. xref:protocols/dhcp/policy.adoc[Defining the DHCP policy]
diff --git a/doc/antora/modules/howto/pages/protocols/dhcp/policy.adoc b/doc/antora/modules/howto/pages/protocols/dhcp/policy.adoc
new file mode 100644
index 0000000..d8f1bcb
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/dhcp/policy.adoc
@@ -0,0 +1,14 @@
+== Defining the DHCP policy
+
+Now that FreeRADIUS is successfully running as a DHCP server it is necessary to
+configure a DHCP policy so that it returns correctly formed responses to the DHCP
+requests that it receives.
+
+This involves a number of steps:
+
+ * xref:protocols/dhcp/policy_ippool_creation.adoc[Defining the IP address pools.]
+ * xref:protocols/dhcp/policy_common_options.adoc[Defining the options that are common to all replies.]
+ * xref:protocols/dhcp/policy_network_options.adoc[Defining the options for the network from which the request originates and ensuring that IP addresses are allocated from the correct pool.]
+ * xref:protocols/dhcp/policy_subnet_options.adoc[Defining the options for the subnet to which this issued IP address belongs.]
+ * xref:protocols/dhcp/policy_device_options.adoc[Defining the device, class and group based options specific to the device.]
+ * xref:protocols/dhcp/policy_ippool_access.adoc[Using device properties to restrict access to certain pools.]
diff --git a/doc/antora/modules/howto/pages/protocols/dhcp/policy_common_options.adoc b/doc/antora/modules/howto/pages/protocols/dhcp/policy_common_options.adoc
new file mode 100644
index 0000000..949868d
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/dhcp/policy_common_options.adoc
@@ -0,0 +1,80 @@
+== Configure common reply options
+
+FreeRADIUS includes a powerful xref:unlang/index.adoc[policy language] called
+"unlang".
+
+Statements in unlang may be used to call further policies, update attribute
+lists and invoke modules. There are also control flow statements (if,
+switch, etc.) typical of most imperative languages.
+
+FreeRADIUS has a number attribute lists that it maintains as it processes
+packets within the virtual server sections. Most relevant to DHCP are
+`request`, `control` and `reply`.
+
+The DHCP options from the current request packet are provided in the
+`request` list. This includes fixed DHCP parameters such as
+`DHCP-Client-Hardware-Address`, optional parameters such as
+`DHCP-Requested-IP-Address`, and parameters synthesised by FreeRADIUS such as
+`DHCP-Message-Type` and `DHCP-Network-Subnet`.
+
+DHCP options can be set by updating their value in the `reply` list. This
+forms the basis of the packet returned to the client.
+
+In the default DHCP server configuration, a "policy" (akin to a subroutine) is
+used to set common options for reply packets. The policy is found in
+`<raddb>/policy.d/dhcp`.
+
+Look at the contents of the `dhcp_common` section and set any global options
+applicable to all clients in this policy.
+
+[source,unlang]
+----
+dhcp_common {
+ update reply {
+ &DHCP-Domain-Name-Server := 8.8.8.8
+ &DHCP-Domain-Name-Server += 8.8.4.4
+ &DHCP-Subnet-Mask := 255.255.255.0
+ &DHCP-Router-Address := 192.0.2.1
+ ...
+ }
+}
+----
+
+Note, FreeRADIUS has four main operators for assigning values to attributes:
+
+`=`:: Add the attribute to the list, if and only if an attribute of the same
+ name is not already present in that list.
+`:=`:: Add the attribute to the list. If any attribute of the same name is
+ already present in that list it is replaced with the new one.
+`+=`:: Add the attribute to the tail of the list, even if attributes of the
+ same name are already present in the list.
+`^=`:: Add the attribute to the head of the list, even if attributes of the
+ same name are already present in the list.
+
+These operators allow for attributes to be set to default values and then
+overwritten, e.g. setting a default lease time, but then overwriting it for
+a particular group of clients.
+
+Attributes in the `control` list are not returned in the DHCP reply packets
+but instead govern aspects of server's behaviour.
+
+To use an SQL backend for either static or dynamic IP allocation, un-comment
+the block:
+
+[source,unlang]
+----
+update control {
+ &Pool-Name := "local"
+}
+dhcp_sqlippool
+----
+
+The `Pool-Name` control attribute is used in looking up addresses in the
+database. The line containing `dhcp_sqlippool` is a call to invoke an
+instance of a module with that name. This module is responsible for assigning a
+free IP address into the `DHCP-Your-IP-Address` reply attribute from the pool
+identified by `Pool-Name`.
+
+Here `Pool-Name` is being set to a constant value (`local`) indicating
+that a single pool is to be used. If you have multiple pools, then replace this
+`update` block with logic to map clients to the correct pool, as described below.
diff --git a/doc/antora/modules/howto/pages/protocols/dhcp/policy_device_options.adoc b/doc/antora/modules/howto/pages/protocols/dhcp/policy_device_options.adoc
new file mode 100644
index 0000000..05845ea
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/dhcp/policy_device_options.adoc
@@ -0,0 +1,310 @@
+== Configure "device", "class" and "group" options
+
+Beyond the global, network and subnet options already described, most sites
+will have a number of group or class based options, and have a requirement for
+setting reply parameters against individual devices.
+
+In general, FreeRADIUS does not differentiate between "classes" (memberships
+defined by some attribute of the DHCP request) and "groups" (memberships
+defined by some manually aggregation related devices, typically based on lists
+of MAC address).
+
+The sample DHCP configuration provided with FreeRADIUS makes use of an internal
+attribute `DHCP-Group-Name` to support the setting of different options for
+different groups of devices.
+
+In general the groups to which a device belongs is determined during the
+processing of a request and these are added as instances of the
+`DHCP-Group-Name` attribute. This may be by performing a test on one or more
+request parameters (akin to a "class"), hash-based lookup of up all of part of
+an attribute in a local list (akin to a "subclass"), or doing the same using a
+remote datastore (SQL, LDAP, REST API, etc).
+
+FreeRADIUS can then iterate over `DHCP-Group-Name` to set group-specific
+options.
+
+We describe some of these options in more detail.
+
+=== Directly in Policy
+
+Simple class options can be written directly into policy. This is most
+suited to those options that rarely change and are based on attributes in the
+request such as the `User-Class`.
+
+Consider the ISC DHCP configuration snippet:
+
+[source,iscdhcp]
+----
+filename "undionly.kpxe";
+class "pxeclient" {
+ match option substring(user-class,0,4);
+}
+subclass "pxeclient" "iPXE" {
+ filename "http://my.web.server/boot_script.php";
+}
+----
+
+Or the equivalent Kea configuration:
+
+[source,isckea]
+----
+"Dhcp4": {
+ "option-data": [
+ { "name": "boot-file-name", "data": "undionly.kpxe" }
+ ],
+ "client-classes": [
+ {
+ "name": "pxeclient",
+ "test": "substring(option[77],0,4) == 'iPXE'",
+ "option-data": [
+ {
+ "name": "boot-file-name",
+ "data": "http://my.web.server/boot_script.php"
+ }
+ ]
+ }
+ ]
+ ...
+}
+----
+
+These define the "filename" DHCP option differently based on whether or not the
+supplied "user-class" option begins with "iPXE".
+
+FreeRADIUS provides multiple ways for this to be configured.
+
+For example, the following "unlang" policy implements the class options defined
+above:
+
+[source,unlang]
+----
+if (&DHCP-User-Class && "%{substring:&DHCP-User-Class 0 4}" == "iPXE") {
+ update reply {
+ &DHCP-Boot-Filename := "http://my.web.server/boot_script.php"
+ }
+} else {
+ update reply {
+ &DHCP-Boot-Filename := "undionly.kpxe"
+ }
+}
+----
+
+Policy-based configuration of DHCP options is also useful for complex matching.
+For example, the following Unlang sets the DHCP-Boot-Filename parameter based
+on the request's DHCP-Client-Identifier using regular expression captures,
+provided that it matches the given format:
+
+[source,unlang]
+----
+if (&DHCP-Client-Identifier && \
+ "%{string:DHCP-Client-Identifier}" =~ /^RAS([0-9])-site([A-Z])$/) {
+ update reply {
+ &DHCP-Boot-Filename := "rasboot-%{1}-%{2}.kpxe"
+ }
+}
+----
+
+=== In Text Files
+
+The `files` module that has already been described for global, network and
+subnet options can also be used to apply options to groups of clients.
+
+Firstly we must defined a mapping from a set of clients clients to their
+respective groups. One option for this is to use the `passwd` module, for
+which a sample configuration is included.
+
+Firstly symlink or copy the module configuration
+`<raddb>/mods-available/dhcp_passwd` into `<raddb>/mods-enabled/`. The
+suggested configuration expects the group membership file to be in
+`<raddb>/mods-config/files/dhcp_groups` and take the form of:
+
+[source,config]
+----
+<group1 name>|<hardware address>,<hardware address>,<hardware address>
+<group2 name>|<hardware address>,<hardware address>
+----
+
+i.e. one line for each group starting with the group name followed by a pipe
+character and then a comma-separated list of hardware addresses.
+
+The `allow_multiple_keys` option allows for a host to be a member of
+more than one group.
+
+Sample configuration for looking up group options is contained in
+`<raddb>/policy.d/dhcp` in the `dhcp_group_options` policy and in
+`<raddb>/mods-available/dhcp_files` as the `dhcp_set_group_options` instance.
+
+The same data file `<raddb>/mods-config/files/dhcp` is used to lookup
+group options as was used for global and network options. In this instance,
+add entries with the group name as the key such as:
+
+[source,config]
+----
+group1
+ DHCP-Log-Server := 10.10.0.100,
+ DHCP-LPR-Server := 10.10.0.200
+
+group2
+ DHCP-LPR-Server := 192.168.20.200
+----
+
+=== In the SQL Database
+
+Policy and files are both read during startup and editing them while
+FreeRADIUS is running will not result in any changes in behaviour. If
+you require regular changes to DHCP options, then storing them in
+an SQL database provides greater flexibility since the queries will be run in
+response to each DHCP packet rather than requiring the server to be restarted.
+
+DHCP reply options for devices (including network-specific options) can be
+fetched from SQL using an arbitrary lookup key. This can be performed multiple
+times as necessary using different contexts, for example to first set
+subnet-specific options and then to set group-specific options.
+
+The default schema contains three tables to support this:
+
+"dhcpreply" contains reply options for a given identifier (e.g. MAC Address):
+
+.dhcpreply table
+|===
+|Identifier |Attribute |Op |Value |Context
+
+|`02:01:aa:bb:cc:dd` |`DHCP-Log-Server` |`:=` |`192.0.2.10` |`by-mac`
+|`02:01:aa:bb:cc:dd` |`DHCP-LPR-Server` |`:=` |`192.0.2.11` |`by-mac`
+|`02:01:aa:bb:cc:dd` |`Fall-Through` |`:=` |`Yes` |`by-mac`
+|===
+
+"dhcpgroup" maps identifiers to a group of options that can be shared:
+
+.dhcpgroup table
+|===
+|Identifier |GroupName |Priority |Context
+
+|`02:01:aa:bb:cc:dd` |`salesdept` |`10` |`by-mac`
+|===
+
+"dhcpgroupreply" contains reply options for each group:
+
+.dhcpgroupreply table
+|===
+|GroupName |Attribute |Op |Value |Context
+
+|`salesdept` |`DHCP-NTP-Servers` |`:=` |`192.0.2.20` |`by-mac`
+|`salesdept` |`DHCP-Log-Server` |`+=` |`192.0.2.21` |`by-mac`
+|`salesdept` |`DHCP-LPR-Server` |`^=` |`192.0.2.22` |`by-mac`
+|===
+
+Within the context of assigning options directly to devices, as well as to
+manually-curated groups of devices keyed by their MAC address:
+
+ - Place device-specific options in the "dhcpreply" table.
+ - Add `Fall-Through := Yes` to the options in the "dhcpreply" table in order
+ to trigger group lookups, which are disabled by default.
+ - Place entries in the "dhcpgroup" `identifier = <MAC-Address>, groupname = <group>, priority =
+ <priority>` in the "dhcpgroup" table to map a device to its groups by
+ priority.
+ - Place the grouped options in the "dhcpgroupreply" table.
+ - For each of the above, set `Context` to something by which the option
+ lookup is referred to in the policy, for example `Context = 'by-mac'`.
+
+For the above example you would add the following to the DHCP virtual server to
+perform reply option lookup using the device's MAC address against the `by-mac`
+context:
+
+[source,unlang]
+----
+update control {
+ &DHCP-SQL-Option-Context := "by-mac"
+ &DHCP-SQL-Option-Identifier := &request:DHCP-Client-Hardware-Address
+}
+dhcp_sql.authorize
+----
+
+In the above, the DHCP reply options would be assigned to a device with MAC
+address 02:01:aa:bb:cc:dd as follows:
+
+ - Firstly, the `DHCP-Log-Server` option would be set to `192.0.2.10` and the
+ `DHCP-LPR-Server` option set to `192.0.2.11`.
+ - `Fall-Through` is set, so the group mapping is then queried which
+ determines that the device belongs to a single `salesdept` group.
+ - Finally, the options for the `salesdept` group are now merged, setting a
+ `DHCP-NTP-Servers` option to `192.0.2.20`, appending an additional
+ `DHCP-Log-Server` option set to `192.0.2.21`, and prepending an additional
+ `DHCP-LPR-Server` option set to `192.0.2.22`.
+
+If instead you wanted to perform a "subclass" lookup based on the first three
+octets of the device's MAC address then with tables containing the following
+sample data you could invoke an SQL lookup as shown:
+
+."dhcpreply" table:
+|===
+|Identifier |Attribute |Op |Value |Context
+
+|`000393` |`Fall-Through` |`:=` |`Yes` |`class-vendor`
+|`000a27` |`Fall-Through` |`:=` |`Yes` |`class-vendor`
+|`f40304` |`Fall-Through` |`:=` |`Yes` |`class-vendor`
+|===
+
+."dhcpgroup" table:
+|===
+|Identifier |GroupName |Priority |Context
+
+|`000393` |`apple` |`10` |`class-vendor`
+|`000a27` |`apple` |`10` |`class-vendor`
+|`f40304` |`google` |`10` |`class-vendor`
+|===
+
+."dhcpgroupreply" table:
+|===
+|GroupName |Attribute |Op |Value |Context
+
+|`apple` |`DHCP-Boot-Filename` |`:=` |`apple.efi` |`class-vendor`
+|`google` |`DHCP-Boot-Filename` |`:=` |`google.efi` |`class-vendor`
+|===
+
+
+[source,unlang]
+----
+update control {
+ &DHCP-SQL-Option-Context := "class-vendor"
+ &DHCP-SQL-Option-Identifier := \
+ "%{substring:%{hex:&DHCP-Client-Hardware-Address} 0 6}"
+}
+dhcp_sql.authorize
+----
+
+The file `policy.d/dhcp` contains a policy named `dhcp_policy_sql` which
+provides further worked examples for different types of option lookups.
+
+=== Testing "device", "class" and "group" options
+
+You should now test that any device-related options that you have configured
+using the various methods available are applied successfully by generating
+packets containing those parameters based upon which the reply options are set.
+
+For example, to test the iPXE user class example above you might want to
+generate a request as follows:
+
+[source,shell]
+----
+cat <<EOF > dhcp-packet-ipxe-boot.txt
+DHCP-Message-Type := DHCP-Discover
+DHCP-Client-Hardware-Address := 02:01:aa:bb:cc:dd
+DHCP-User-Class := "iPXE-class-abc"
+EOF
+----
+
+To which you would expect to see a response such as:
+
+.Example output from dhcpclient
+===============================
+ dhcpclient: ...
+ ----------------------------------------------------------------------
+ Waiting for DHCP replies for: 5.000000
+ ----------------------------------------------------------------------
+ ...
+ DHCP-Message-Type = DHCP-Offer
+ DHCP-Your-IP-Address = 1.2.3.4
+ DHCP-Boot-Filename := "http://my.web.server/boot_script.php"
+ ...
+===============================
diff --git a/doc/antora/modules/howto/pages/protocols/dhcp/policy_ippool_access.adoc b/doc/antora/modules/howto/pages/protocols/dhcp/policy_ippool_access.adoc
new file mode 100644
index 0000000..40b8e30
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/dhcp/policy_ippool_access.adoc
@@ -0,0 +1,54 @@
+== Configure access restrictions for pools
+
+We can combine what we have learned in the preceeding sections to provide pools
+whose access is restricted in some way, for example to a particular class.
+
+Consider the ISC DHCP configuration snippet:
+
+[source,iscdhcp]
+----
+subnet 10.99.99.0 netmask 255.255.255.0 {
+ pool {
+ range 10.99.99.200 10.99.99.250;
+ allow members of "printers";
+ }
+ option routers 10.99.99.1;
+}
+----
+
+Or the equivalent Kea configuration:
+
+[source,isckea]
+----
+"Dhcp4": {
+ "subnet4": [{
+ "subnet": "10.99.99.0/24",
+ "pools": [
+ {
+ "pool": "10.99.99.200 - 10.99.99.250",
+ "client-class": "printers"
+ }
+ ],
+ "option-data": [
+ { "name": "routers", "data": "10.10.0.1" }
+ ]
+ }],
+ ...
+}
+----
+
+These define a subnet containing a single pool that is restricted to members of
+the "printers" class. (The definition for this class is omitted.)
+
+In FreeRADIUS, to filter access to this pool entries such as the following
+should included in the `<raddb>/mods-config/files/dhcp` configuration file:
+
+[source,config]
+----
+network DHCP-Network-Subnet < 10.99.99.0/24, \
+ DHCP-Group-Name == "printers", Pool-Name := "printers-pool"
+ DHCP-Router-Address := 10.99.99.1
+----
+
+Note that any number of additional filters can be added to the initial "check"
+line to restrict matches to the network block.
diff --git a/doc/antora/modules/howto/pages/protocols/dhcp/policy_ippool_creation.adoc b/doc/antora/modules/howto/pages/protocols/dhcp/policy_ippool_creation.adoc
new file mode 100644
index 0000000..e976873
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/dhcp/policy_ippool_creation.adoc
@@ -0,0 +1,112 @@
+=== Determine the IP pool plan
+
+Except for cases where all IP allocation is performed using a mapping from the
+device MAC address to a fixed IP address, the DHCP configuration will involve
+the use of one or more IP address pools.
+
+FreeRADIUS stores all the IP addresses in its pools in whichever database has
+been chosen. An instance of the `sqlippools` module is used to manage all pools
+within a single table (normally `dhcpippool`). Each row of this table
+corresponds to an IP address that is a member of some pool. The pools are
+distinguished by name, so the table has a column (`pool_name`) that denotes
+this.
+
+Each pool in this table should be composed of a set of equally valid IP
+addresses for the devices that are designated to be members of the pool.
+
+Firstly, consider the network locations to which distinct ranges of IP
+addresses must be allocated and provisionally assign a pool to each.
+
+Next, consider that many networks support multiple co-existing subnets without
+VLAN separation. We will call this a "shared-network" to use the original ISC
+DHCP parlance. In Microsoft DHCP contexts this is often referred to as a
+"multinet".
+
+Often in a shared-network the policy has no regard for which of the network's
+devices is allocated to which subnet. In this case we must create a single,
+combined pool containing all of the IP addresses from each subnet in that
+network. Since all addresses in a pool are treated equally this will mean that
+any IP address may be allocated to a device that is making a DHCP request from
+that network. The appropriate DHCP parameters for the subnet to which the IP
+address belongs is determined after allocation.
+
+There are sometimes shared-networks (or even single subnets) for which IP
+addresses belonging to any subnet may be technically suitable for any device,
+however some local policy wants to assigning them to a particular subnet, for
+example to provide loose segregation between classes of device. In this case we
+define multiple pools, one for each range of IP addresses whose devices needs to
+be differentiated.
+
+The choice of pool is ordinarily determined based on the network from which the
+request originates using a mapping from Layer 2 networks to the pool name
+provided by the user. The indicator for the originating network can be
+overridden when this alone is insufficient to implement the required pool
+selection policy such as when you need to differentiate the pool's users with
+more granularity that their Layer 2 network, such as by considering device
+attributes ("class" membership in ISC parlance) or Option 82 circuit data.
+
+
+=== Populate the IP Pools
+
+By this stage you should have derived a list of pools, the IP address ranges
+contained therein, and the means of selecting the pool to use based on the
+originating network and/or some additional criteria from the request.
+
+A helper Perl script is provided with FreeRADIUS that can be used to populate
+the pools provide that you are using the default schema.
+
+[source,shell]
+----
+rlm_sqlippool_tool -p <pool_name> -s <range_start> -e <range_end> \
+ -t <table_name> (-d <sql_dialect> | -f <raddb_dir> [ -i <instance> ]) \
+ [ -c <capacity> ] [ -x <existing_ips_file> ]
+----
+
+If, for example, you had a range configured in ISC DHCP as:
+
+[source,iscdhcp]
+----
+range 10.0.0.5 10.0.0.199
+----
+
+and you are using PostgreSQL as your database, and you wish to refer to this pool
+using the name `local`, this could be prepared with:
+
+[source,shell]
+----
+rlm_sqlippool_tool -p local -s 10.0.0.5 -e 10.0.0.199 -t dhcpippool -d postgresql
+----
+
+If the SQL module of FreeRADIUS is already configured then this can
+be referenced so that the tool is able to use the configured connection
+parameters to connect to the database and populate the pool:
+
+[source,shell]
+----
+rlm_sqlippool_tool -p local -s 10.0.0.5 -e 10.0.0.199 -t dhcpippool -f /etc/raddb
+----
+
+For installations that require multiple pools, `rlm_sqlippool_tool` can
+be called referencing a YAML file defining the pools. Comments at the
+head of `rlm_sqlippool_tool` explain the options in more detail.
+
+If static leases are required then these should be set up in the database
+such that the MAC address of the client should be set as the `pool_key`
+against the corresponding address and the `status` column of the row
+representing the address set to `static`. A helper perl script,
+`rlm_iscfixed2ippool` can be used to read an ISC DHCP config file and produce
+SQL to perform these changes or directly update the database:
+
+[source,shell]
+----
+rlm_iscfixed2ippool -c <dhcpd.<raddb> -t <table_name> -k <mac|id> \
+ (-d <sql_dialect> | -f <raddb_dir> [-i <instance>])
+----
+
+For example, to read /etc/dhcp/dhcpd.conf and populate the configured
+FreeRADIUS database, using the mac as the identifier:
+
+[source,shell]
+----
+rlm_iscfixed2ippool -c /etc/dhcp/dhcpd.conf -t dhcpippool -k mac -f /usr/local/etc/raddb
+----
diff --git a/doc/antora/modules/howto/pages/protocols/dhcp/policy_network_options.adoc b/doc/antora/modules/howto/pages/protocols/dhcp/policy_network_options.adoc
new file mode 100644
index 0000000..e2657a8
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/dhcp/policy_network_options.adoc
@@ -0,0 +1,237 @@
+== Configure network-specific options and IP pool selection
+
+In an environment where multiple networks (often VLANs) are in use, it is
+necessary to identify which network a client belongs to in order to assign an
+address from the correct pool.
+
+Consider the ISC DHCP configuration snippet:
+
+[source,iscdhcp]
+----
+option domain-name "example.org";
+
+subnet 10.10.0.0 netmask 255.255.0.0 {
+ range 10.10.1.10 10.10.10.254;
+ range 10.10.100.10 10.10.110.254;
+ option routers 10.10.0.1;
+ option domain-name-servers 10.10.0.2, 10.10.0.3;
+ default-lease-time 7200;
+}
+----
+
+Or the equivalent Kea configuration:
+
+[source,isckea]
+----
+"Dhcp4": {
+ "option-data": [
+ { "name": "domain-name", "data": "example.org" }
+ ],
+ "subnet4": [{
+ "subnet": "10.10.0.0/16",
+ "pools": [ { "pool": "10.10.1.10 - 10.10.10.254" },
+ { "pool": "10.10.100.10 - 10.10.110.254" }
+ ],
+ "option-data": [
+ { "name": "routers", "data": "10.10.0.1" },
+ { "name": "domain-name-servers", "data": "10.10.0.2, 10.10.0.3" }
+ ],
+ "valid-lifetime": 7200
+ }],
+ ...
+}
+----
+
+These define a network consisting of a single subnet 10.10.0.0/16 containing two
+IP address pools 10.10.1.10 - 10.10.10.254 and 10.10.100.10 - 10.10.110.254.
+Requests that are determined to have originated from this network (e.g. because
+their `giaddr` belongs within the subnet) will be assigned the specified DHCP
+parameters and allocated an address from one of its ranges.
+
+To provide equivalent functionality, FreeRADIUS must identify the correct DHCP
+reply parameters as well as the name of the pool to be used for IP address
+assignment, based on the originating network of the request.
+
+The definition for this pool (the addresses contained within it, corresponding
+to the `range` statement in ISC DHCP and Kea) is specified entirely in the
+database: It is precisely the rows in the `dhcpippool` table with a particular
+`pool_name`.
+
+[TIP]
+====
+As described previously, in FreeRADIUS a pool is a set of IP addresses that are
+equally valid with respect to the network policy; therefore, unlike ISC DHCP
+and ISC Kea, FreeRADIUS does not differentiate between the two `range`s.
+Instead we should have previously populated a single pool containing all of the
+IP addresses from both ranges.
+====
+
+FreeRADIUS derives a request attribute called `DHCP-Network-Subnet` which
+honours the standard DHCP process for designating the choice of network, in
+order of preference:
+
+ 1. Link Selection Suboption of Option 82
+ 2. IPv4 Subnet Selection Option
+ 3. Gateway IP Address ("giaddr")
+ 4. Client IP Address ("ciaddr", only set for unicast packets)
+
+If `DHCP-Network-Subnet` contains an IP address then this should be used as
+the basis of choosing a network. When there is no address in this attribute it
+can be assumed that the packet has been received from a client on the local
+LAN.
+
+The `files` module in FreeRADIUS provides a simple method to map
+`DHCP-Network-Subnet` to the corresponding pool based on its network
+membership, setting the appropriate options to return to clients. It can also
+set the global options.
+
+[TIP]
+====
+In the case where an instance of the `files` module is used to get global
+default parameters, the `dhcp_common` policy becomes redundant so the
+statement calling the policy (by name) can be commented out in
+`<raddb>/sites-enabled/dhcp`.
+====
+
+To use the provided example `files` module instance for DHCP, symlink or copy
+`<raddb>/mods-available/dhcp_files` into `<raddb>/mods-enabled/` and then
+uncomment the calls to `dhcp_network` in `<raddb>/sites-enabled/dhcp`.
+
+A template configuration file `<raddb>/mods-config/files/dhcp` is also
+provided which should be adapted to suit your network topology.
+
+For the configuration above you may deduce the following configuration, which
+has been extended to include an initial default section for requests originating
+from directly-connected clients on the local LAN (192.168.20/24):
+
+[source,config]
+----
+network Pool-Name := "local"
+ DHCP-Domain-Name := "example.org",
+ DHCP-Subnet-Mask := 255.255.255.0,
+ DHCP-Router-Address := 192.168.20.1,
+ DHCP-Domain-Name-Server := 192.168.20.2,
+ Fall-Through := yes
+
+network DHCP-Network-Subnet < 10.10.0.0/16, Pool-Name := "remote"
+ DHCP-Subnet-Mask := 255.0.0.0,
+ DHCP-Router-Address := 10.10.0.1,
+ DHCP-Domain-Name-Server := 10.10.0.2,
+ DHCP-Domain-Name-Server += 10.10.0.3,
+ DHCP-IP-Address-Lease-Time := 7200
+----
+
+Each block in the file starts with a line beginning with the key to be matched.
+In this case the keyword of `network` (defined earlier in `dhcp_networks`
+configuration) is used for each block, so each of the above blocks is a
+candidate during the search.
+
+There may be further filtering of the candidates in the form of `<Attribute>
+<op> <Value>`. In the case of the second block we match the
+`DHCP-Network-Subnet` to an enclosing subnet with
+`DHCP-Network-Subnet < <subnet>`. Additional filters could be added as
+required, comma separated.
+
+Following the filters on the first line, attributes in the `control` list can
+be set using the syntax of `<Attribute> := <Value>`. In this example this is
+used to specify the `Pool-Name` for choosing the appropriate IP pool to
+allocate an address from.
+
+Subsequent indented lines are attribute assignments for values in the `reply`
+list. Note that, apart from the last line, they are all terminated with a
+comma.
+
+The special option `Fall-Through` determines whether, following a match,
+other records are checked for a match. All lookups will match the entry
+with a key of `network` and no further filtering, so `Fall-Through`
+is set on that record in order that the other records will be tested
+to find subnet matches.
+
+=== Example packet processing
+
+For our example, we consider a request arriving from a DHCP relay within
+10.10.0.0/16. In the absence of any specific DHCP subnet selection options in
+the request, the `DHCP-Network-Subnet` attribute is calculated to be the
+relay's IP address, say 10.10.0.1.
+
+The request is matched against the first block, setting an initial pool name to
+"local", domain name to "example.org" and setting some additional global
+default parameters. By virtue of `Fall-Through` being set, the next block is
+considered.
+
+Since the network identifier is within the specified subnet (i.e. `10.10.0.1 <
+10.10.0.0/16`) this second block is matched. This block overrides the pool name
+setting it to "remote", overrides some other global defaults and sets the lease
+time to 7200 seconds. `Fall-Through` is not set, so we are now done with
+deriving the pool name and network options.
+
+When the `dhcp_sqlippool` module is called during DHCP DISCOVER processing (in
+`<raddb>/sites-enabled/dhcp`) the `remote` pool will be used for IP address
+allocation.
+
+The assigned IP address and network parameters will subsequently be returned in
+the DHCP reply.
+
+=== Testing the pool operation and network-specific options
+
+Before proceeding further, you should test the operation of the IP pools and
+ensure that any network-specific reply attributes that you have configured are
+correctly set in replies.
+
+For example, if you have a single, flat pool you should test using sample
+packets for devices with different MAC addresses and/or Client Identifiers.
+
+[source,shell]
+----
+cat <<EOF > dhcp-packet-1.txt
+DHCP-Message-Type := DHCP-Discover
+DHCP-Client-Hardware-Address := 02:01:11:11:11:11
+DHCP-Client-Identifier := device1
+EOF
+----
+
+[source,shell]
+----
+cat <<EOF > dhcp-packet-2.txt
+DHCP-Message-Type := DHCP-Discover
+DHCP-Client-Hardware-Address := 02:01:22:22:22:22
+DHCP-Client-Identifier := device2
+EOF
+----
+
+Generate these packets as show previously using the dhcpclient tool and look
+for `DHCP-Your-IP-Address` in the DHCP responses to determine the IP address
+that has been offered.
+
+Ensure that the DHCP Offer responses contain unique IP addresses. Ensure that
+when these requests are resent within the lifetime of the initial offer that
+the reponses to the subsequent replies contain the original IP address that was
+in the initial offer to the device.
+
+Additionally, ensure that the DHCP Offers contain any network-specific
+parameters that you have specified.
+
+In the case that the policy contains multiple IP pools and network definitions
+for clients belonging to different Layer 2 networks (or indeed belonging to the
+same network but segregated according to some local policy) you should ensure
+that the devices are being mapped to the correct definition.
+
+For a typical policy that selects the IP pool and network options based on the
+originating network for the DHCP packet, explicitly specifying a network by
+including a `DHCP-Subnet-Selection-Option` parameter may avoid the need to test
+from a host within each individual network:
+
+[source,shell]
+----
+cat <<EOF > dhcp-packet-network-10.10.10.0.txt
+DHCP-Message-Type := DHCP-Discover
+DHCP-Client-Hardware-Address := 02:01:aa:bb:cc:dd
+DHCP-Client-Identifier := abc123
+DHCP-Subnet-Selection-Option := 10.10.10.0
+EOF
+----
+
+For policies where the IP pool and network option selection is based on some
+custom criteria it is necessary to include different variations for the
+parameters on which the policy makes the decision. The testing example for the
+class-specific options later in this document provides such an example.
diff --git a/doc/antora/modules/howto/pages/protocols/dhcp/policy_subnet_options.adoc b/doc/antora/modules/howto/pages/protocols/dhcp/policy_subnet_options.adoc
new file mode 100644
index 0000000..1980e89
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/dhcp/policy_subnet_options.adoc
@@ -0,0 +1,184 @@
+== Configure subnet-specific options for shared networks
+
+In the case that shared-networks are in use, with the pool containing
+equally-valid IP addresses from multiple subnets, it is necessary to set the
+subnet-specific parameters such as `DHCP-Router-Address`, `DHCP-Subnet-Mask`
+and `DHCP-Broadcast-Address` based on the IP address that has been allocated.
+
+Consider the ISC DHCP configuration snippet:
+
+[source,iscdhcp]
+----
+option domain-name "example.org";
+
+shared-network bigdept {
+
+ option domain-name-servers 10.10.0.2, 10.10.0.3;
+ default-lease-time 7200;
+
+ subnet 10.30.10.0 netmask 255.255.255.0 {
+ option routers 10.30.10.1;
+ }
+ subnet 10.30.20.0 netmask 255.255.255.0 {
+ option routers 10.30.20.1;
+ }
+ range 10.30.10.10 10.30.10.254;
+ range 10.30.20.10 10.30.20.254;
+
+}
+----
+
+Or the equivalent Kea configuration:
+
+[source,kea]
+----
+"Dhcp4": {
+ "option-data": [
+ { "name": "domain-name", "data": "example.org" }
+ ],
+ "shared-networks": [{
+ "name": "bigdept",
+ "option-data": [
+ { "name": "domain-name-servers", "data": "10.10.0.2, 10.10.0.3" }
+ ],
+ "valid-lifetime": 7200,
+ "subnet4": [{
+ "subnet": "10.30.10.0/24",
+ "pools": [ { "pool": "10.30.10.10 - 10.30.10.254" } ],
+ "option-data": [
+ { "name": "routers", "data": "10.30.10.1" }
+ ]
+ }],
+ "subnet4": [{
+ "subnet": "10.30.20.0/24",
+ "pools": [ { "pool": "10.30.20.10 - 10.30.20.254" } ],
+ "option-data": [
+ { "name": "routers", "data": "10.30.20.1" }
+ ]
+ }]
+ }],
+ ...
+}
+----
+
+As with the network to pool lookup, an instance of the `files` modules can be
+employed (this time after the allocation of an IP address) to set the correct
+reply parameters based on the subnet membership of the assigned address.
+
+To do this, we can use this section of `<raddb>/mods-available/dhcp_files`:
+
+[source,config]
+----
+files dhcp_subnets {
+ filename = ${modconfdir}/files/dhcp
+ key = "subnet"
+}
+----
+
+Additionally, uncomment the `dhcp_subnets` policy in `<raddb>/policy.d/dhcp`.
+This policy wraps the call to the `dhcp_subnets` files module with code that
+"tightens" the `DHCP-Network-Subnet` attribute by setting it to the
+just-allocated IP address.
+
+The relevant entries in the `<raddb>/mods-config/files/dhcp` configuration
+file might then look something like this:
+
+[source,config]
+----
+network
+ DHCP-Domain-Name := "example.org",
+ Fall-Through := yes
+
+network DHCP-Network-Subnet < 10.30.0.0/16, Pool-Name := "bigdept"
+ DHCP-Domain-Name-Server := 10.10.0.2,
+ DHCP-Domain-Name-Server += 10.10.0.3,
+ DHCP-IP-Address-Lease-Time := 7200
+
+subnet DHCP-Network-Subnet < 10.30.10.0/24
+ DHCP-Router-Address := 10.30.10.1
+
+subnet DHCP-Network-Subnet < 10.30.20.0/24
+ DHCP-Router-Address := 10.30.20.1
+----
+
+=== Example packet processing
+
+For our example, we consider a request arriving from a DHCP relay within
+10.30.10.0/24. In the absence of any specific DHCP subnet selection options in
+the request, the `DHCP-Network-Subnet` attribute is calculated to be the
+relay's IP address, say 10.30.10.1.
+
+The request is matched against the first "network" block, setting the domain
+name to "example.org". By virtue of `Fall-Through` being set, the next "network"
+block is considered.
+
+Since the network identifier is within the specified subnet (i.e. `10.30.10.1 <
+10.30.0.0/16`) this second "network" block is matched. This block sets the pool
+name to "bigdept", sets some network-specific DNS resolvers and sets the lease
+time to 7200 seconds. `Fall-Through` is not set, so we are now done with
+deriving the pool name and network options.
+
+When the `dhcp_sqlippool` module is called during DHCP DISCOVER processing (in
+`<raddb>/sites-enabled/dhcp`) the `bigdept` pool will be used for IP address
+allocation.
+
+After IP allocation the `dhcp_subnet` policy and files instance are called.
+Before the subnet options are looked up the `DHCP-Network-Subnet`
+attribute is tightened to match the assigned IP address, say 10.30.20.123.
+
+The request does not match the first subnet block since 10.30.20.123 is not
+within 10.30.10.0/24. However, the request does match the second subnet block
+since `10.30.20.123 < 10.30.20.0/24`. This block sets the default gateway
+reply parameter. `Fall-Through` is not set, so we are now done with deriving
+the pool name and network options.
+
+The assigned IP address, network and subnet parameters will subsequently be
+returned in the DHCP reply.
+
+=== Testing the subnet-specific options
+
+If you have set any subnet-specific reply parameters then you should test these
+before proceeding further.
+
+For example, in the case that you have a single, large pool spanning two IP
+subnets you might want to test by repeatedly allocating addresses using sample
+packets with different MAC addresses, each time checking to ensure that the
+DHCP parameters correspond to the IP address that has been offered.
+
+.Example output from dhcpclient showing a response
+==================================================
+ dhcpclient: ...
+ ...
+ ----------------------------------------------------------------------
+ Waiting for DHCP replies for: 5.000000
+ ----------------------------------------------------------------------
+ ...
+ DHCP-Your-IP-Address = 10.0.10.50
+ DHCP-Router-Address = 10.0.10.1
+ DHCP-Broadcast-Address = 10.0.10.255
+ DHCP-Subnet-Mask = 255.255.255.255
+==================================================
+
+
+.Example output from dhcpclient showing a response
+==================================================
+ dhcpclient: ...
+ ...
+ ----------------------------------------------------------------------
+ Waiting for DHCP replies for: 5.000000
+ ----------------------------------------------------------------------
+ ...
+ DHCP-Your-IP-Address = 10.99.99.50
+ DHCP-Router-Address = 10.99.99.1
+ DHCP-Broadcast-Address = 10.99.99.255
+ DHCP-Subnet-Mask = 255.255.255.255
+==================================================
+
+
+[TIP]
+====
+If the subnets are large then you might want to temporarily reduce their
+size by setting the `status` field of the majority of the rows for each subnet
+to "`disabled`" to cause offers to be made more readily with IP addresses in
+different subnets.
+====
diff --git a/doc/antora/modules/howto/pages/protocols/dhcp/prepare.adoc b/doc/antora/modules/howto/pages/protocols/dhcp/prepare.adoc
new file mode 100644
index 0000000..aa43530
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/dhcp/prepare.adoc
@@ -0,0 +1,59 @@
+== Preparation
+
+It is necessary to consider the requirements for the installation in order to
+devise an efficient and manageable set up.
+
+=== Understand the network topology
+
+When multiple networks (VLANs) are in use consideration must be given to how
+the correct "pool" (IP address ranges) from which to allocate addresses is
+identified.
+
+The policy for setting specific DHCP options (e.g. lease time, default gateway,
+time server and vendor-specific parameters) for different groups of hosts,
+based on their network or some device attributes either supplied in the DHCP
+requests or determined by dynamic lookup, should be well defined and
+understood.
+
+Other DHCP servers may implement implicit assumptions about the requirement of
+your network topology and silently define particular behaviours, such as the
+selection of IP address pool for a request based on a relay address. Some of
+these behaviours must be specifed explicitly when using FreeRADIUS.
+
+=== Choose a database backend
+
+FreeRADIUS stores its leases in an SQL database, so one of the key decisions to
+make is which database to use.
+
+FreeRADIUS supports:
+
+ * SQLite
+ * PostgreSQL
+ * MySQL / MariaDB
+ * Microsoft SQL Server
+ * Oracle
+
+In most configurations the SQL database is likely to be the limiting component
+that restricts the IP allocation throughput of the overall system. Each
+database server has its own performance characteristics and unique approach to
+features such as high-availability.
+
+The choice of database should be made carefully based on the performance and
+high-availability requirements of the system, as well as any prior experience.
+
+[TIP]
+====
+SQLite is an in-process database that uses the local file system, is simple to
+configure and is suitable for smaller installations. However, users with larger
+address pools or high availability requirements should choose one of the other
+standalone databases based on criteria such as performance, features,
+familiarity and your need for commercial support.
+====
+
+FreeRADIUS ships with a default database schema and set of queries for each
+supported database. These are sufficient for most DHCP deployments but can be
+reviewed and modified as required to suit a particular situation, for example
+to customise the IP allocation policy such as by disabling address
+"stickiness".
+
+Now xref:protocols/dhcp/enable.adoc[enable the DHCP service].
diff --git a/doc/antora/modules/howto/pages/protocols/dhcp/test.adoc b/doc/antora/modules/howto/pages/protocols/dhcp/test.adoc
new file mode 100644
index 0000000..322de08
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/dhcp/test.adoc
@@ -0,0 +1,143 @@
+== Testing the DHCP service
+
+We can verify that FreeRADIUS is providing a DHCP service using the
+`dhcpclient` tool that is included with the FreeRADIUS distribution.
+
+Temporarily configure FreeRADIUS to issue a single static IP address to all
+clients by updating the `dhcp DHCP-Discover` section in the `dhcp` virtual
+server to include the following:
+
+[source,unlang]
+----
+update reply {
+ &DHCP-Your-IP-Address := 1.2.3.4
+}
+----
+
+Define a sample DHCP packet as follows:
+
+[source,shell]
+----
+cat <<EOF > dhcp-packet.txt
+DHCP-Message-Type := DHCP-Discover
+DHCP-Client-Hardware-Address := 02:01:aa:bb:cc:dd
+DHCP-Client-Identifier := abc123
+EOF
+----
+
+We can now generate this packet by invoking one of the following commands based
+on the current circumstances...
+
+From the host that is running the FreeRADIUS DHCP server:
+
+[source,shell]
+----
+dhcpclient -i lo 255.255.255.255 -f dhcp-packet.txt -x auto
+----
+
+From a different host with an interface (eth0) in the same broadcast domain
+as the FreeRADIUS DHCP server:
+
+[source,shell]
+----
+dhcpclient -i eth0 255.255.255.255 -f dhcp-packet.txt -x auto
+----
+
+If all of the DHCP broadcast traffic in other Layer 2 networks is converted to
+unicast by DHCP relay agents then it is not necessary for FreeRADIUS to listen
+on a broadcast address. In this case you can test DHCP using a unicast request:
+
+[source,shell]
+----
+dhcpclient 192.0.2.10 -f dhcp-packet.txt -x auto
+----
+
+[NOTE]
+====
+In order for the returned, unicast DHCP OFFER to be received it is necessary to
+ensure that the `DHCP-Your-IP-Address` parameter set by FreeRADIUS matches an
+address on the interface used by the dhcpclient tool to send the Discover
+packet.
+====
+
+When one of the above commands is run, the tool with generate output such as
+the following which shows that the packet was sent and that it is now waiting
+for replies:
+
+.Example output from dhcpclient showing the request
+===================================================
+ dhcpclient: ...
+ ----------------------------------------------------------------------
+ DHCP-Opcode = 0x01
+ DHCP-Hardware-Type = 0x01
+ DHCP-Hardware-Address-Length = 0x06
+ DHCP-Hop-Count = 0x00
+ DHCP-Transaction-Id = 0x5e0bbfab
+ DHCP-Number-of-Seconds = 0x0000
+ DHCP-Flags = 0x0000
+ DHCP-Client-IP-Address = 0x00000000
+ DHCP-Your-IP-Address = 0x00000000
+ DHCP-Server-IP-Address = 0x00000000
+ DHCP-Gateway-IP-Address = 0x00000000
+ ...
+ ----------------------------------------------------------------------
+ Waiting for DHCP replies for: 5.000000
+ ----------------------------------------------------------------------
+===================================================
+
+
+Each received DHCP response will generate output such as the following:
+
+.Example output from dhcpclient showing a response
+==================================================
+ ...
+ ----------------------------------------------------------------------
+ DHCP-Opcode = Server-Message
+ DHCP-Hardware-Type = Ethernet
+ DHCP-Hardware-Address-Length = 6
+ DHCP-Hop-Count = 0
+ DHCP-Transaction-Id = 1577828267
+ DHCP-Number-of-Seconds = 0
+ DHCP-Flags = 0
+ DHCP-Client-IP-Address = 0.0.0.0
+ DHCP-Your-IP-Address = 1.2.3.4
+ DHCP-Server-IP-Address = 192.0.2.10
+ DHCP-Gateway-IP-Address = 0.0.0.0
+ DHCP-Client-Hardware-Address = 02:42:0a:00:00:0b
+ DHCP-Message-Type = DHCP-Offer
+ DHCP-Client-Identifier = 0x616263313233
+ Waiting for additional DHCP replies for: 4.999429
+ ...
+==================================================
+
+Examine the DHCP response to ensure that it has the correct message type
+(`DHCP-Offer`, in this case), contains the temporary IP address that you
+configured earlier, i.e. `DHCP-Your-IP-Address = 1.2.3.4`, and any other
+expected reply parameters (which we configure later). You should also carefully
+examine the output of a FreeRADIUS debug session (`radius -X`) to ensure that
+the policy is being executed in the way that you expect and that no warnings
+are being generated.
+
+You can now change the content of the sample DHCP request by editing the
+`dhcp-packet.txt` file and re-run the above command to see the server's reply.
+You should examine the DHCP dictionary distrubuted with FreeRADIUS (usually
+`/usr/share/freeradius/dictionary.dhcp`) which provides the list of all of the
+DHCP parameters ("attributes") understood by FreeRADIUS.
+
+[WARNING]
+====
+When you are done **remember** to remove the temporary edit that was made to
+the `dhcp` virtual server that provides the static IP assignment.
+====
+
+=== Testing the DHCP policy
+
+The remainder of this guide describes how to configure the IP address plan,
+setup the IP pools and define a DHCP policy. You should develop your policy by
+making small, incremental changes to the provided configuration and then test
+those changes with the approach described above, using `dhcpclient` and `radius -X`,
+modifying the sample DHCP packet as required. If you break the policy then
+revert the last change, attempt to understand what went wrong, and try
+something else.
+
+Now xref:protocols/dhcp/policy.adoc[define the DHCP policy].
diff --git a/doc/antora/modules/howto/pages/protocols/proxy/enable_proxy_protocol.adoc b/doc/antora/modules/howto/pages/protocols/proxy/enable_proxy_protocol.adoc
new file mode 100644
index 0000000..b689824
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/proxy/enable_proxy_protocol.adoc
@@ -0,0 +1,114 @@
+== Enabling PROXY Protocol
+
+Now that we have a working configuration which used RadSec and HAproxy
+or Traefik, we are finally ready to enable PROXY Protocol.
+
+Configure FreeRADIUS on the `radsecsvr` host to expect the PROXY
+Protocol for RadSec connections. This is done by editing the `listen
+{}` section of the `tls` virtual server to include a reference to the
+proxy protocol:
+
+.Enabling PROXY Protocol in a FreeRADIUS virtual server
+=======================================================
+
+ listen {
+ ...
+ proxy_protocol = true
+ ...
+ }
+
+=======================================================
+
+Now restart the debugging session:
+[source,shell]
+----
+radiusd -fxxl /dev/stdout
+----
+
+
+For HAproxy, you should enable the PROXY Protocol on connections to
+the RadSec backend, by editing the `backend` definition to add a
+`send-proxy` argument:
+
+.Example HAproxy backend configuration with PROXY Protocol
+==========================================================
+
+ backend radsec_be
+ mode tcp
+ balance roundrobin
+ server radsecsvr 172.23.0.3:2083 send-proxy
+
+==========================================================
+
+Note the `send-proxy` argument in the `server` definition.
+
+Now reload the HAproxy service:
+
+[source,shell]
+----
+service haproxy reload
+---
+
+
+For Traefik, enable the PROXY Protocol on connections to the RadSec
+backend by editing the `radsec-service` definition to add a reference
+to the proxy protocol"
+
+.Example Traefik service configuration with PROXY Protocol
+==========================================================
+
+ radsec-service:
+ loadBalancer:
+ servers:
+ - address: "172.23.0.3:2083"
+ proxyProtocol:
+ version: 1
+
+==========================================================
+
+Note the `proxyProtocol` and `version: 1` directives.
+
+Traefik should automatically detect the updates and reconfigure the
+service.
+
+
+=== Testing RadSec connectivity via a proxy using PROXY Protocol
+
+Finally, with your test client configured to use the proxy, perform a
+test authentication:
+
+[source,shell]
+----
+ echo "User-Name = bob" | radclient 127.0.0.1 auth testing123
+----
+
+You should expect to see the familiar output:
+
+.Example output from radclient
+==============================
+
+ Sent Access-Request Id 252 from 0.0.0.0:50118 to 127.0.0.1:1812 length 27
+ Received Access-Accept Id 252 from 127.0.0.1:1812 to 127.0.0.1:50118 length 39
+
+==============================
+
+Now examine the FreeRADIUS debug output on the RadSec server:
+
+.Expected output from `radiusd -X` with PROXY Protocol
+======================================================
+
+ ...
+ (0) (TLS) Received PROXY protocol connection from client \
+ 172.23.0.2:55343 -> 172.23.0.4:2083, via proxy 172.23.0.4:40268 -> 0.0.0.0:2083
+ ...
+ (0) Received Access-Request Id 227 from 172.23.0.2:55343 to 172.23.0.4:2083 length 49
+ (0) Sent Access-Accept Id 227 from 172.23.0.4:2083 to 172.23.0.2:55343 length 0
+ ...
+
+======================================================
+
+The output indicates that FreeRADIUS is receiving the originating
+connection information from the PROXY Protocol. FreeRADIUS then
+handles the RadSec requests as though they have been received directly
+from the originating client.
+
diff --git a/doc/antora/modules/howto/pages/protocols/proxy/enable_radsec.adoc b/doc/antora/modules/howto/pages/protocols/proxy/enable_radsec.adoc
new file mode 100644
index 0000000..f5e7603
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/proxy/enable_radsec.adoc
@@ -0,0 +1,188 @@
+== Enabling RadSec with FreeRADIUS
+
+Our first task is to set up a RadSec server by configuring an instance of
+FreeRADIUS to accept RADIUS over TLS requests.
+
+The following steps should be performed on the host which will be the
+RadSec server, we will call it `radsecsvr`.
+
+You can install FreeRADIUS using the NetworkRADIUS packages by
+following the instructions provided here:
+
+<https://networkradius.com/packages/>
+
+Before making any configuration changes, you should stop the radiusd
+service:
+
+[source,shell]
+----
+ service radiusd stop
+----
+
+Then, enable the `tls` virtual server:
+
+[source,shell]
+----
+cd /etc/raddb/sites-enabled
+ln -s ../sites-available/tls
+----
+
+The FreeRADIUS distribution contains an example Certificate Authority
+that will have generated the necessary CA, server and client
+certificates and keys during package installation. You can use this
+CA, or you can use your own CA and certificates.
+
+[TIP]
+====
+If the example certificates are not present (for example if FreeRADIUS was
+installed from source) then FreeRADIUS will fail to start. The files can be
+regenerated by running `make` in the `/etc/raddb/certs` directory.
+====
+
+Edit the `tls` virtual server configuration, in order to add
+definitions for the clients by extending the `clients radsec {}` section:
+
+.Example radsec client definitions in `/etc/raddb/sites-available/tls`
+====
+
+ clients radsec {
+ ...
+ # Direct connections from the test client
+ client radseccli {
+ ipaddr = 172.23.0.2
+ proto = tls
+ virtual_server = default
+ }
+ # Connections via HAproxy
+ client haproxy {
+ ipaddr = 172.23.0.4
+ proto = tls
+ virtual_server = default
+ }
+ # Connections via Traefik
+ client traefik {
+ ipaddr = 172.23.0.5
+ proto = tls
+ virtual_server = default
+ }
+ }
+
+====
+
+The client `ipaddr` configuration item is used to match the source IP
+address of incoming connections. You must add client definitions for
+each of the clients which will connect.
+
+For RadSec, you can just list the IP address of the RadSec client.
+This client definition is used for processing RADIUS packets from the
+RadSec client.
+
+[NOTE]
+====
+A `secret` does not have to be specified for RadSec clients, as the
+default is `radsec`. If you specify a secret, then that will be used
+instead of `radsec`.
+====
+
+When the PROXY protocol is used, you must _also_ define a client which
+matches the IP address of the proxy (haproxy, etc). This client is
+only used to check that the source IP is permitted to connect to the
+server. Fields other than `ipaddr` can be specified (and in some
+cases may be required). However, all other fields will be ignored.
+
+For testing purposes, we want to amend the `default` virtual server so
+that it accepts all authentication reqeusts and immediately responds
+to accounting requests.
+
+Edit the `/etc/raddb/sites-enabled/default` file so that the beginning of
+the `authorize` and `preacct` sections looks as follows:
+
+.Example default virtual server modification to unconditionally accept Access-Requests
+====
+
+ authorize {
+ accept
+ ...
+ }
+ ...
+ preacct {
+ handled
+ ...
+ }
+
+====
+
+This change makes the `authorize` section always "accept" the user,
+and makes the `preacct` section always say "we handled the accounting
+request". These changes are only for testing, and should never be
+used in production.
+
+Start the FreeRADIUS service in the foreground with debugging enabled:
+
+[source,shell]
+----
+radiusd -fxxl /dev/stdout
+----
+
+Examine the output from FreeRADIUS to ensure that it is now listening for
+RadSec connection on TCP/2083:
+
+.Example output from running `radiusd -fxxl /dev/stdout`
+====
+
+ FreeRADIUS Version 3.0.24
+ Copyright (C) 1999-2021 The FreeRADIUS server project and contributors
+ ...
+ ... : Debug: Listening on auth+acct proto tcp address * port 2083 (TLS) bound to server default
+ ... : Debug: Listening on auth address * port 1812 bound to server default
+ ... : Debug: Listening on acct address * port 1813 bound to server default
+ ... : Debug: Listening on auth address :: port 1812 bound to server default
+ ... : Debug: Listening on acct address :: port 1813 bound to server default
+ ...
+ ... : Info: Ready to process requests
+
+====
+
+FreeRADIUS is now ready to process RadSec traffic.
+
+For testing, we first test normal RADIUS over UDP functionality, then
+the RadSec connection using a test client, then introduce a proxy
+server, and finally we enable PROXY Protocol. Doing the tests in this
+way ensures that we know that all previous steps work before trying
+the next step. This process allows us to quickly narrow down
+problems, and gets us to the final goal _faster_ than just "doing
+everything all at once".
+
+=== Testing the RADIUS policy
+
+Before moving on, verify that the FreeRADIUS policy is able to
+authenticate a local test RADIUS Access-Request over UDP:
+
+[source,shell]
+----
+echo "User-Name = terry" | radclient 127.0.0.1 auth testing123
+----
+
+Due to the `accept` we added in the `authorize` section, the expected
+output should be an Access-Accept:
+
+.Expected output from radclient
+===============================
+
+ Sent Access-Request Id 157 from 0.0.0.0:36850 to 127.0.0.1:1812 length 27
+ Received Access-Accept Id 157 from 127.0.0.1:1812 to 127.0.0.1:36850 length 20
+
+===============================
+
+Any other output indicates that there is a problem with the FreeRADIUS
+configuration which *must* be solved before testing RadSec. Carefully verify that
+you have carried out each of the above steps correctly and examine the debug
+output from FreeRADIUS, which will usually tell you what is wrong.
+
+See [how to read the debug
+output](http://wiki.freeradius.org/radiusd-X) for instructions on
+reading amd understanding the debug output.
+
+The next step is to xref:protocols/proxy/radsec_client.adoc[configure
+FreeRADIUS as a RadSec test client] so that we can verify that our
+RadSec server is working.
diff --git a/doc/antora/modules/howto/pages/protocols/proxy/index.adoc b/doc/antora/modules/howto/pages/protocols/proxy/index.adoc
new file mode 100644
index 0000000..5100635
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/proxy/index.adoc
@@ -0,0 +1,126 @@
+= Proxying RadSec and enabling PROXY Protocol
+
+This guide shows how to set up FreeRADIUS to serve RadSec connections, fronted
+by either HAproxy or Traefik as Layer 4 proxies that pass on the original
+client connection information using PROXY Protocol.
+
+It is not a comprehensive guide to using RadSec with FreeRADIUS. It presents a
+basic configuration that uses an example CA and does not validate certificate
+attributes or perform revokation status.
+
+
+== Introduction
+
+FreeRADIUS supports receiving RADIUS requests over TLS-enabled TCP connections
+and supports proxying of requests over TCP connections to another TLS-enabled
+homeserver. The protocol for RADIUS over TLS is called "RadSec" and is defined
+in RFC 6614.
+
+FreeRADIUS is a capable and performant application-aware ("Layer 7") proxy /
+load-balancer for RadSec and other forms of RADIUS traffic.
+
+
+=== Layer 4 proxying
+
+Rather than use an application-aware proxy it is sometimes better to reduce the
+performance impact incurred by re-encoding an application protocol by using a
+"Layer 4" proxy that operates at the level of individual connections without
+regard for the application protocol. Such a proxy is more of a "bump in the
+wire" than a request buffer and minimises the latency incurred due to proxying.
+
+It is common to see software such as HAproxy and Traefik used in Layer 4 mode
+in place of FreeRADIUS for purposes such as connection load balancing. In
+addition to improved performance, these tools have the benefit that they
+typically support dynamic service discovery and "hitless" reloads to
+automatically adapt their connection routing based on changes to backend
+services such as the introduction of new nodes with even a momentary loss of
+service.
+
+
+=== Loss of connection information
+
+When TCP connections are relayed through Layer 4 proxies the information
+about the originating source of the connection is no longer known to the
+backend service, unless it is otherwise made available. Identifying the
+originator of connections is often necessary for security purposes and for
+request processing.
+
+Whilst many application protcols support headers that allow proxies to preserve
+connection information these are not helpful in the context of Layer 4
+proxying: The process of populating headers requires knowledge of the
+application protocol to re-encode requests as they are transmitted between the
+frontend and backend connections.
+
+
+=== PROXY Protocol
+
+PROXY Protocol overcomes this limitation by allowing the original connection
+information to be provided to the backend at the start of the TCP connection.
+After this initial data is encoded the remainder of the conversation then
+proceeds as normal. However now that the connection information is known to the
+backend server it is able to process requests made on the connection as though
+the connection were being made directly by the client and not via the proxy.
+
+PROXY Protocol is specified in this document:
+http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
+
+
+== Requirements
+
+PROXY Protocol Version 1 is supported by FreeRADIUS v3.0.24 and later versions.
+
+You will require the following set of VMs or containers, each with their own
+IP address:
+
+[cols="1,1,1"]
+|===
+|Hostname|IP address|Purpose
+
+|radseccli
+|172.23.0.2
+|FreeRADIUS configured to provide a RadSec test client
+
+|radsecsvr
+|172.23.0.3
+|FreeRADIUS configured as a RadSec server
+
+|haproxy
+|172.23.0.4
+|HAproxy in Layer 4 mode to the FreeRADIUS RadSec backend
+|===
+
+Optionally you may want to configure a host to run Traefik within a Docker
+container using host mode networking, perhaps configured by Docker Compose,
+however the installation is beyond the scope of this guide:
+
+[cols="1,1,1"]
+|===
+|traefik
+|172.23.0.5
+|Traefik configured as a TCP router with TLS passthrough to the FreeRADIUS RadSec backend
+|===
+
+The hostnames and IP addresses provided above are for examples purposes and are
+used throughout the remainder of this guide. This guide provides commands and
+output for CentOS. Other distributions will have minor differences, including
+the location of the FreeRADIUS configuration (the "raddb").
+
+[NOTE]
+====
+You can choose to use your own hostname, IP addresses and OS distribution. You
+could also use official Docker images provided by the respecitive projects,
+however these prescribe methods for configuring and managing the services
+that are not typical for a normal package installation which would provide a
+distraction if used for by guide.
+====
+
+
+== Sections in this guide
+
+This guide is organised into four parts that should be read in order:
+
+1. xref:protocols/proxy/enable_radsec.adoc[Enabling RadSec]
+2. xref:protocols/proxy/radsec_client.adoc[Configuring a test RadSec client]
+3. xref:protocols/proxy/radsec_with_haproxy.adoc[Proxying RadSec with HAproxy]
+4. xref:protocols/proxy/radsec_with_traefik.adoc[Proxying RadSec with Traefik]
+5. xref:protocols/proxy/enable_proxy_protocol.adoc[Enabling PROXY Protocol for RadSec]
diff --git a/doc/antora/modules/howto/pages/protocols/proxy/radsec_client.adoc b/doc/antora/modules/howto/pages/protocols/proxy/radsec_client.adoc
new file mode 100644
index 0000000..d92345e
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/proxy/radsec_client.adoc
@@ -0,0 +1,181 @@
+== Configuring FreeRADIUS as a RadSec test client
+
+Unfortunately, the `radclient` program does not support RadSec. We
+must therefore configure an instance of FreeRADIUS as a "transport
+converter" which proxies UDP-based RADIUS requests to a RadSec
+destination of our choice.
+
+The following steps should be performed on a client system, which we
+will call `radseccli`. This system should be a new system, with a
+different IP address. That is, you shoudl not edit the configuration
+on the `radsecsvr` host. Doing so will break the RadSec configuration.
+
+Install FreeRADIUS using the NetworkRADIUS packages by following the
+instructions provided here:
+
+<https://networkradius.com/packages/>
+
+Before making any configuration changes, you should stop the radiusd
+service:
+
+[source,shell]
+----
+ service radiusd stop
+----
+
+Add a new `tls` home server definition, which will point to the RadSec
+server. We do this by creating a file
+`/etc/raddb/sites-enabled/radsec-homeserver` with the following
+contents:
+
+.Example homeserver, pool and realm definitions for the RadSec service
+====
+
+ home_server tls {
+ ipaddr = 172.23.0.3 # IP address of our RadSec server
+ port = 2083
+ type = auth+acct
+ proto = tcp
+ tls {
+ private_key_password = whatever
+ private_key_file = ${certdir}/client.pem
+ certificate_file = ${certdir}/client.pem
+ ca_file = ${cadir}/ca.pem
+ }
+ }
+ home_server_pool tls {
+ type = fail-over
+ home_server = tls
+ }
+ realm tls {
+ auth_pool = tls
+ acct_pool = tls
+ }
+
+====
+
+[TIP]
+====
+Complete descriptions of each of the above configuration items can be found in the
+`[raddb]/sites-available/tls` example configuration file. For simple tests, however,
+we can omit all of the comments from the file.
+====
+
+To use this `tls` home server, we change the `default` virtual server to proxy
+all authentication and accounting requests to it.
+
+Edit the `/etc/raddb/sites-enabled/default` file so that the beginning of
+the `authorize` and `preacct` sections looks as follows:
+
+.Example default virtual server modification to proxy requests to a RadSec proxy server
+====
+
+ authorize {
+ update control {
+ &Proxy-To-Realm := tls
+ }
+ handled
+ ...
+ }
+ ...
+ preacct {
+ update control {
+ &Proxy-To-Realm := tls
+ }
+ handled
+ ...
+ }
+
+====
+
+These changes make the `tls` virtual server always proxy packets.
+These changes are only for testing, and should never be used in
+production.
+
+We must now copy the example CA certificate as well as the client
+certificate and key files which are on the `radsecsrv` host to this
+test client.
+
+Replace the following files on `radseccli` with the equivalent files from
+`radsecsrv`:
+
+[cols="1,1,1"]
+|===
+|File|Corresponding configuration item|Purpose
+
+|/etc/raddb/certs/ca.pem
+|`ca_file`
+|CA certificate which is used to authenticate the server certificate presented by the RadSec server to the client.
+
+|/etc/raddb/certs/client.pem
+|`certificate_file`
+|Client certificate (signed by the CA certificate) that is presented by the test client to the RadSec server.
+
+|/etc/raddb/certs/client.pem
+|`private_key_file` and `private_key_password`
+|Private key corresponding to the client certificate
+|===
+
+Note that the client certificate and key are typically bundled into a single file.
+
+[CAUTION]
+====
+If you do not correctly replace the CA, client certificate, and key
+material on the test client then the RadSec client and RadSec server
+will fail to mutually authenticate each other as they do not share a
+trusted CA. If you see messages like `unknown CA`, then you know that
+the certificates have not been set up correctly.
+====
+
+Start the FreeRADIUS service in debug mode:
+
+[source,shell]
+----
+radiusd -X
+----
+
+
+=== Testing RadSec connectivity
+
+At this stage you should be able to cause the test client to send RadSec
+requests directly to the RadSec server.
+
+Run the following to send a RADUS (UDP) Access-Request to the local FreeRADIUS
+instance. It should then proxy the request over RadSec connection to
+the remote RadSec server:
+
+[source,shell]
+----
+ echo "User-Name = bob" | radclient 127.0.0.1 auth testing123
+----
+
+If the test client is able to successfully establish the RadSec
+connection, and the RadSec server replies with an Access-Accept
+response, then the output will be as follows:
+
+.Expected output from radclient
+===============================
+
+ Sent Access-Request Id 252 from 0.0.0.0:50118 to 127.0.0.1:1812 length 27
+ Received Access-Accept Id 252 from 127.0.0.1:1812 to 127.0.0.1:50118 length 39
+
+===============================
+
+Lack of response or an Access-Reject response indicates that the RadSec
+connection is not being established successfully.
+
+There may be serveral reasons for broken connectivity including:
+
+ * The client not accepting the certificate presented by the server.
+ * The server not accepting the certificate presented by the client.
+
+Look at the debug output generated by both the test client and the RadSec
+server. In many cases it will tell you exactly what the problem is.
+
+Do not proceed with any further steps until direct connections between the
+RadSec client and Radsec Server are working properly.
+
+Once things are working we are ready to
+xref:protocols/proxy/radsec_with_haproxy.adoc[configure HAproxy to proxy RadSec
+connections] or to xref:protocols/proxy/radsec_with_traefik.adoc[configure
+Traefik to proxy RadSec connections].
diff --git a/doc/antora/modules/howto/pages/protocols/proxy/radsec_with_haproxy.adoc b/doc/antora/modules/howto/pages/protocols/proxy/radsec_with_haproxy.adoc
new file mode 100644
index 0000000..e58abfe
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/proxy/radsec_with_haproxy.adoc
@@ -0,0 +1,134 @@
+== Proxying RadSec with HAproxy
+
+This section shows how to configure HAproxy to proxy RadSec connections.
+
+The following steps should be performed on the `haproxy` host, unless otherwise
+stated.
+
+Install the HAproxy package supplied with the OS distribution:
+
+[source,shell]
+----
+ yum install haproxy
+----
+
+Stop the haproxy service:
+
+[source,shell]
+----
+ service haproxy stop
+----
+
+Modify the haproxy configuration (typically `/etc/haproxy/haproxy.conf`) so
+that it includes new frontend and backend configuration for the radsec service:
+
+.Example minimal HAproxy configuration
+======================================
+
+ global
+ maxconn 100
+ defaults
+ mode tcp
+ timeout connect 10s
+ timeout client 30s
+ timeout server 30s
+ frontend radsec_fe
+ bind *:2083
+ default_backend radsec_be
+ backend radsec_be
+ balance roundrobin
+ server radsecsvr 172.23.0.3:2083
+
+======================================
+
+Note the `mode tcp` directive which tells HAproxy to act as a Layer 4
+proxy, so that it doesn't attempt to perform SSL termination or
+decode the RADIUS protocol.
+
+[NOTE]
+====
+The above example is a minimal configuration. In practise you will want to
+retain many of the HAproxy configuration items already present in the
+configuration (e.g. `log`, `chroot`, `user`, `group`), but these vary across
+distributions. Other HTTP-related options that may already exist in the
+configuration will conflict with `mode tcp` (Layer 4 proxying) and should be
+removed if HAproxy complains about them.
+
+However, you should first get things working with the minimal
+configuration which is known to work, and then make customisations.
+If you start off with a complex configuration, then there may be a
+large number of things which are broken, and debugging them all will
+be difficult. Start simple, and then add complexity!
+====
+
+Restart the haproxy service in foreground mode for debugging purposes:
+
+[source,shell]
+----
+haproxy -f /etc/haproxy/haproxy.cfg -db
+----
+
+
+=== Testing RadSec connectivity via HAproxy
+
+Now edit the test RadSec client, so that instead of making connections directly
+to the RadSec server it makes connections to the HAproxy server.
+
+On `radseccli` edit the `/etc/raddb/sites-enabled/tls` file, and set
+the IP address to the address of the `haproxy` host.
+
+.Example updated test client homeserver configuration
+=====================================================
+
+ home_server tls {
+ ipaddr = 172.23.0.4 # Updated from radsecsvr to haproxy
+ ...
+ }
+
+=====================================================
+
+Restart the debug mode session:
+
+[source,shell]
+----
+radiusd -X
+----
+
+Perform a test authentication:
+
+[source,shell]
+----
+ echo "User-Name = bob" | radclient 127.0.0.1 auth testing123
+----
+
+If the test client is able to successfully establish the RadSec
+connection via HAproxy, and the RadSec server replies with an
+Access-Accept response, then the output will be as follows:
+
+.Expected output from radclient
+===============================
+
+ Sent Access-Request Id 252 from 0.0.0.0:50118 to 127.0.0.1:1812 length 27
+ Received Access-Accept Id 252 from 127.0.0.1:1812 to 127.0.0.1:50118 length 39
+
+===============================
+
+HAproxy should also log a message that indicates that the connection was
+proxied, such as the following:
+
+.Expected output from HAproxy
+=============================
+
+ <150>...: Connect from 172.23.0.2:50087 to 172.23.0.4:2083 (radius_fr/TCP)
+
+=============================
+
+Any other output from radclient or HAproxy indicates that there is a
+problem with the HAproxy configuration, or that FreeRADIUS is not
+accepting connection from the `haproxy` host, which must be solved
+before continuing.
+
+Once proxied connections are working we are ready to
+xref:protocols/proxy/enable_proxy_protocol.adoc[enable the PROXY
+Protocol] on both HAproxy and the RadSec server.
+
diff --git a/doc/antora/modules/howto/pages/protocols/proxy/radsec_with_traefik.adoc b/doc/antora/modules/howto/pages/protocols/proxy/radsec_with_traefik.adoc
new file mode 100644
index 0000000..11030e9
--- /dev/null
+++ b/doc/antora/modules/howto/pages/protocols/proxy/radsec_with_traefik.adoc
@@ -0,0 +1,128 @@
+== Proxying RadSec with Traefik
+
+This section shows how to configure Traefik to proxy RadSec connections. You
+should skip this section if you are not using Traefik as your proxy.
+
+Installing Traefik is beyond the scope of this guide. It is typically installed
+as a service mesh router within a Docker or Kubernetes environment using
+offical Docker images.
+
+Traefik configuration has two components of interest:
+
+ * Static configuration: Defines "entrypoints" on which Traefik listens for connections.
+ * Dynamic configuration: Defines backend service components and the routing policy.
+
+Traefik supports a number of providers of dynamic configuration data for the
+router and service definitions. For demonstration purposes the files provider
+is used here, however you can switch to another provide once you have things
+working using this method.
+
+The static configuration can be provided by starting Traefik with the following
+arguments:
+
+.Example Traefik static configuration
+=====================================
+
+ traefik \
+ --log.level=DEBUG \
+ --providers.file.filename=/etc/traefik/dynamic_config.yml
+ --providers.file.watch=true
+ --entryPoints.radsec.address=:2083
+
+=====================================
+
+Note that a `radsec` entrypoint is defined to listen on port 2083 and that a
+static `file` provider is used to defined the dynamic services.
+
+The backend for RadSec should be defined in this file as follows:
+
+.Example Traefik dynamic configuration
+======================================
+
+ tcp:
+ routers:
+ radsec-router:
+ entryPoints:
+ - radsec
+ rule: "HostSNI(`*`)"
+ service: "radsec-service"
+ tls:
+ passthrough: true
+ services:
+ radsec-service:
+ loadBalancer:
+ servers:
+ - address: "172.23.0.3:2083"
+
+======================================
+
+Note the `passthrough: true` directive under `tls:` which tells Treafik not to
+attempt TLS termination which it would otherwise perform for all incoming TLS
+connections. We require that the connection is passed through from the RadSec
+client to the RadSec server without being reterminated since the end client's
+certificate is authenticated by the RadSec server and many be used for
+policy decisions.
+
+
+=== Testing RadSec connectivity via Traefik
+
+Now amend the test RadSec client so that instead of making connections directly
+to the RadSec server it makes them via Traefik.
+
+On `radseccli` amend `/etc/raddb/sites-enabled/tls` and set the IP address to
+that of the `traefik` host.
+
+.Example updated test client homeserver configuration
+=====================================================
+
+ home_server tls {
+ ipaddr = 172.23.0.5 # Updated from radsecsvr to traefik
+ ...
+ }
+
+=====================================================
+
+Restart the debug mode session:
+
+[source,shell]
+----
+radiusd -X
+----
+
+Perform a test authentication:
+
+[source,shell]
+----
+ echo "User-Name = bob" | radclient 127.0.0.1 auth testing123
+----
+
+If the test client is able to successfully establish the RadSec connection via
+Traefik and the RadSec server replies with an Access-Accept response then the
+output will be as follows:
+
+.Example output from radclient
+==============================
+
+ Sent Access-Request Id 252 from 0.0.0.0:50118 to 127.0.0.1:1812 length 27
+ Received Access-Accept Id 252 from 127.0.0.1:1812 to 127.0.0.1:50118 length 39
+
+==============================
+
+Traefik should also log a message that indicates that the connection was
+proxied, such as the following:
+
+.Example output from Traefik
+============================
+
+ time="..." level=debug msg="Handling connection from 172.23.0.2:57367"
+
+============================
+
+Any other output from radclient or Traefik indicates that there is a problem
+with the Traefik configuration or that FreeRADIUS is not accepting connection
+from the `traefik` host, which must be solved before continuing.
+
+Once proxied connections are working we are ready to
+xref:protocols/proxy/enable_proxy_protocol.adoc[enable the PROXY Protocol] on
+both Traefik and the RadSec server.
+
diff --git a/doc/antora/modules/installation/nav.adoc b/doc/antora/modules/installation/nav.adoc
new file mode 100644
index 0000000..26ce32e
--- /dev/null
+++ b/doc/antora/modules/installation/nav.adoc
@@ -0,0 +1,5 @@
+* xref:index.adoc[Installing and upgrading]
+** xref:packages.adoc[Install from packages]
+** xref:dependencies.adoc[Dependencies]
+** xref:source.adoc[Build from source]
+** xref:upgrade.adoc[Upgrading to v3]
diff --git a/doc/antora/modules/installation/pages/dependencies.adoc b/doc/antora/modules/installation/pages/dependencies.adoc
new file mode 100644
index 0000000..e910e76
--- /dev/null
+++ b/doc/antora/modules/installation/pages/dependencies.adoc
@@ -0,0 +1,58 @@
+= FreeRADIUS Dependencies
+
+Some external dependencies must be installed before building or
+running FreeRADIUS. The core depends on two mandatory libraries:
+`libtalloc` for memory management and `libkqueue` for event
+handling.
+
+Many of the modules also have optional dependencies. For example,
+the LDAP module requires LDAP client libraries to be installed
+and database modules need their respective database client
+libraries.
+
+If building from source code, the configure stage will check for
+the optional dependencies. Any missing libraries will cause that
+particular module to be skipped.
+
+== Libraries
+
+=== libtalloc
+
+Talloc is a memory allocation library available at
+https://talloc.samba.org/talloc/doc/html/index.html
+
+*OSX*
+
+`# brew install talloc`
+
+*Debian, Ubuntu and `dpkg`-based systems*
+
+`# apt-get install libtalloc-dev`
+
+*RedHat or CentOS*
+
+```
+# subscription-manager repos --enable rhel-7-server-optional-rpms
+# yum install libtalloc-dev
+```
+
+=== kqueue
+
+Kqueue is an event / timer API originally written for BSD systems.
+It is _much_ simpler to use than third-party event libraries. A
+library, `libkqueue`, is available for Linux systems.
+
+*OSX*
+
+_kqueue is already available, there is nothing to install._
+
+*Debian, Ubuntu and `dpkg`-based systems*
+
+`# apt-get install libkqueue-dev`
+
+*RedHat or CentOS*
+
+```
+# subscription-manager repos --enable rhel-7-server-optional-rpms
+# yum install libkqueue-dev
+```
diff --git a/doc/antora/modules/installation/pages/index.adoc b/doc/antora/modules/installation/pages/index.adoc
new file mode 100644
index 0000000..b810078
--- /dev/null
+++ b/doc/antora/modules/installation/pages/index.adoc
@@ -0,0 +1,15 @@
+== Installation
+
+FreeRADIUS is available from multiple sources:
+
+* Official xref:packages.adoc[Network RADIUS packages]
+* xref:source.adoc[Source code]
+* Many Operating System distributions
+
+We highly recommend using the official packages from Network
+RADIUS, where available.
+
+The documents in this section cover details of the above
+installation methods, as well as instructions on building
+packages locally.
+
diff --git a/doc/antora/modules/installation/pages/packages.adoc b/doc/antora/modules/installation/pages/packages.adoc
new file mode 100644
index 0000000..ffc52cd
--- /dev/null
+++ b/doc/antora/modules/installation/pages/packages.adoc
@@ -0,0 +1,22 @@
+== Install from packages
+
+Network RADIUS provide pre-built binary packages of FreeRADIUS for
+common Linux distributions. This is the recommended installation
+method when packages are available for your system.
+
+The official http://packages.networkradius.com[Network RADIUS
+packages] page contains recent FreeRADIUS packages and
+installation instructions.
+
+=== Distribution-supplied packages
+
+While many Operating System distributions ship FreeRADIUS
+packages, the versions they include are often years out of date.
+As well as missing out on the latest bug fixes and features, this
+also means that it is very hard to know if an issue encountered is
+still a problem or if it is fixed in the latest release.
+
+Therefore, whilst the distribution-supplied packages can often be
+the most convenient to install, we do not usually recommend using
+them.
+
diff --git a/doc/antora/modules/installation/pages/source.adoc b/doc/antora/modules/installation/pages/source.adoc
new file mode 100644
index 0000000..cf40a79
--- /dev/null
+++ b/doc/antora/modules/installation/pages/source.adoc
@@ -0,0 +1,199 @@
+== Building from Source
+
+We recommend xref:packages.adoc[installing from packages] if
+possible. Full instructions on building and installing from source
+code follow.
+
+The mandatory xref:installation:dependencies.adoc[dependencies]
+must be installed before FreeRADIUS can be built. These dependencies
+are `libtalloc` and `libkqueue`, which FreeRADIUS uses for memory
+management, and platform-independent event handling.
+
+Per-module dependencies that enable support for external services
+such as LDAP, SQL, etc, are optional. They must be installed for
+any modules that are to be used. The FreeRADIUS `./configure` step
+will automatically detect if each module has its dependencies met
+and automatically enable support for them. If the features you
+require are not enabled you should inspect the configure script
+output to figure out which additional development libraries need
+to be installed.
+
+The FreeRADIUS source may be obtained from a number of locations:
+
+* Download the latest version of the FreeRADIUS source from
+ https://www.freeradius.org/releases/[the FreeRADIUS web site]; or
+* download directly from the
+ ftp://ftp.freeradius.org/pub/freeradius/[FreeRADIUS FTP site]; or
+* download from
+ https://github.com/FreeRADIUS/freeradius-server/[GitHub].
+
+The file wil be name something like: `freeradius-server-3.0.22.tar.gz`.
+Later versions will be `3.0.23`, or `4.0.0`, etc. PGP signatures are
+also provided for official releases from the FTP site; these are
+named e.g. `freeradius-server-3.0.22.tar.gz.sig`.
+
+Un-tar the file, and change to the FreeRADIUS directory (where
+`VERSION` below is the version of the server that you have
+downloaded).
+
+[source,shell]
+----
+tar -zxf freeradius-server-VERSION.tar.gz
+cd freeradius-server-VERSION
+----
+
+Take the following steps to build and install the server from source:
+
+[source,shell]
+----
+./configure
+make
+sudo make install
+----
+
+=== Custom build
+
+FreeRADIUS has GNU autoconf support. This means you have to run
+`./configure`, and then run `make`. To see which configuration
+options are supported, run `./configure --help`, and read its output.
+
+The `make install` stage will install the binaries, the "man" pages,
+and _may_ install the configuration files. If you have not installed a
+RADIUS server before, then the configuration files for FreeRADIUS will
+be installed.
+
+If you already have a RADIUS server installed, then *FreeRADIUS
+WILL NOT over-write your current configuration.*
+
+The `make install` process will warn you about the files it could not
+install.
+
+If you see a warning message about files that could not be
+installed, then you *must* ensure that the new server is using the
+new configuration files and not the old configuration files, as
+this may cause undesired behavior and failure to operate correctly.
+
+The initial output from running in debugging mode (`radiusd -X`)
+will tell you which configuration files are being used. See
+xref:installation:upgrade.adoc[Upgrading] for information about
+upgrading from older versions. There _may_ be changes in the
+dictionary files which are required for a new version of the
+software. These files will not be installed over your current
+configuration, so you *must* verify and install any problem files by
+hand, for example using `diff(1)` to check for changes.
+
+When installing from source, it is _extremely_ helpful to read the
+output of `./configure`, `make`, and `make install`. If a
+particular module you expected to be installed was not installed,
+then the output will tell you why that module was not installed.
+The most likely reason is that required libraries (including their
+development header files) are not available.
+
+Please do _not_ post questions to the FreeRADIUS users list
+without first carefully reading the output of this process as it
+often contains the information needed to resolve a problem.
+
+== Upgrading To A New Minor Release
+
+The installation process will not over-write your existing configuration
+files. It will, however, warn you about the files it did not install.
+These will require manual integration with the existing files.
+
+It is not possible to re-use configurations between different major
+versions of the server.
+
+For details on what has changed between the version, see the
+xref:installation:upgrade.adoc[upgrade] guide.
+
+We _strongly_ recommend that new major versions be installed in a
+different location than any existing installations. Any local policies
+can then be migrated gradually to the configuration format of the new
+major version. The number of differences in the new configuration mean
+that is is both simpler and safer to migrate your configurations rather
+than to try and just get the old configuration to work.
+
+== Running the server
+
+If the server builds and installs, but doesn’t run correctly, then
+you should first use debugging mode (`radiusd -X`) to figure out
+the problem.
+
+This is your best hope for understanding the problem. Read _all_
+of the messages which are printed to the screen, the answer to
+your problem will often be in a warning or error message.
+
+We really cannot emphasize that last sentence enough. Configuring
+a RADIUS server for complex local authentication isn’t a trivial
+task. Your _best_ and _only_ method for debugging it is to read
+the debug messages, where the server will tell you exactly what
+it’s doing, and why. You should then compare its behaviour to what
+you intended, and edit the configuration files as appropriate.
+
+If you don’t use debugging mode, and ask questions on the mailing
+list, then the responses will all tell you to use debugging mode.
+The server prints out a lot of information in this mode, including
+suggestions for fixes to common problems. Look especially for
+`WARNING` and `ERROR` messages in the output, and read the related
+messages.
+
+Since the main developers of FreeRADIUS use debugging mode to
+track down their configuration problems with the server, it’s a
+good idea for you to use it, too. If you don’t, there is little
+hope for you to solve any configuration problem related to the
+server.
+
+To start the server in debugging mode, do:
+
+[source,shell]
+----
+radiusd -X
+----
+
+You should see a lot of text printed on the screen as it starts up. If
+you don’t, or if you see error messages, please read the FAQ:
+
+https://wiki.freeradius.org/guide/FAQ
+
+If the server says `Ready to process requests.`, then it is running
+properly. From another shell (or another window), type
+
+[source,shell]
+----
+radtest test test localhost 0 testing123
+----
+
+You should see the server print out more messages as it receives the
+request, and responds to it. The `radtest` program should receive the
+response within a few seconds. It doesn’t matter if the authentication
+request is accepted or rejected, what matters is that the server
+received the request, and responded to it.
+
+You can now edit the configuration files for your local system. You will
+usually want to start with `sites-enabled/default` for main
+configurations. To set which NASes (clients) can communicate with this
+server, edit `raddb/clients.conf`. Please read the configuration files
+carefully, as many configuration options are only documented in comments
+in the file.
+
+Note that is is _highly_ recommended that you use some sort of version
+control system to manage your configuration, such as git or Subversion.
+You should then make small changes to the configuration, checking in and
+testing as you go. When a config change causes the server to stop
+working, you will be able to easily step back and find out what update
+broke the configuration.
+
+It is also considered a best practice to maintain a staging or
+development environment. This allows you to test and integrate your
+changes without impacting your active production environment. You should
+make the appropirate investment in order to properly support a critical
+resource such as your authentication servers.
+
+Configuring and running the server MAY be complicated. Many modules have
+`man` pages. See `man rlm_pap`, or `man rlm_*` for information.
+Please read the documentation in the doc/ directory. The comments in the
+configuration files also contain a lot of documentation.
+
+If you have any additional issues, the FAQ is also a good place to
+start.
+
+https://wiki.freeradius.org/guide/FAQ
diff --git a/doc/antora/modules/installation/pages/upgrade.adoc b/doc/antora/modules/installation/pages/upgrade.adoc
new file mode 100644
index 0000000..67874c8
--- /dev/null
+++ b/doc/antora/modules/installation/pages/upgrade.adoc
@@ -0,0 +1,737 @@
+= Upgrading from v2 to v3
+
+The configuration for 3.0 is *largely* compatible with the 2.x.x
+configuration. However, it is NOT possible to simply use the 2.x.x
+configuration as-is. Instead, you should re-create it.
+
+== Security
+
+A number of configuration items have moved into the "security" subsection of
+radiusd.conf. If you use these, you should move them. Otherwise, they can
+be ignored.
+
+The list of moved options is:
+
+* chroot
+* user
+* group
+* allow_core_dumps
+* reject_delay
+* status_server
+
+These entries should be moved from "radiusd.conf" to the "security"
+subsection of that file.
+
+== Naming
+
+Many names used by configuration items were inconsistent in earlier
+versions of the server. These names have been unified in version 3.0.
+
+If a file is being referenced or created the config item `filename`
+is used.
+
+If a file is being created, the initial permissions are set by the
+`permissions` config item.
+
+If a directory hierarchy needs to be created, the permissions are set
+by `dir_permissions`.
+
+If an external host is referenced in the context of a module the
+`server` config item is used.
+
+Unless the config item is a well recognised portmanteau
+(as `filename` is for example), it must be written as multiple
+distinct words separated by underscores `_`.
+
+The configuration items `file`, `script_file`, `module`,
+`detail`, `detailfile`, `attrsfile`, `perm`, `dirperm`,
+`detailperm`, and `hostname` are deprecated. As well as any false
+portmanteaus, and configuration items that used hyphens as word
+delimiters. e.g. `foo-bar` has been changed to `foo_bar`. Please
+update your module configuration to use the new syntax.
+
+In most cases the server will tell you the replacement config item to
+use. As always, run the server in debugging mode to see these
+messages.
+
+== Modules Directory
+
+As of version 3.0, the `modules/` directory no longer exists.
+
+Instead, all "example" modules have been put into the
+`mods-available/` directory. Modules which can be loaded by the
+server are placed in the `mods-enabled/` directory. All of the
+modules in that directory will be loaded. This means that the
+`instantiate` section of radiusd.conf is less important. The only
+reason to list a module in the `instantiate` section is to force
+ordering when the modules are loaded.
+
+Modules can be enabled by creating a soft link. For module `foo`, do:
+
+[source,shell]
+----
+cd raddb/mods-enabled
+ln -s ../mods-available/foo
+----
+
+To create "local" versions of the modules, we suggest copying the file
+instead. This leaves the original file (with documentation) in the
+`mods-available/` directory. Local changes should go into the
+`mods-enabled/` directory.
+
+Module-specific configuration files are now in the `mods-config/`
+directory. This change allows for better organization, and means that
+there are fewer files in the main `raddb` directory. See
+`mods-config/README.rst` for more details.
+
+== Changed Modules
+
+The following modules have been changed in this version.
+
+=== rlm_sql
+
+The SQL configuration has been moved from `sql.conf` to
+`mods-available/sql`. The `sqlippool.conf` file has also been
+moved to `mods-available/sqlippool`.
+
+The SQL module configuration has been changed. The old connection
+pool options are no longer accepted:
+
+----
+num_sql_socks
+connect_failure_retry_delay
+lifetime
+max_queries
+----
+
+Instead, a connection pool configuration is used. This configuration
+contains all of the functionality of the previous configuration, but
+in a more generic form. It also is used in multiple modules, meaning
+that there are fewer different configuration items. The mapping
+between the configuration items is:
+
+----
+num_sql_socks -> pool { max }
+connect_failure_retry_delay -> pool { retry_delay }
+lifetime -> pool { lifetime }
+max_queries -> pool { uses }
+----
+
+The pool configuration adds a number of new configuration options,
+which allow the administrator to better control how FreeRADIUS uses
+SQL connection pools.
+
+The following parameters have been changed:
+
+----
+trace -> removed
+tracefile -> logfile
+----
+
+The logfile is intended to log SQL queries performed. If you need to
+debug the server, use debugging mode. If `logfile` is set, then
+*all* SQL queries will go to `logfile`.
+
+You can now use a NULL SQL database:
+
+.Example
+----
+driver = rlm_sql_null
+----
+
+This is an empty driver which will always return "success". It is
+intended to be used to replace the `sql_log` module, and to work in
+conjunction with the `radsqlrelay` program. Simply take your normal
+configuration for raddb/mods-enabled/sql, and set:
+
+.Example
+----
+driver = rlm_sql_null
+...
+logfile = ${radacctdir}/sql.log
+----
+
+All of the SQL queries will be logged to that file. The connection
+pool does not need to be configured for the `null` SQL driver. It
+can be left as-is, or deleted from the SQL configuration file.
+
+
+=== rlm_sql_sybase
+
+The `rlm_sql_sybase` module has been renamed to `rlm_sql_freetds`
+and the old `rlm_sql_freetds` module has been removed.
+
+`rlm_sql_sybase` used the newer ct-lib API, and `rlm_sql_freetds`
+used an older API and was incomplete.
+
+The new `rlm_sql_freetds` module now also supports database
+selection on connection startup so `use` statements no longer
+have to be included in queries.
+
+
+=== sql/dialup.conf
+
+Queries for post-auth and accounting calls have been re-arranged. The
+SQL module will now expand the 'reference' configuration item in the
+appropriate sub-section, and resolve this to a configuration
+item. This behaviour is similar to rlm_linelog. This dynamic
+expansion allows for a dynamic mapping between accounting types and
+SQL queries. Previously, the mapping was fixed. Any "new" accounting
+type was ignored by the module. Now, support for any accounting type
+can be added by just adding a new target, as below.
+
+Queries from v2.x.x may be manually copied to the new v3.0
+`dialup.conf` file (`raddb/sql/main/<dialect>/queries.conf`).
+When doing this you may also need to update references to the
+accounting tables, as their definitions will now be outside of
+the subsection containing the query.
+
+The mapping from old "fixed" query to new "dynamic" query is as follows:
+
+----
+accounting_onoff_query -> accounting.type.accounting-on.query
+accounting_update_query -> accounting.type.interim-update.query
+accounting_update_query_alt +> accounting.type.interim-update.query
+accounting_start_query -> accounting.type.start.query
+accounting_start_query_alt +> accounting.type.start.query
+accounting_stop_query -> accounting.type.stop.query
+accounting_stop_query_alt +> accounting.type.stop.query
+postauth_query -> post-auth.query
+----
+
+Alternatively a 2.x.x config may be patched to work with the
+3.0 module by adding the following:
+
+.Example
+[source,unlang]
+----
+ accounting {
+ reference = "%{tolower:type.%{Acct-Status-Type}.query}"
+ type {
+ accounting-on {
+ query = "${....accounting_onoff_query}"
+ }
+ accounting-off {
+ query = "${....accounting_onoff_query}"
+ }
+ start {
+ query = "${....accounting_start_query}"
+ query = "${....accounting_start_query_alt}"
+ }
+ interim-update {
+ query = "${....accounting_update_query}"
+ query = "${....accounting_update_query_alt}"
+ }
+ stop {
+ query = "${....accounting_stop_query}"
+ query = "${....accounting_stop_query_alt}"
+ }
+ }
+ }
+ post-auth {
+ query = "${..postauth_query}"
+ }
+----
+
+In general, it is safer to migrate the configuration rather than
+trying to "patch" it, to make it look like a v2 configuration.
+
+Note that the sub-sections holding the queries are labelled
+`accounting-on`, and not `accounting_on`. The reason is that the
+names of these sections are taken directly from the
+`Accounting-Request` packet, and the `Acct-Status-Type` field.
+The `sql` module looks at the value of that field, and then looks
+for a section of that name, in order to find the query to use.
+
+That process means that the server can be extended to support any new
+value of `Acct-Status-Type`, simply by adding a named sub-section,
+and a query. This behavior is preferable to that of v2, which had
+hard-coded queries for certain `Acct-Status-Type` values, and was
+ignored all other values.
+
+=== rlm_ldap
+
+The LDAP module configuration has been substantially changed. Please
+read `raddb/mods-available/ldap`. It now uses a connection pool,
+just like the SQL module.
+
+Many of the configuration items remain the same, but they have been
+moved into subsections. This change is largely cosmetic, but it makes
+the configuration clearer. Instead of having a large set of random
+configuration items, they are now organized into logical groups.
+
+You will need to read your old LDAP configuration, and migrate it
+manually to the new configuration. Simply copying the old
+configuration WILL NOT WORK.
+
+Users upgrading from 2.x.x who used to call the ldap module in
+`post-auth` should now set `edir_autz = yes`, and remove the `ldap`
+module from the `post-auth` section.
+
+=== rlm_ldap and LDAP-Group
+
+In 2.x.x the registration of the `LDAP-Group` pair comparison was done
+by the last instance of rlm_ldap to be instantiated. In 3.0 this has
+changed so that only the default `ldap {}` instance registers
+`LDAP-Group`.
+
+If `<instance>-LDAP-Group` is already used throughout your configuration
+no changes will be needed.
+
+=== rlm_ldap authentication
+
+In 2.x.x the LDAP module had a `set_auth_type` configuration item,
+which forced `Auth-Type := ldap`. This was removed in 3.x.x as it
+often did not work, and was not consistent with the rest of the
+server. We generally recommend that LDAP should be used as a
+database, and that FreeRADIUS should do authentication.
+
+The only reason to use `Auth-Type := ldap` is when the LDAP server
+will not supply the "known good" password to FreeRADIUS, *and* where
+the Access-Request contains User-Password. This situation happens
+only for Active Directory. If you think you need to force `Auth-Type
+:= ldap` in other situations, you are very likely to be wrong.
+
+The following is an example of what should be inserted into the
+`authorize {}` and `authenticate {}` sections of the relevant
+virtual-servers, to get functionality equivalent to v2.x:
+
+.Example
+[source,unlang]
+----
+authorize {
+ ...
+ ldap
+ if ((ok || updated) && User-Password) {
+ update control {
+ Auth-Type := ldap
+ }
+...
+}
+authenticate {
+ ...
+ Auth-Type ldap {
+ ldap
+ }
+...
+}
+----
+
+=== rlm_eap
+
+The EAP configuration has been moved from `eap.conf` to
+`mods-available/eap`. A new `pwd` subsection has been added for
+EAP-PWD.
+
+
+=== rlm_expiration & rlm_logintime
+
+The rlm_expiration and rlm_logintime modules no longer add a `Reply-Message`,
+the same behaviour can be achieved checking the return code of the module and
+adding the `Reply-Message` with unlang:
+
+.Example
+[source,unlang]
+----
+expiration
+if (userlock) {
+ update reply {
+ Reply-Message := "Your account has expired"
+ }
+}
+----
+
+
+=== rlm_unix
+
+The `unix` module does not have an `authenticate` section. So you
+cannot set `Auth-Type := System`. The `unix` module has also been
+deleted from the examples in `sites-available/`. Listing it there
+has been deprecated for many years.
+
+The PAP module can do crypt authentication. It should be used instead
+of Unix authentication.
+
+The Unix module still can pull the passwords from `/etc/passwd`, or
+`/etc/shadow`. This is done by listing it in the `authorize`
+section, as is done in the examples in `sites-available/`. However,
+some systems using NIS or NSS will not supply passwords to the
+`unix` module. For those systems, we recommend putting users and
+passwords into a database, instead of relying on `/etc/passwd`.
+
+
+=== rlm_preprocess
+
+In 2.x.x `huntroups` and `users` files were loaded from default locations
+without being configured explicitly. Since 3.x.x you need to set
+`huntgroups` and `users` configuration item(s) in module section in order
+to get them being processed.
+
+
+== New Modules
+
+=== rlm_date
+
+Instances of rlm_date register an xlat method which can translate
+integer and date values to an arbitrarily formatted date time
+string, or an arbitrarily formated time string to an integer,
+depending on the attribute type passed.
+
+
+=== rlm_rest
+
+The `rest` module is used to translate RADIUS requests into
+RESTfull HTTP requests. Currently supported body types are JSON
+and POST.
+
+
+=== rlm_unpack
+
+The `unpack` module is used to turn data buried inside of binary
+attributes. e.g. if we have `Class = 0x00000001020304` then:
+
+.Example
+[source,unlang]
+----
+Tmp-Integer-0 := "%{unpack:&Class 4 short}"
+----
+
+will unpack octets 4 and 5 as a "short", which has value 0x0304.
+All integers are assumed to be in network byte order.
+
+
+=== rlm_yubikey
+
+The `yubikey` module can be used to forward yubikey OTP token
+values to a Yubico validation server, or decrypt the token
+using a PSK.
+
+
+== Deleted Modules
+
+The following modules have been deleted, and are no longer supported
+in Version 3. If you are using one of these modules, your
+configuration can probably be changed to not need it. Otherwise email
+the freeradius-devel list, and ask about the module.
+
+
+=== rlm_acct_unique
+
+This module has been replaced by the "acct_unique" policy. See
+raddb/policy.d/accounting.
+
+The method for calculating the value of acct_unique has changed.
+However, as this method was configurable, this change should not
+matter. The only issue is in having a v2 and v3 server writing to the
+same database at the same time. They will calculate different values
+for Acct-Unique-Id.
+
+
+=== rlm_acctlog
+
+You should use rlm_linelog instead. That module has a superset of the
+acctlog functionality.
+
+
+=== rlm_attr_rewrite
+
+The attr_rewrite module looked for an attribute, and then re-wrote it,
+or created a new attribute. All of that can be done in "unlang".
+
+A sample configuration in "unlang" is:
+
+.Example
+[source,unlang]
+----
+if (request:Calling-Station-Id) {
+ update request {
+ Calling-Station-Id := "...."
+ }
+}
+----
+
+We suggest updating all uses of attr_rewrite to use unlang instead.
+
+
+=== rlm_checkval
+
+The checkval module compared two attributes. All of that can be done in "unlang":
+
+.Example
+[source,unlang]
+----
+if (&request:Calling-Station-Id == &control:Calling-Station-Id) {
+ ok
+}
+----
+
+We suggest updating all uses of checkval to use unlang instead.
+
+
+=== rlm_dbm
+
+No one seems to use it. There is no sample configuration for it.
+There is no speed advantage to using it over the "files" module.
+Modern systems are fast enough that 10K entries can be read from the
+"users" file in about 10ms. If you need more users than that, use a
+real database such as SQL.
+
+
+=== rlm_fastusers
+
+No one seems to use it. It has been deprecated since Version 2.0.0.
+The "files" module was rewritten so that the "fastusers" module was no
+longer necessary.
+
+
+=== rlm_policy
+
+No one seems to use it. Almost all of its functionality is available
+via `unlang`.
+
+
+=== rlm_sim_files
+
+The rlm_sim_files module has been deleted. It was never marked "stable",
+and was never used in a production environment. There are better ways
+to test EAP.
+
+If you want similar functionality, see rlm_passwd. It can read CSV
+files, and create attributes from them.
+
+
+=== rlm_sql_log
+
+This has been replaced with the "null" sql driver. See
+`raddb/mods-available/sql` for an example configuration.
+
+The main SQL module has more functionality than rlm_sql_log, and
+results in less code in the server.
+
+== Other Functionality
+
+The following is a list of new / changed functionality.
+
+=== RadSec
+
+RadSec (or RADIUS over TLS) is now supported. RADIUS over bare TCP
+is also supported, but is recommended only for secure networks.
+
+See `sites-available/tls` for complete details on using TLS. The server
+can both receive incoming TLS connections, and also originate outgoing
+TLS connections.
+
+The TLS configuration is taken from the old EAP-TLS configuration. It
+is largely identical to the old EAP-TLS configuration, so it should be
+simple to use and configure. It re-uses much of the EAP-TLS code,
+so it is well-tested and reliable.
+
+Once RadSec is enabled, normal debugging mode will not work. This is
+because the TLS code requires threading to work properly. Instead of doing:
+
+.Example
+[source,shell]
+----
+radiusd -X
+----
+
+you will need to do:
+
+.Example
+[source,shell]
+----
+radiusd -fxx -l stdout
+----
+
+That's the price to pay for using RadSec. This limitation may be
+lifted in a future version of the server.
+
+
+=== PAP and User-Password
+
+From version 3.0 onwards the server no longer supports authenticating
+against a cleartext password in the 'User-Password' attribute. Any
+occurences of this (for instance, in the users file) should now be changed
+to 'Cleartext-Password' instead.
+
+e.g. change entries like this:
+
+----
+bob User-Password == "hello"
+----
+
+to ones like this:
+
+----
+bob Cleartext-Password := "hello"
+----
+
+If this is not done, authentication will likely fail. The server will
+also print a helpful message in debugging mode.
+
+If it really is impossible to do this, the following unlang inserted above
+the call to the pap module may be used to copy User-Password to the correct
+attribute:
+
+.Example
+[source,unlang]
+----
+if (!control:Cleartext-Password && control:User-Password) {
+ update control {
+ Cleartext-Password := "%{control:User-Password}"
+ }
+}
+----
+
+However, this should only be seen as a temporary, not permanent, fix.
+It is better to fix your databases to use the correct configuration.
+
+
+== Unlang
+
+
+The unlang policy language is compatible with v2, but has a number of
+new features. See `man unlang` for complete documentation.
+
+
+=== Errors
+
+Many more errors are caught when the server is starting up. Syntax
+errors in `unlang` are caught, and a helpful error message is
+printed. The error message points to the exact place where the error
+occurred:
+
+----
+ ./raddb/sites-enabled/default[230]: Parse error in condition
+ ERROR: if (User-Name ! "bob") {
+ ERROR: ^ Invalid operator
+----
+
+`update` sections are more generic. Instead of doing `update
+reply`, you can do the following:
+
+.Example
+[source,unlang]
+----
+update {
+ reply:Class := 0x0000
+ control:Cleartext-Password := "hello"
+}
+----
+
+This change means that you need fewer `update` sections.
+
+
+=== Comparisons
+
+Attribute comparisons can be done via the `&` operator. When you
+needed to compare two attributes, the old comparison style was:
+
+.Example
+[source,unlang]
+----
+if (User-Name == "%{control:Tmp-String-0}") {
+----
+
+This syntax is inefficient, as the `Tmp-String-0` attribute would be
+printed to an intermediate string, causing unnecessary work. You can
+now instead compare the two attributes directly:
+
+.Example
+[source,unlang]
+----
+if (&User-Name == &control:Tmp-String-0) {
+----
+
+See `man unlang` for more details.
+
+=== Casts
+
+Casts are now permitted. This allows you to force type-specific
+comparisons:
+
+.Example
+[source,unlang]
+----
+if (<ipaddr>"%{sql: SELECT...}" == 127.0.0.1) {
+----
+
+This forces the string returned by the SELECT to be treated as an IP
+address, and compare to `127.0.0.1`. Previously, the comparison
+would have been done as a simple string comparison.
+
+
+=== Networks
+
+IP networks are now supported:
+
+ if (127.0.0.1/32 == 127.0.0.1) {
+
+Will be `true`. The various comparison operators can be used to
+check IP network membership::
+
+.Example
+[source,unlang]
+----
+if (127/8 > 127.0.0.1) {
+----
+
+Returns `true`, because `127.0.0.1` is within the `127/8`
+network. However, the following comparison will return `false`::
+
+.Example
+[source,unlang]
+----
+if (127/8 > 192.168.0.1) {
+----
+
+because `192.168.0.1` is outside of the `127/8` network.
+
+
+=== Optimization
+
+As `unlang` is now pre-compiled, many compile-time optimizations are
+done. This means that the debug output may not be exactly the same as
+what is in the configuration files:
+
+ if (0 && (User-Name == "bob')) {
+
+The result will always be `false`, as the `if 0` prevents the
+following `&& ...` from being evaluated.
+
+Not only that, but the entire contents of that section will be ignored
+entirely:
+
+.Example
+[source,unlang]
+----
+if (0) {
+ this_module_does_not_exist
+ and_this_one_does_not_exist_either
+}
+----
+
+In v2, that configuration would result in a parse error, as there is
+no module called `this_module_does_not_exist`. In v3, that text is
+ignored. This ability allows you to have dynamic configurations where
+certain parts are used (or not) depending on compile-time configuration.
+
+Similarly, conditions which always evaluate to `true` will be
+optimized away:
+
+
+.Example
+[source,unlang]
+----
+if (1) {
+ files
+}
+----
+
+That configuration will never show the `if (1)` output in debugging mode.
+
+=== Dialup_admin
+
+The dialup_admin directory has been removed. No one stepped forward
+to maintain it, and the code had not been changed in many years.
+
diff --git a/doc/antora/modules/unlang/.gitignore b/doc/antora/modules/unlang/.gitignore
new file mode 100644
index 0000000..c5722d7
--- /dev/null
+++ b/doc/antora/modules/unlang/.gitignore
@@ -0,0 +1 @@
+!*.adoc
diff --git a/doc/antora/modules/unlang/nav.adoc b/doc/antora/modules/unlang/nav.adoc
new file mode 100644
index 0000000..77be328
--- /dev/null
+++ b/doc/antora/modules/unlang/nav.adoc
@@ -0,0 +1,51 @@
+* xref:index.adoc[Unlang Policy Language]
+
+** xref:list.adoc[Attribute Lists]
+** xref:attr.adoc[Attribute References]
+** xref:return_codes.adoc[Return Codes]
+
+** xref:keywords.adoc[Keywords]
+*** xref:break.adoc[break]
+*** xref:case.adoc[case]
+*** xref:else.adoc[else]
+*** xref:elsif.adoc[elsif]
+*** xref:foreach.adoc[foreach]
+*** xref:group.adoc[group]
+*** xref:if.adoc[if]
+*** xref:load-balance.adoc[load-balance]
+*** xref:redundant-load-balance.adoc[redundant-load-balance]
+*** xref:redundant.adoc[redundant]
+*** xref:return.adoc[return]
+*** xref:switch.adoc[switch]
+*** xref:update.adoc[update]
+
+** xref:module.adoc[Modules]
+*** xref:module_method.adoc[Module Methods]
+*** xref:module_builtin.adoc[Built-in Modules]
+
+** xref:type/index.adoc[Data Types]
+*** xref:type/index.adoc[List of Data Types]
+*** xref:type/ip.adoc[IP Addresses]
+*** xref:type/numb.adoc[Numbers]
+*** xref:type/string/single.adoc[Single Quoted Strings]
+*** xref:type/string/double.adoc[Double Quoted Strings]
+*** xref:type/string/backticks.adoc[Backtick-quoted string]
+*** xref:type/string/unquoted.adoc[Unquoted Strings]
+
+** xref:condition/index.adoc[Conditional Expressions]
+*** xref:condition/cmp.adoc[Comparisons]
+*** xref:condition/operands.adoc[Operands]
+*** xref:condition/return_code.adoc[The Return Code Operator]
+*** xref:condition/eq.adoc[The '==' Operator]
+*** xref:condition/and.adoc[The '&&' Operator]
+*** xref:condition/or.adoc[The '||' Operator]
+*** xref:condition/not.adoc[The '!' Operator]
+*** xref:condition/para.adoc[The '( )' Operator]
+*** xref:condition/regex.adoc[Regular Expressions]
+
+** xref:xlat/index.adoc[String Expansion]
+*** xref:xlat/alternation.adoc[Alternation Syntax]
+*** xref:xlat/builtin.adoc[Built-in Expansions]
+*** xref:xlat/character.adoc[Single Letter Expansions]
+*** xref:xlat/attribute.adoc[Attribute References]
+*** xref:xlat/module.adoc[Module References]
diff --git a/doc/antora/modules/unlang/pages/attr.adoc b/doc/antora/modules/unlang/pages/attr.adoc
new file mode 100644
index 0000000..70afce4
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/attr.adoc
@@ -0,0 +1,77 @@
+= &Attribute References
+
+.Syntax
+[source,unlang]
+----
+[&]Attribute-Name
+----
+
+The `&Attribute-Name` operator returns a reference to the named
+attribute.
+
+When used as an existence check in a condition, the condition
+evaluates to `true` if the attribute exists. Otherwise, the condition
+evaluates to `false`.
+
+When used elsewhere, such as in xref:switch.adoc[switch], it returns
+the value of the named attribute.
+
+.Examples
+[source,unlang]
+----
+&User-Name
+&NAS-IP-Address
+----
+
+== Lists
+
+The attribute reference can also be qualified with a
+xref:list.adoc[list] reference. When no list is given, the server
+looks in the input packet list for the named attribute.
+
+.Examples
+
+[source,unlang]
+----
+&request:User-Name
+&reply:NAS-IP-Address
+----
+
+== Array References
+
+.Syntax
+[source,unlang]
+----
+&Attribute-Name[<integer>]
+----
+
+When an attribute appears multiple times in a list, this syntax allows
+you to address the attributes as if they were array entries. The
+`<integer>` value defines which attribute to address. The `[0]` value
+refers to the first attributes, `[1]` refers to the second attribute,
+etc.
+
+.Examples
+[source,unlang]
+----
+&EAP-Message[1]
+&reply:NAS-IP-Address[2]
+----
+
+== Removing ambuguity from the configuration files
+
+The server does not use the `&` character to distinguish attribute names
+from other strings.
+
+Without the `&`, it is more difficult to parse the configuration file
+clearly. You could interpret a string as `hello-there`
+either as a literal string "hello-there", or as a reference to an
+attribute named `hello-there`.
+
+Adding the leading `&` character means that attribute references are
+now easily distinguishable from literal strings. The use of the leading
+`&` character is highly recommended.
+
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/break.adoc b/doc/antora/modules/unlang/pages/break.adoc
new file mode 100644
index 0000000..01783ea
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/break.adoc
@@ -0,0 +1,28 @@
+= The break statement
+
+.Syntax
+[source,unlang]
+----
+break
+----
+
+The `break` statement is used to exit an enclosing
+xref:foreach.adoc[foreach] loop. The `break` statement only be used
+inside of a xref:foreach.adoc[foreach] loop.
+
+.Example
+[source,unlang]
+----
+foreach &Class {
+ if (&Foreach-Variable-0 == 0xabcdef) {
+ break
+ }
+
+ update reply {
+ Reply-Message += "Contains %{Foreach-Variable-0}"
+ }
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/case.adoc b/doc/antora/modules/unlang/pages/case.adoc
new file mode 100644
index 0000000..ba2b5fe
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/case.adoc
@@ -0,0 +1,44 @@
+= The case Statement
+
+.Syntax
+[source,unlang]
+----
+case [ <match> ] {
+ [ statements ]
+}
+----
+
+The `case` statement is used to match data inside of a
+xref:switch.adoc[switch] statement. The `case` statement cannot be used
+outside of a xref:switch.adoc[switch] statement.
+
+
+The `<match>` text can be an attribute reference such as `&User-Name`,
+or it can be a xref:type/string/index.adoc[string]. If the match
+text is a dynamically expanded string, then the match is performed on
+the output of the string expansion.
+
+If no `<match>` text is given, it means that the `case` statement is
+the "default" and will match all which is not matched by another
+`case` statement inside of the same xref:switch.adoc[switch].
+
+.Example
+[source,unlang]
+----
+switch &User-Name {
+ case "bob" {
+ reject
+ }
+
+ case &Filter-Id {
+ reject
+ }
+
+ case {
+ ok
+ }
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/condition/and.adoc b/doc/antora/modules/unlang/pages/condition/and.adoc
new file mode 100644
index 0000000..50b3deb
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/condition/and.adoc
@@ -0,0 +1,21 @@
+= The && Operator
+
+.Syntax
+[source,unlang]
+----
+(condition-1 && condition-2)
+----
+
+The `&&` operator performs a short-circuit "and" evaluation of the
+two conditions. This operator evaluates _condition-1_ and returns
+`false` if _condition-1_ returns `false`. Only if _condition-1_
+returns `true` is _condition-2_ evaluated and its result returned.
+
+.Examples
+[source,unlang]
+----
+if (&User-Name && &EAP-Message) { ...
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/condition/cmp.adoc b/doc/antora/modules/unlang/pages/condition/cmp.adoc
new file mode 100644
index 0000000..4138b86
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/condition/cmp.adoc
@@ -0,0 +1,104 @@
+= Comparisons
+
+.Syntax
+[source,unlang]
+----
+lhs OP rhs
+----
+
+The most common use-case for conditions is to perform comparisons.
+The `lhs` and `rhs` of a conditional comparison can be
+xref:attr.adoc[&Attribute-Name] or xref:type/index.adoc[data]. The
+the `OP` is an operator, commonly `==` or `\<=`. It is used to
+control how the two other portions of the condition are compared.
+
+== The Comparison Operators
+
+The comparison operators are given below.
+
+[options="header"]
+|=====
+| Operator | Description
+| < | less than
+| \<= | less than or equals
+| == | equals
+| != | not equals
+| >= | greater than or equals
+| > | greater than
+| xref:condition/regex.adoc[=~] | regular expression matches
+| xref:condition/regex.adoc[!~] | regular expression does not match
+|=====
+
+The comparison operators perform _type-specific_ comparisons. The
+only exceptions are the xref:condition/regex.adoc[regular expression] operators,
+which interpret the `lhs` as a printable string, and the `rhs` as a
+regular expression.
+
+== IP Address Comparisons
+
+The type-specific comparisons operate as expected for most data types.
+The only exception is data types that are IP addresses or IP address
+prefixes. For those data types, the comparisons are done via the
+following rules:
+
+* Any unqualified IP address is assumed to have a /32 prefix (IPv4)
+ or a /128 prefix (IPv6).
+
+* If the prefixes of the left and right sides are equal, then the comparisons
+ are performed on the IP address portion.
+
+* If the prefixes of the left and right sides are not equal, then the
+ comparisons are performed as _set membership checks_.
+
+The syntax allows conditions such as `192.0.2.1 < 192.0.2/24`. This
+condition will return `true`, as the IP address `192.0.2.1' is within
+the network `192.0.2/24`.
+
+== Casting
+
+In some situations, it is useful to force the left side to be
+interpreted as a particular data type.
+
+[NOTE]
+The data types used by the cast *must* be a type taken from the RADIUS
+dictionaries, e.g., `ipaddr`, `integer`, etc. These types are not the
+same as the xref:type/index.adoc[data types] used in the
+configuration files.
+
+.Syntax
+[source,unlang]
+----
+<cast>lhs OP rhs
+----
+
+The `cast` text can be any one of the standard RADIUS dictionary data
+types, as with the following example:
+
+.Example
+[source,unlang]
+----
+<ipaddr>&Class == 127.0.0.1
+----
+
+In this example, the `Class` attribute is treated as if it was an IPv4
+address and is compared to the address `127.0.0.1`
+
+Casting is most useful when the left side of a comparison is a
+dynamically expanded string. The cast ensures that the comparison is
+done in a type-safe manner, instead of performing a string comparison.
+
+.Example
+[source,unlang]
+----
+<integer>`/bin/echo 00` == 0
+----
+
+In this example, the string output of the `echo` program is interpreted as an
+integer. It is then compared to the right side via integer
+comparisons. Since the integer `00` is equivalent to the integer `0`,
+the comparison will match. If the comparison had been performed via
+string equality checks, then the comparison would fail, because the
+strings `00` and `0` are different.
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/condition/eq.adoc b/doc/antora/modules/unlang/pages/condition/eq.adoc
new file mode 100644
index 0000000..d9e51f3
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/condition/eq.adoc
@@ -0,0 +1,30 @@
+= The == Operator
+
+.Syntax
+`(data-1 == data-2)`
+
+The `==` operator compares the result of evaluating `data-1` and
+`data-2`. As discussed in xref:type/index.adoc[Data Types], the `data-1`
+field may be interpreted as a reference to an attribute.
+
+The `data-2` field is interpreted in a type-specific manner. For
+example, if `data-1` refers to an attribute of type `ipaddr`, then
+`data-2` is evaluated as an IP address. If `data-1` refers to an
+attribute of type `integer`, then `data-2` is evaluated as an integer
+or as a named enumeration defined by a `VALUE` statement in a
+dictionary. Similarly, if `data-1` refers to an attribute of type
+`date`, then `data-2` will be interpreted as a date string.
+
+If the resulting data evaluates to be the same, then the operator
+returns `true`; otherwise, it returns `false`.
+
+.Example
+[source,unlang]
+----
+if (User-Name == "bob") {
+ ...
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/condition/index.adoc b/doc/antora/modules/unlang/pages/condition/index.adoc
new file mode 100644
index 0000000..b9d9d5f
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/condition/index.adoc
@@ -0,0 +1,85 @@
+= Conditional Expressions
+
+Conditions are evaluated when parsing xref:if.adoc[if] and
+xref:elsif.adoc[elsif] statements. These conditions allow the server to
+make complex decisions based on one of a number of possible criteria.
+
+.Syntax
+[source,unlang]
+----
+if ( condition ) { ...
+
+elsif ( condition ) { ...
+----
+
+Conditions are expressed using the following syntax:
+
+[options="header"]
+|=====
+| Syntax | Description
+| xref:attr.adoc[&Attribute-Name] | Check for attribute existence.
+| xref:condition/return_code.adoc[rcode] | Check return code of a previous module.
+| xref:condition/operands.adoc[data] | Check value of data.
+| xref:condition/cmp.adoc[lhs OP rhs] | Compare two kinds of data.
+| xref:condition/para.adoc[( condition )] | Check sub-condition
+| xref:condition/not.adoc[! condition] | Negate a conditional check
+| xref:condition/and.adoc[( condition ) && ...] | Check a condition AND the next one
+| xref:condition/or.adoc[( condition ) \|\| ...] | Check a condition OR the next one
+|=====
+
+
+.Examples
+[source,unlang]
+----
+if ( &User-Name == "bob" ) {
+ ...
+}
+
+if ( &Framed-IP-Address == 127.0.0.1 ) {
+ ...
+}
+
+if ( &Calling-Station-Id == "%{sql:SELECT ...}" ) {
+ ...
+}
+----
+
+== Load-time Syntax Checks
+
+The server performs a number of checks when it loads the configuration
+files. Unlike version 2, all of the conditions are syntax checked
+when the server loads. This checking greatly aids in creating
+configurations that are correct. Where the configuration is
+incorrect, a descriptive error is produced.
+
+This error contains the filename and line number of the syntax error.
+In addition, it will print out a portion of the line that caused the
+error and will point to the exact character where the error was seen.
+These descriptive messages mean that most errors are easy to find and fix.
+
+== Load-time Optimizations
+
+The server performs a number of optimizations when it loads the
+configuration files. Conditions that have static values are
+evaluated and replaced with the result of the conditional comparison.
+
+.Example
+[source,unlang]
+----
+if ( 0 == 1 ) {
+ ...
+}
+----
+
+The condition `0 == 1` is static and will evaluate to `false`. Since
+it evaluates to `false`, the configuration inside of the `if`
+statement is ignored. Any modules referenced inside of the `if`
+statement will not be loaded.
+
+This optimization is most useful for creating configurations that
+selectively load (or not) certain policies. If the condition above
+was used in version 2, then the configuration inside of the `if` statement
+would be loaded, even though it would never be used.
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/condition/not.adoc b/doc/antora/modules/unlang/pages/condition/not.adoc
new file mode 100644
index 0000000..bde038e
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/condition/not.adoc
@@ -0,0 +1,19 @@
+= The ! Operator
+
+.Syntax
+[source,unlang]
+----
+! condition
+----
+
+The `!` operator negates the result of the following condition. It
+returns `true` when _condition_ returns `false`. It returns `false`
+when _condition_ returns `true`.
+
+.Examples
+
+`(! (foo == bar))` +
+`! &User-Name`
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/condition/operands.adoc b/doc/antora/modules/unlang/pages/condition/operands.adoc
new file mode 100644
index 0000000..4a2d00b
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/condition/operands.adoc
@@ -0,0 +1,37 @@
+= Operands
+
+.Syntax
+[source,unlang]
+----
+string
+integer
+"double-quoted string"
+'single-quoted string'
+`back-quoted string`
+----
+
+Any text not matching xref:attr.adoc[&Attribute-Name] or
+xref:condition/return_code.adoc[Return Code] is interpreted as a value for a
+particular xref:type/index.adoc[data type].
+
+Double-quoted strings and back-quoted strings are dynamically expanded
+before the condition is evaluated. Single-quoted strings are static
+literals and are not dynamically expanded.
+
+When used as an existence check, the condition evaluates to `true` if
+the data is non-zero. Otherwise, the condition evaluates to `false`.
+
+For integer existence checks, `0` is `false`; all other values are `true`.
+
+For string existence checks, an empty string is `false`. All other
+strings are `true`.
+
+All other data types are disallowed in existence checks.
+
+.Examples
+
+`"hello there"` +
+`5`
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/condition/or.adoc b/doc/antora/modules/unlang/pages/condition/or.adoc
new file mode 100644
index 0000000..80c2cb4
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/condition/or.adoc
@@ -0,0 +1,21 @@
+= The || Operator
+
+.Syntax
+[source,unlang]
+----
+(expression-1 || expression-2)
+----
+
+The `||` operator performs a short-circuit "or" evaluation of the two
+expressions. This operator evaluates _condition-1_ and returns `true`
+if _condition-1_ returns true. Only if _condition-1_ returns `false`
+is _condition-2_ evaluated and its result returned.
+
+.Examples
+[source,unlang]
+----
+if (&User-Name || &NAS-IP-Address) { ...
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/condition/para.adoc b/doc/antora/modules/unlang/pages/condition/para.adoc
new file mode 100644
index 0000000..bdb3f01
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/condition/para.adoc
@@ -0,0 +1,19 @@
+= The ( ) Operator
+
+.Syntax
+[source,unlang]
+----
+( condition )
+----
+
+The `( )` operator returns the result of evaluating the given
+`condition`. It is used to clarify policies or to explicitly define
+conditional precedence.
+
+.Examples
+
+`(foo)` +
+`(bar || (baz && dub))`
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/condition/regex.adoc b/doc/antora/modules/unlang/pages/condition/regex.adoc
new file mode 100644
index 0000000..038faa6
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/condition/regex.adoc
@@ -0,0 +1,180 @@
+= Regular Expressions
+
+.Syntax
+====
+[source,unlang]
+----
+(<subject> =~ /<pattern>/)
+(<subject> =~ /<pattern>/[imsux])
+
+(<subject> !~ /<pattern>/)
+(<subject> !~ /<pattern>/[imsux])
+----
+====
+
+== Matching
+The regular expression operators perform regular expression matching
+on the data. The `<subject>` field can be an attribute reference or data,
+as with the other xref:condition/cmp.adoc[comparison] operators. The `/<pattern>/`
+field must be a valid regular expression.
+
+The `=~` operator evaluates to `true` when `data` matches the
+`/<pattern>/`. Otherwise, it evaluates to `false`.
+
+The `!~` operator evaluates to `true` when `data` does not match the
+`/<pattern>/`. Otherwise, it evaluates to `true`.
+
+The regular expression comparison is performed on the _string representation_
+of the left side of the comparison. That is, if the left side is an
+xref:type/numb.adoc[integer], the regular expression will behave is if the
+value `0` was the literal string `"0"`. Similarly, if the left side is an
+xref:attr.adoc[&Attribute-Name], then the regular expression will behave is if
+the attribute was printed to a string, and the match was performed on the
+resulting string.
+
+.Checking if the `User-Name` attribute contains a realm of example.com
+====
+[source,unlang]
+----
+if (&User-Name =~ /@example\.com$/) {
+ ...
+}
+----
+====
+
+== Dialects
+
+The syntax of the regular expression is defined by the regular
+expression library available on the local system.
+
+FreeRADIUS currently supports:
+
+* link:https://www.pcre.org/original/doc/html/[libpcre] and
+link:https://www.pcre.org/current/doc/html/[libpcre2] both of which
+provide
+link:https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions[Perl
+Compatible Regular expressions].
+* Regex support provided by the local libc implementation, usually
+link:http://en.wikipedia.org/wiki/Regular_expression#POSIX_basic_and_extended[
+Posix regular expressions].
+
+[TIP]
+====
+Use the output of `radiusd -Xxv` to determine which regular expression library is in use.
+
+....
+...
+Debug : regex-pcre : no
+Debug : regex-pcre2 : yes
+Debug : regex-posix : no
+Debug : regex-posix-extended : no
+Debug : regex-binsafe : yes
+...
+Debug : pcre2 : 10.33 (2019-04-16) - retrieved at build time
+....
+====
+
+[WARNING]
+====
+Depending on the regular expression library or libc implementation the server
+was built against, the pattern matching function available may not be binary
+safe (see `regex-binsafe` in the output of `radiusd -Xxv`).
+
+If a binary safe regex match function is not available, and a match is
+attempted against a subject that contains one or more `NUL` ('\0') bytes, the
+match will be aborted, any condition that uses the result will evaluate to false,
+and a warning will be emitted.
+====
+
+== Flags
+
+The regular expression `/<pattern>/` may be followed by one or more flag
+characters. Again, which flags are available depends on the regular expression
+library the server was built with. Multiple flags may be specified per
+`/pattern/`.
+
+.Flags and their uses
+
+[options="header"]
+|=====
+| Flag Character | Available with | Effect
+| `i` | All | Enable case-insensitive matching.
+| `m` | All | '^' and '$' match newlines within the subject.
+| `s` | libpcre[2] | '.' matches anything, including newlines.
+| `u` | libpcre[2] | Treats subjects as UTF8. Invalid UTF8
+ sequences will result in the match failing.
+ |`x` | libpcre[2] | Allows comments in expressions by ignoring
+ whitespace, and text between '#' and the next
+ newline character.
+|=====
+
+== Subcapture groups
+
+When the `=~` or `!~` operators are used, then parentheses in the regular
+expression will sub capture groups, which contain part of the subject string.
+
+The special expansion `%{0}` expands to the portion of the subject that
+matched. The expansions +
+`%{1}`..`%{32}` expand to the contents of any subcapture groups.
+
+When using libpcre[2], named capture groups may also be accessed using the
+built-in expansion +
+`%{regex:<named capture group>}`.
+
+Please see the xref:xlat/builtin.adoc#_0_32[xlat documentation] for
+more information on regular expression matching.
+
+.Extracting the 'user' portion of a realm qualified string
+====
+[source,unlang]
+----
+if (&User-Name =~ /^(.*)@example\.com$/) {
+ update reply {
+ Reply-Message := "Hello %{1}"
+ }
+}
+----
+====
+
+== Pre-Compiled vs Runtime Compiled
+
+When the server starts any regular expressions comparisons it finds will be
+pre-compiled, and if support is available, JIT'd (converted to machine code)
+to ensure fast execution.
+
+If a pattern contains a xref:xlat/index.adoc[string expansion], the pattern
+cannot be compiled on startup, and will be compiled at runtime each time the
+expression is evaluated. The server will also turn off JITing for runtime
+compiled expressions, as the overhead is greater than the time that would be
+saved during evaluation.
+
+.A runtime compiled regular expression
+====
+[source,unlang]
+----
+if (&User-Name =~ /^@%{Tmp-String-0}$/) {
+ ...
+}
+----
+====
+
+To ensure optimal performance you should limit the number of patterns
+containing xref:xlat/index.adoc[string expansions], and if using PCRE, combine
+multiple expressions operating on the same subject into a single expression
+using the PCRE alternation '|' operator.
+
+.Using multiple string expansions and the PCRE alternation operator
+====
+[source,unlang]
+----
+if (&User-Name =~ /^@(%{Tmp-String-0}|%{Tmp-String-1})$/) {
+ ...
+}
+----
+====
+
+
+// Licenced under CC-by-NC 4.0.
+// Copyright (C) 2020 Network RADIUS SAS.
+// Copyright (C) 2019 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/condition/return_codes.adoc b/doc/antora/modules/unlang/pages/condition/return_codes.adoc
new file mode 100644
index 0000000..ebc49ed
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/condition/return_codes.adoc
@@ -0,0 +1,35 @@
+= The return code Operator
+
+.Syntax
+[source,unlang]
+----
+rcode
+----
+
+The Unlang interpreter tracks the return code of any module, string expansion
+or keyword that has been called.
+
+This return code can be checked in any condition. If the saved return code
+matches the `code` given here, then the condition evaluates to `true`.
+Otherwise, it evaluates to `false`.
+
+rcodes cannot be set in a condition. rcodes cannot be compared with anything else.
+
+The list of valid return codes is as follows:
+
+.Return Codes
+
+include::partial$rcode_table.adoc[]
+
+.Examples
+
+[source,unlang]
+----
+sql
+if (notfound) {
+ ...
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/default.adoc b/doc/antora/modules/unlang/pages/default.adoc
new file mode 100644
index 0000000..3b298f6
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/default.adoc
@@ -0,0 +1,47 @@
+= The case Statement
+
+.Syntax
+[source,unlang]
+----
+case [ <match> ] {
+ [ statements ]
+}
+----
+
+The `case` statement is used to match data inside of a
+xref:switch.adoc[switch] statement. The `case` statement cannot be used
+outside of a xref:switch.adoc[switch] statement.
+
+
+The `<match>` text can be an attribute reference such as `&User-Name`,
+or it can be a xref:type/string/index.adoc[string]. If the match
+text is a dynamically expanded string, then the match is performed on
+the output of the string expansion.
+
+The keyword `default` can be used to specify the default action to
+take inside of a xref:switch.adoc[switch] statement.
+
+If no `<match>` text is given, it means that the `case` statement is
+the "default" and will match all which is not matched by another
+`case` statement inside of the same xref:switch.adoc[switch].
+
+.Example
+[source,unlang]
+----
+switch &User-Name {
+ case "bob" {
+ reject
+ }
+
+ case &Filter-Id {
+ reject
+ }
+
+ default {
+ ok
+ }
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/else.adoc b/doc/antora/modules/unlang/pages/else.adoc
new file mode 100644
index 0000000..a795d0e
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/else.adoc
@@ -0,0 +1,30 @@
+= The else Statement
+
+.Syntax
+[source,unlang]
+----
+if (condition) {
+ [ statements ]
+}
+else {
+ [ statements ]
+}
+----
+
+An xref:if.adoc[if] statement can have an `else` clause. If _condition_
+evaluates to `false`, the statements in the xref:if.adoc[if] subsection are skipped
+and the statements within the `else` subsection are executed.
+
+.Example
+[source,unlang]
+----
+if (&User-Name == "bob") {
+ reject
+}
+else {
+ ok
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/elsif.adoc b/doc/antora/modules/unlang/pages/elsif.adoc
new file mode 100644
index 0000000..ff5799c
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/elsif.adoc
@@ -0,0 +1,43 @@
+= The elsif Statement
+
+.Syntax
+[source,unlang]
+----
+if (condition-1) {
+ [ statements-1 ]
+}
+elsif (condition-2) {
+ [ statements-2 ]
+}
+else {
+ [ statements-3 ]
+}
+----
+
+An `elsif` statement is used to evaluate a subsequent
+xref:condition/index.adoc[condition] after a preceding xref:if.adoc[if] statement
+evaluates to `false`. In the example above, when _condition-1_
+evaluates to false, then _statements-1_ are skipped and _condition-2_
+is checked. When _condition-2_ evaluates true, then _statements-2_
+are executed. When _condition-2_ evaluates false, then
+_statements-2_ are skipped and _statements-3_ are executed.
+
+As with xref:if.adoc[if], an `elsif` clause does not need to be followed by
+an xref:else.adoc[else] statement. However, any xref:else.adoc[else] statement
+must be the last statement in an `elsif` chain. An arbitrary number of
+`elsif` statements can be chained together to create a series of
+conditional checks and statements.
+
+.Example
+[source,unlang]
+----
+if (&User-Name == "bob") {
+ reject
+}
+elsif (&User-Name == "doug") {
+ ok
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/foreach.adoc b/doc/antora/modules/unlang/pages/foreach.adoc
new file mode 100644
index 0000000..6ed3ddf
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/foreach.adoc
@@ -0,0 +1,40 @@
+= The foreach Statement
+
+.Syntax
+[source,unlang]
+----
+foreach <attribute-reference> {
+ [ statements ]
+}
+----
+
+The `foreach` statement loops over a set of attributes as given by
+`<attribute-reference>`. The loop can be exited early by using the
+xref:break.adoc[break] keyword.
+
+<attribute-reference>::
+
+The xref:attr.adoc[attribute reference] which will will be looped
+over. The reference can be to one attribute, to an array, a child, or
+be a subset.
+
+Inside of the `foreach` block, the attribute that is being looped over
+can be referenced as `Foreach-Variable-0`, through
+`Foreach-Variable-9`. The last digit is the depth of the loop,
+starting at "0". The loops can be nested up to eight (8) deep, though
+this is not recommended.
+
+The attributes being looped over cannot be modified or deleted.
+
+.Example
+[source,unlang]
+----
+foreach &Class {
+ update reply {
+ Reply-Message += "Contains %{Foreach-Variable-0}"
+ }
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/group.adoc b/doc/antora/modules/unlang/pages/group.adoc
new file mode 100644
index 0000000..98801fd
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/group.adoc
@@ -0,0 +1,39 @@
+= The group Statement
+
+.Syntax
+[source,unlang]
+----
+group {
+ [ statements ]
+}
+----
+
+The `group` statement collects a series of statements into a list.
+The default processing sections of the server (`authorize`,
+`accounting`, etc.) are also `group` statements. Those sections are
+given different name for management reasons, but they behave
+internally exactly like a `group`.
+
+[ statements ]:: The `unlang` commands which will be executed.
+
+All of the statements inside of the `group` are executed in sequence.
+The `group` statement is not normally used, as the statements within
+it can just be placed inside of the enclosing section. However, the
+`group` statement is included in the `unlang` syntax for completeness.
+
+.Examples
+
+[source,unlang]
+----
+group {
+ sql
+ ldap
+ file
+ if (updated) {
+ ...
+ }
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/if.adoc b/doc/antora/modules/unlang/pages/if.adoc
new file mode 100644
index 0000000..ea549ef
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/if.adoc
@@ -0,0 +1,29 @@
+= The if Statement
+
+.Syntax
+[source,unlang]
+----
+if (condition) {
+ [ statements ]
+}
+----
+
+.Description
+The `if` statement evaluates a xref:condition/index.adoc[condition]. When the
+_condition_ evaluates to `true`, the statements within the subsection
+are executed. When the _condition_ evaluates to `false`, those
+statements are skipped.
+
+An `if` statement can optionally be followed by an xref:else.adoc[else] or
+an xref:elsif.adoc[elsif] statement.
+
+.Example
+[source,unlang]
+----
+if (&User-Name == "bob") {
+ reject
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/index.adoc b/doc/antora/modules/unlang/pages/index.adoc
new file mode 100644
index 0000000..fc812f8
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/index.adoc
@@ -0,0 +1,162 @@
+= Unlang Policy Language
+
+The server supports a simple processing language called "Unlang",
+which is short for "un-language". The original intention of using an
+"un-language" was to avoid creating yet another programming language.
+Instead, the `unlang` syntax allows for simple _if / then / else_
+checks, and attribute editing. It does not support recursion,
+subroutines, or more complex flow controls.
+
+Where more complicated functionality is required, we recommend using
+the `lua`, `perl` or `python` modules. Those modules allow the insertion of
+full-featured scripts at any point in the packet processing.
+
+NOTE: The documentation is this directory is _reference_
+documentation. That is, it describes the syntax of `unlang` keywords,
+using minimal examples. The reference documentation does not,
+however, describe _when_ to use the keywords, or _how_ to create
+policies. Please see the xref:howto:index.adoc[howto] directory for
+more in-depth "how to" guides.
+
+The documentation is organized so that each item is on its own page.
+The page beings with a description of the item, followed by some text
+explaining what the item does. The page concludes with one or more
+examples of using the item in `unlang` policies.
+
+The `unlang` processing can be split into some high-level concepts.
+
+== Keywords
+
+xref:keywords.adoc[Keywords], which are the basic statements of the
+language, e.g., xref:load-balance.adoc[load-balance],
+xref:if.adoc[if], xref:else.adoc[else], etc.
+
+.Example
+[source,unlang]
+----
+load-balance {
+ sql1
+ sql2
+ sql3
+}
+----
+
+== Conditional Expressions
+
+xref:condition/index.adoc[Conditional expressions], which are used to check
+if conditions evaluate to _true_ or _false_. Conditional expressions
+can be used to control the flow of processing.
+
+.Example
+[source,unlang]
+----
+if ((&User-Name == "bob") && (&Calling-Station-Id == "00:01:03:04:05")) {
+ ...
+}
+----
+
+== Update Statements
+
+xref:update.adoc[update] statements are used to edit attributes and
+lists of attributes.
+
+Most request packets will result in reply packets that contain
+additional information for the requestor. The `update` section allows
+policies to add attributes to requests, replies, or to other places.
+
+.Example
+[source,unlang]
+----
+update reply {
+ &Session-Timeout := 3600
+ &Framed-IP-Address := 192.0.2.4
+}
+----
+
+== String Expansions
+
+xref:xlat/index.adoc[String expansion] Using `%{...}` to perform dynamic
+string expansions. (also known as xref:xlat/index.adoc[xlat])
+
+String expansions are usually performed in order to get additional
+information which is not immediately available to the policy. This
+information can be taken from almost any source, including other
+attributes, databases, and scripts.
+
+.Example
+[source,unlang]
+----
+update reply {
+ &Framed-IP-Address := "%{sql:SELECT static_ip from table WHERE user = '%{User-Name}'}"
+}
+----
+
+== Data Types
+
+Each attribute used by the server has an associated
+xref:type/index.adoc[data type]. The `unlang` interpreter enforces
+restrictions on assignments, so that only valid data types can be
+assigned to an attribute. Invalid assignments result in a run-time
+error.
+
+.Example
+[source,unlang]
+----
+update reply {
+ &Framed-IP-Address := 192.0.2.4
+ &Session-Timeout := 5
+ &Reply-Message := "hello"
+}
+----
+
+== Design Goals of Unlang
+
+The goal of `unlang` is to allow simple policies to be written with
+minimal effort. Conditional checks can be performed by the policies,
+which can then update the request or response attributes based on the
+results of those checks. `unlang` can only be used in a processing
+section, it cannot be used anywhere else, including in configuration
+sections for a client or a module. The reason for this limitation is
+that the language is intended to perform specific actions on requests
+and responses. The client and module sections contain definitions for
+a client or module; they do not define how a request is processed.
+
+`unlang` uses the same the basic syntax as the configuration files.
+The syntax of the configuration file for lines, comments, sections,
+sub-section, etc., all apply to `unlang`.
+
+Where `unlang` differs from the basic configuration file format is in
+complexity and operation. The normal configuration files are
+_declarative_ and they are _static_. That is, they declare variables
+and values for those variables. Those values do not change when the
+server is running.
+
+In contrast, `unlang` performs run-time processing of requests.
+Conditional statements such as xref:if.adoc[if] are evaluated for every
+packet that is received. Attribute editing statements such as
+xref:update.adoc[update] can be used to edit attribute contents or lists
+of attributes.
+
+.Example
+[source,unlang]
+----
+# First, the keyword 'if'
+
+# followed by condition which checks that the User-Name
+# attribute has value "bob"
+
+if (&User-Name == "bob") {
+ # keyword "update"
+
+ # followed by instructions to add the Reply-Message
+ # attribute to the "reply" list, with contents
+ # "Hello, bob"
+
+ update reply {
+ Reply-Message := "Hello, bob"
+ }
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/keywords.adoc b/doc/antora/modules/unlang/pages/keywords.adoc
new file mode 100644
index 0000000..e6411ea
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/keywords.adoc
@@ -0,0 +1,78 @@
+= Keywords
+
+The following tables list the keywords used in `Unlang`. These keywords
+implement the "flow control" of the policies.
+
+== Flow Control Keywords
+
+The _flow control_ keywords allow _if / then / else_ checks, simple
+looping, etc.
+
+.Flow Control
+[options="header"]
+[cols="30%,70%"]
+|=====
+| Keyword | Description
+| xref:break.adoc[break] | Exit early from a `foreach` loop.
+| xref:case.adoc[case] | Match inside of a `switch`.
+| xref:else.adoc[else] | Do something when an `if` does not match.
+| xref:elsif.adoc[elsif] | Check for condition when a previous `if` does not match.
+| xref:foreach.adoc[foreach] | Loop over a list of attributes.
+| xref:if.adoc[if] | Check for a condition, and execute a sub-policy if it matches.
+| xref:return.adoc[return] | Immediately stop processing a section.
+| xref:switch.adoc[switch] | Check for multiple values.
+|=====
+
+== Attribute Editing Keywords
+
+The _attribute editing_ keywords allow policies to add, delete, and
+modify attributes in any list or packet.
+
+.Attribute Editing
+[options="header"]
+[cols="30%,70%"]
+|=====
+| Keyword | Description
+| xref:update.adoc[update] | Add or filter attributes to a list
+|=====
+
+== Grouping Keywords
+
+The _grouping_ keywords allow policies to be organized into groups,
+including load-balancing.
+
+.Grouping
+[options="header"]
+[cols="30%,70%"]
+|=====
+| Keyword | Description
+| xref:group.adoc[group] | Group a series of statements.
+| xref:load-balance.adoc[load-balance] | Define a load balancing group without fail-over.
+| xref:redundant.adoc[redundant] | Define a redundant group with fail-over.
+| xref:redundant-load-balance.adoc[redundant-load-balance] | Define a redundant group with fail-over and load balancing.
+|=====
+
+== Module Keywords
+
+The _module_ keywords refer pre-packaged libraries that implement
+specific functionality, such as connecting to SQL, LDAP, etc. The
+name used here is not the literal string `module`. Instead, it is the
+name of an instance of a pre-packaged module such as `sql`, or `ldap`, or
+`eap`.
+
+The documentation below describes how to reference modules. That is,
+how to use `sql`, etc. in the policies. Please see
+`raddb/mods-available/` for configuration examples for each module.
+
+.Modules
+[options="header"]
+[cols="30%,70%"]
+|=====
+| Keyword | Description
+| xref:module.adoc[<module>] | Execute a named module, e.g., `sql`.
+| xref:module_method.adoc[<module>.<method>] | Execute a particular method of a named module, e.g., `pap.authorize`
+| xref:module_builtin.adoc[reject] | Built-in modules, e.g., `reject`, or `ok`, etc.
+|=====
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/list.adoc b/doc/antora/modules/unlang/pages/list.adoc
new file mode 100644
index 0000000..a55a54f
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/list.adoc
@@ -0,0 +1,72 @@
+= Attribute Lists
+
+An attribute list contains a set of attributes. The allowed lists
+are:
+
+`request`:: Attributes in the incoming request packet.
+
+`reply`:: Attributes in the outgoing reply packet.
+
+`control`:: Attributes in the internal "control" list that is
+associated with the request.
++
+The `control` attributes are used to manage how the request is
+processed. These attributes are never sent in any packet.
+
+`session-state`:: Attributes which are maintained across multi-packet
+exchanges.
+
+`proxy-request`:: Attributes in the proxied request packet to a home server.
+
+`proxy-reply`:: Attributes in the reply packet from the home server.
+
+`coa`:: Attributes in a CoA-Request packet which is sent to a home server.
+
+`disconnect`:: Attributes in a Disconnect-Request packet which is sent to a home server.
+
+There must be a colon `:` after the list name and before the attribute name.
+This syntax helps the server to distinguish between list names and attribute
+names.
+
+With the exception of `session-state`, all of the above lists are
+ephemeral. That is, they exist for one packet exchange, and only one
+packet exchange. When a reply is sent for a request, the above lists
+and all attributes are deleted. There is no way to reference an
+attribute from a previous packet. We recommend using a database to
+track complex state.
+
+The `coa` and `disconnect` lists can only be used when the server
+receives an Access-Request or Accounting-Request. Use `request` and
+`reply` instead of `coa` when the server receives a CoA-Request or
+Disconnect-Request packet.
+
+Adding one or more attributes to either of the `coa` or `disconnect`
+list causes server to originate a CoA-Request or Disconnect-Request
+packet. That packet is sent when the current Access-Request or
+Accounting-Request has been finished, and a reply sent to the NAS.
+See `raddb/sites-available/originate-coa` for additional information.
+
+In some cases, requests are associated a multi-packet exchange. For
+those situations, the `session-state` list is automatically saved when
+a reply is sent, and it is automatically restored when the next packet
+in sequence comes in. Once the packet exchange has been finished, the
+`session-state` list is deleted.
+
+In some cases, there is a parent-child relationship between requests.
+In those situations, it is possible for the policy rules in the child
+to refer to attributes in the parent. This reference can be made by
+prefixing the _<list>_ name with the `parent` qualifier. The key word
+`outer` is also a synonym for `parent`. If there are multiple
+parent-child relationships, the `parent` qualifier can be repeated.
+
+There is, however, no way for the parent to refer to the child. When
+the child is running, the parent is suspended. Once the child
+finishes, it is deleted, and is no longer accessible to the parent.
+
+.Examples
+`&parent.request.User-Name` +
+`&parent.reply.Reply-Message` +
+`&parent.parent.session-state.Filter-Id`
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/load-balance.adoc b/doc/antora/modules/unlang/pages/load-balance.adoc
new file mode 100644
index 0000000..d64b161
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/load-balance.adoc
@@ -0,0 +1,32 @@
+= The load-balance Statement
+
+.Syntax
+[source,unlang]
+----
+load-balance {
+ [ statements ]
+}
+----
+
+The `load-balance` section is similar to the `redundant` section
+except that only one module in the subsection is ever called.
+
+In general, the
+xref:redundant-load-balance.adoc[redundant-load-balance] statement is
+more useful than this one.
+
+[ statements ]:: One or more `unlang` commands. Only one of the
+statements is executed.
+
+.Examples
+
+[source,unlang]
+----
+load-balance &User-Name {
+ sql1
+ sql2
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/module.adoc b/doc/antora/modules/unlang/pages/module.adoc
new file mode 100644
index 0000000..fd18f2f
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/module.adoc
@@ -0,0 +1,86 @@
+= Modules
+
+.Syntax
+[source,unlang]
+----
+<module>
+----
+
+The `<module>` statement is a reference to the named module. Common
+module names include `pap`, `chap`, `files`, `eap`, and `sql`. The
+`modules { ... }` subsection of `radiusd.conf` contains all of the
+valid modules.
+
+When processing reaches a named module, the pre-compiled module is
+called. The module may succeed or fail and will return a status code
+to the `unlang` interpreter detailing success or failure.
+
+.Example
+[source,unlang]
+----
+chap
+sql
+----
+
+== Module Return Codes
+
+When a module is called, it returns one of the following codes to
+the interpreter; the meaning of each code is detailed to the right of
+the source, below:
+
+.Module Return Codes
+
+The below table describes the purpose of the rcodes that may be returned
+by a module call. In this case the 'operation' referenced in the table is
+the current module call.
+
+include::partial$rcode_table.adoc[]
+
+These return codes can be used in a subsequent
+xref:condition/index.adoc[conditional expression] thus allowing policies to
+perform different actions based on the behaviour of the modules.
+
+.Example
+[source,unlang]
+----
+sql
+if (notfound) {
+ update reply {
+ Reply-Message = "We don't know who you are"
+ }
+ reject
+}
+----
+
+== Module Return Code priority overrides
+
+In the case of modules, rcodes can be modified on a per-call basis.
+
+Module priority overrides are specified in a block inline with the module call.
+The format of an override is `<rcode> = (0+|<rcode>|return)` - That is,
+a number greater than or equal to 0, the priority of another rcode, or the special
+priority `return` which causes the current section to immediately exit.
+
+[source, unlang]
+----
+ldap { <1>
+ fail = 1 <2>
+ ok = handled <3>
+ reject = return <4>
+}
+----
+
+<1> Call to the `ldap` module.
+<2> Sets the priority of the `fail` rcode to be `1`. If the priority
+ of the rcode for the request is `0`, then the request request rcode
+ will be set to `fail` if the module returns `fail`.
+<3> Sets the priority of the `ok` rcode to be whatever the default is for
+ `handled` in this section. As the default for `handled` is usually
+ `return`. If `ok` is returned, the current section will immediately
+ exit.
+<4> Sets the priority of `reject` to be `return`. If the module returns
+ `reject`, the current section will immediately exit.
+
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/module_builtin.adoc b/doc/antora/modules/unlang/pages/module_builtin.adoc
new file mode 100644
index 0000000..f21a128
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/module_builtin.adoc
@@ -0,0 +1,42 @@
+= Built-in Modules
+
+In some cases, it is useful to reject a request immediately or perform another
+action on it. The built-in modules can be used to perform these actions. These
+modules are named for the return codes given in the xref:module.adoc[module]
+section.
+
+In practice, these modules are implemented by the `always` module and
+exist so that a success or failure can be forced during the processing
+of a policy.
+
+The names and behaviours of these modules are given below:
+
+`fail`::
+Causes the request to be treated as if a database failure had
+occurred.
+
+`noop`::
+Do nothing. This also serves as an instruction to the
+configurable failover tracking that nothing was done in the current
+section.
+
+`ok`::
+Instructs the server that the request was processed properly. This keyword can be used to over-ride earlier failures if the local
+administrator determines that the failures are not catastrophic.
+
+`reject`::
+Causes the request to be immediately rejected.
+
+.Example
+[source,unlang]
+----
+if (!&User-Name) {
+ update reply {
+ Reply-Message := "We don't know who you are"
+ }
+ reject
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/module_method.adoc b/doc/antora/modules/unlang/pages/module_method.adoc
new file mode 100644
index 0000000..98cd375
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/module_method.adoc
@@ -0,0 +1,27 @@
+= Module methods
+
+.Syntax
+[source,unlang]
+----
+<module>.<method>
+----
+
+This variant of xref:module.adoc[<module>] is used in one processing
+section. It calls a module using the method of another processing
+section. For example, it can be used to call a module's `authorize`
+method while processing the `post-auth` section.
+
+The `<module>` portion must refer to an existing module; the
+`<method>` portion must refer to processing method supported by that
+module. Typically, the names of the processing method will be the
+same as the processing sections.
+
+.Example
+[source,unlang]
+----
+sql.recv.Accounting-Request
+files.recv.Access-Request
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/redundant-load-balance.adoc b/doc/antora/modules/unlang/pages/redundant-load-balance.adoc
new file mode 100644
index 0000000..2322f72
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/redundant-load-balance.adoc
@@ -0,0 +1,39 @@
+= The redundant-load-balance Statement
+
+.Syntax
+[source,unlang]
+----
+redundant-load-balance {
+ [ statements ]
+}
+----
+
+The `redundant-load-balance` section operates as a combination of the
+xref:redundant.adoc[redundant] and xref:load-balance.adoc[load-balance]
+sections.
+
+[ statements ]:: One or more `unlang` commands.
++
+If the selected statement succeeds, then the server stops processing
+the `redundant-load-balance` section. If, however, that statement fails,
+then the next statement in the list is chosen (wrapping around to the
+top). This process continues until either one statement succeeds or all
+of the statements have failed.
++
+All of the statements in the list should be modules, and of the same
+type (e.g., `ldap` or `sql`). All of the statements in the list should
+behave identically, otherwise different requests will be processed
+through different modules and will give different results.
+
+.Example
+[source,unlang]
+----
+redundant-load-balance &User-Name {
+ sql1
+ sql2
+ sql3
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/redundant.adoc b/doc/antora/modules/unlang/pages/redundant.adoc
new file mode 100644
index 0000000..e837d1f
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/redundant.adoc
@@ -0,0 +1,42 @@
+= The redundant Statement
+
+.Syntax
+[source,unlang]
+----
+redundant {
+ [ statements ]
+}
+----
+
+The `redundant` section executes a series of statements in sequence.
+As soon as one statement succeeds, the rest of the section is skipped.
+
+[ statements ]:: One or more `unlang` commands. Processing starts
+from the first statement in the list.
++
+If the selected statement succeeds, then the server stops processing
+the `redundant` section. If, however, that statement fails, then the
+next statement in the list is chosen. This process continues until
+either one statement succeeds or all of the statements have failed.
++
+All of the statements in the list should be modules, and of the same
+type (e.g., `ldap` or `sql`). All of the statements in the list should
+behave identically, otherwise different requests will be processed
+through different modules and will give different results.
+
+In general, we recommend using the
+xref:redundant-load-balance.adoc[redundant-load-balance] statement
+instead of `redundant`.
+
+.Example
+[source,unlang]
+----
+redundant {
+ sql1
+ sql2
+ sql3
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/return.adoc b/doc/antora/modules/unlang/pages/return.adoc
new file mode 100644
index 0000000..aea1bc2
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/return.adoc
@@ -0,0 +1,36 @@
+= The return Statement
+
+.Syntax
+[source,unlang]
+----
+return
+----
+
+The `return` statement is used to exit a processing section such as
+`authorize`. It behaves similarly to the
+xref:break.adoc[break] statement, except that it is not limited to
+being used inside of a xref:foreach.adoc[foreach] loop.
+
+The `return` statement is not strictly necessary. It is mainly used
+to simplify the layout of `unlang` policies. If the `return`
+statement did not exist, then every xref:if.adoc[if] statement might need
+to have a matching xref:else.adoc[else] statement.
+
+The `return` statement will also exit a policy which is defined in the
+`policy { ... } ` subsection. This behavior allows policies to be
+treated as a function call. Any `return` inside of the policy section
+will only return from that policy. The `return` will _not_ return
+from the enclosing processing section which called the policy.
+
+.Example
+[source,unlang]
+----
+sql
+if (&reply.Filter-Id == "hello") {
+ return
+}
+...
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/return_codes.adoc b/doc/antora/modules/unlang/pages/return_codes.adoc
new file mode 100644
index 0000000..3b09c2d
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/return_codes.adoc
@@ -0,0 +1,17 @@
+= Return codes
+
+Many operations in the server produce a return code (rcode).
+The different rcodes give a course indication of whether a particular operation
+(a module call, string expansion, or keyword) was successful.
+
+Unlike return values in other languages, FreeRADIUS' rcodes are are always taken
+from a fixed compiled-in set.
+
+include::partial$rcode_table.adoc[]
+
+Return codes propagate through nested unlang sections based on their priority.
+If a rcode returned by an operation has a higher priority than the current rcode
+associated with the request, then the request rcode is overwritten.
+
+Return code priorities are assigned by the section the module call, expansion or
+keyword was used in.
diff --git a/doc/antora/modules/unlang/pages/switch.adoc b/doc/antora/modules/unlang/pages/switch.adoc
new file mode 100644
index 0000000..deff703
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/switch.adoc
@@ -0,0 +1,83 @@
+= The switch Statement
+
+.Syntax
+[source,unlang]
+----
+switch <expansion> {
+ case <match-1> {
+ [ statements-1 ]
+ }
+ case <match-2> {
+ [ statements-2 ]
+ }
+ case {
+ [ statements-3 ]
+ }
+}
+----
+
+A `switch` statement causes the server to evaluate _expansion_, which
+can be an xref:attr.adoc[&Attribute-Name] or
+xref:condition/operands.adoc[data]. The result is compared against _match-1_
+and _match-2_ to find a match. If no string matches, then the server
+looks for the default xref:case.adoc[case] statement, which has no
+associated match.
+
+The matching is done via equality. The `switch` statement is mostly
+syntactic sugar and is used to simplify the visual form of the
+configuration. It is mostly equivalent to the following use of
+xref:if.adoc[if] statements:
+
+.Nearly equivalent syntax
+[source,unlang]
+----
+if (<expansion> == <match-1>) {
+ [ statements-1 ]
+}
+elsif (<expansion> == <match-2>) {
+ [ statements-2 ]
+}
+else {
+ [ statements-3 ]
+}
+----
+
+The only difference between the two forms is that for a `switch`
+statement, the _expansion_ is evaluated only once. For the equivalent
+xref:if.adoc[if] statement, the _expansion_ is evaluated again for every
+xref:if.adoc[if].
+
+If a matching xref:case.adoc[case] is found, the statements within
+that xref:case.adoc[case] are evaluated. If no matching
+xref:case.adoc[case] is found, the `case` section with no "match" is
+evaluated. If there is no such `case { ...}` subsection, then the
+`switch` statement behaves as if the `case {...}` section was empty.
+
+////
+For compatibility with version 3, and empty `case` statement can also
+be used instead of `default`.
+////
+
+The _match_ text for the xref:case.adoc[case] statement can be an
+xref:attr.adoc[&Attribute-Name] or xref:type/index.adoc[data].
+
+No statement other than xref:case.adoc[case] can appear in a `switch`
+statement, and the xref:case.adoc[case] statement cannot appear outside of a
+`switch` statement.
+
+.Example
+[source,unlang]
+----
+switch &User-Name {
+ case "bob" {
+ reject
+ }
+
+ case {
+ ok
+ }
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/type/all_types.adoc b/doc/antora/modules/unlang/pages/type/all_types.adoc
new file mode 100644
index 0000000..0bace01
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/type/all_types.adoc
@@ -0,0 +1,80 @@
+= List of Data Types
+
+The server support a wide range of data types, both in `unlang` and in
+the dictionaries. This page outlines the names and functionality of
+those data types.
+
+== Basic Data Types
+
+There are a number of "basic" data types. These data types are
+fixed-size, and encapsulate simple concepts such as "integer" or "IP
+address".
+
+Basic data types can be used in `unlang`, as they contain simple
+values which can be compared, or assigned to one attribute. In most
+cases, it is not necessary to know the name of the data type. It is
+possible to write values in the format you expect, The server will do
+"the right thing" when interpreting the values.
+
+.Basic Data Types
+[options="header"]
+[cols="15%,85%"]
+|=====
+| Data Type | Description
+| bool | boolean
+| date | calendar date
+| ethernet | Ethernet address
+| float32 | 32-bit floating point number
+| float64 | 64-bit floating point number
+| ifid | interface ID
+| int8 | 8-bit signed integer
+| int16 | 16-bit signed integer
+| int32 | 32-bit signed integer
+| int64 | 64-bit signed integer
+| ipaddr | IPv4 address
+| ipv6addr | IPv6 address
+| ipv4prefix | IPv4 network with address and prefix length
+| ipv6prefix | IPv6 network with address and prefix length
+| octets | raw binary, printed as hex strings
+| string | printable strings
+| time_delta | difference between two calendar dates
+| uint8 | 8-bit unsigned integer
+| uint16 | 16-bit unsigned integer
+| uint32 | 32-bit unsigned integer
+| uint64 | 64-bit unsigned integer
+|=====
+
+=== Structural Data Types
+
+The following data types are "structural", in that they form
+parent-child relationships between attributes. These data types can
+only be used in the dictionaries. They cannot be used in `unlang`
+statements.
+
+.Structural Data Types
+[options="header"]
+[cols="15%,85%"]
+|=====
+| Data Type | Description
+| struct | structure which contains fixed-width fields
+| tlv | type-length-value which contains other attributes
+| vsa | Encapsulation of vendor-specific attributes
+|=====
+
+=== Protocol-Specific Data Types
+
+The following data types are used only in certain protocols. These
+data types can be used only in the dictionaries. They cannot be used
+in `unlang` statements.
+
+.Protocol Specific Data Types
+[options="header"]
+[cols="15%,15%,70%"]
+|=====
+| Data Type | Protocol | Description
+| abinary | RADIUS | Ascend binary filters
+| extended | RADIUS | attributes which "extend" the number space
+|=====
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS
diff --git a/doc/antora/modules/unlang/pages/type/double.adoc b/doc/antora/modules/unlang/pages/type/double.adoc
new file mode 100644
index 0000000..6c3e708
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/type/double.adoc
@@ -0,0 +1,39 @@
+= Double-Quoted Strings
+
+.Syntax
+`"string"`
+
+A double-quoted string is interpreted via the usual rules in
+programming languages for double quoted strings. The double-quote
+character can be placed in a string by escaping it with a backslash.
+Carriage returns and line-feeds can also be used via the usual `\r` and
+`\n` syntax.
+
+The main difference between the single and double quoted strings is
+that the double quoted strings can be dynamically expanded. The syntax
+`${...}` is used for parse-time expansion and `%{...}` is used for
+run-time expansion. The difference between the two methods is that the
+`${...}` form is expanded when the server loads the configuration
+files and is valid anywhere in the configuration files. The `%{...}`
+link:xlat.adoc[string expansion] form is valid only in conditional
+expressions and attribute assignments.
+
+The output of the dynamic expansion can be interpreted as a string,
+a number, or an IP address, depending on its context.
+
+Note that the interpretation of text _strongly_ depends on the
+context. The text `"0000"` can be interpreted as a data type
+"integer", having value zero, or a data type "string", having value
+`"0000"`. In general when a particular piece of text is used, it is
+used with the context of a known attribute. That attribute has a
+link:data.adoc[data type], and the text will be interpreted as that
+data type.
+
+.Examples
+
+`"word"` +
+`"a string"` +
+`"this has embedded\ncharacters"`
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/type/index.adoc b/doc/antora/modules/unlang/pages/type/index.adoc
new file mode 100644
index 0000000..7d0d70f
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/type/index.adoc
@@ -0,0 +1,117 @@
+= Data Types
+
+Unlang supports a number of data types. These data types are used in
+conditional expressions or when assigning a value to an attribute.
+
+== Using Data Types
+
+The server support a wide range of data types, as given in the
+xref:unlang/type/all_types.adoc[list of data types] page. The choice
+of which data type applies is determined by the context in which that
+data type is used. This context is usually taken from an attribute
+which is being assigned a value.
+
+The `unlang` interpreter uses pre-defined attributes which are defined
+in dictionaries. The dictionaries define both a name, and a data type
+for the attributes. In the interpreter, then, attributes can be
+assigned a value or compared to a value, without specifying the data
+type. The interpreter knows how to parse the value by using the data
+type assigned to the attribute.
+
+The result is that in most cases, it is not necessary to know the name
+of the data types. It is possible to write values in the format you
+expect, and he server will do "the right thing" when interpreting the
+values.
+
+.Attributes with Different Data Types
+[source,unlang]
+----
+Framed-IP-Address = 192.0.2.1
+Framed-IPv6-Address = 2001:db8::
+Reply-Message = "This is a reply"
+Port-Limit = 5
+Boolean = true
+Octets-Thing = 0xabcdef0102030405
+MAC-Address = 00:01:02:03:04:05
+----
+
+== Parsing Data Types
+
+The interpreter is flexible when parsing data types. So long as the
+value can be parsed as the given data type without error, the value
+will be accepted.
+
+For example, a particular attribute may be of data type `ipaddr` in
+order to store IPv4 addresses. The interpreter will then accept the
+following strings as valid IPv4 addresses:
+
+`192.168.0.2`:: xref:type/string/unquoted.adoc[Unquoted text], interpreted as the data type
+
+`'192.168.0.2'`:: xref:type/string/single.adoc[Single-quoted string], the contents of the string are parsed as the data type.
++
+The single-quoted string form is most useful when the data type
+contains special characters that may otherwise confuse the parser.
+
+`"192.168.0.2"`:: xref:type/string/double.adoc[Double-quoted string].
++
+The contents of the string are dynamically expanded as described in
+the xref:unlang/xlat/index.adoc[dynamic expansion] page. The
+resulting output is then interpreted as the given data type.
+
+`{backtick}/bin/echo 192.168.0.2{backtick}`:: xref:type/string/backticks.adoc[backtick-quoted string].
+Run a script, and parse the resulting string as the data type.
+
+Similar processing rules are applied when parsing assignments and
+comparisons, for all attributes and data types.
+
+=== Casting Data Types
+
+In some cases, it is necessary to parse values which do not refer to
+attributes. This situation usually occurs when two values need to be
+compared, as in the following example:
+
+[source,unlang]
+----
+if ("%{sql:SELECT ipaddress FROM table WHERE user=%{User-Name}}" == 192.0.2.1) }
+ ....
+}
+----
+
+Since there is no attribute on either side of the `==` operator, the
+interpreter has no way of knowing that the string `192.0.2.1` is an IP
+address. There is unfortunately no way of automatically parsing
+strings in order to determine the data type to use. Any such
+automatic parsing would work most of the time, but it would have
+error cases where the parsing was incorrect.
+
+The solution is to resolve these ambiguities by allowing the values to
+be cast to a particular type. Casting a value to a type tells the
+interpreter how that value should be parsed. Casting is done by
+prefixing a value with the type name, surrounded by angle brackets;
+`<...>`.
+
+.Syntax
+----
+<...>value
+----
+
+We can add a cast to the above example, as follows:
+
+[source,unlang]
+----
+if ("%{sql:SELECT ipaddress FROM table WHERE user=%{User-Name}}" == <ipaddr>192.0.2.1) }
+ ....
+}
+----
+
+In this example, we prefix the IP address with the string `<ipaddr>`.
+The interpreter then knows that the value `192.0.2.` should be
+interpreted as the data type `ipaddr`, and not as the literal string
+`"192.0.2."`.
+
+For a full list of data types which can be used in a cast, please see
+the xref:unlang/type/all_types.adoc[list of data types] page, and the
+"Basic Type Types" section.
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/type/ip.adoc b/doc/antora/modules/unlang/pages/type/ip.adoc
new file mode 100644
index 0000000..fc25ae8
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/type/ip.adoc
@@ -0,0 +1,15 @@
+= IP Addresses
+
+.Examples
+
+`192.0.2.16` +
+`::1` +
+`example.com`
+
+Depending on the context, a "simple word", as above, may be
+interpreted as an IPv4 or an IPv6 address. This interpretation is
+usually done when the string is used in the context of an attribute,
+or to compare two addresses or assign an address to an attribute.
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/type/numb.adoc b/doc/antora/modules/unlang/pages/type/numb.adoc
new file mode 100644
index 0000000..284cf81
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/type/numb.adoc
@@ -0,0 +1,11 @@
+= Numbers
+
+.Examples
+
+`0` +
+`563`
+
+Numbers are unsigned integers that are composed of decimal digits.
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/type/string/backticks.adoc b/doc/antora/modules/unlang/pages/type/string/backticks.adoc
new file mode 100644
index 0000000..9372b4c
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/type/string/backticks.adoc
@@ -0,0 +1,38 @@
+= Backtick-quoted string
+
+.Syntax
+`{backtick}string{backtick}`
+
+The backtick operator is used to perform a run-time expansion
+similar to what is done with the Unix shell. The contents of the string
+are split into one or more sub-strings, based on intermediate
+whitespace. Each substring is then expanded as described above for
+double quoted strings. The resulting set of strings is used to execute a
+program with the associated arguments.
+
+The output of the program is recorded, and the resulting data is
+used in place of the input string value. Where the output is composed of
+multiple lines, any carriage returns and line feeds are replaced by
+spaces.
+
+For safety reasons, the full path to the executed program should be
+given. In addition, the string is split into arguments _before_ the
+substrings are dynamically expanded. This step is done both to allow
+the substrings to contain spaces, and to prevent spaces in the
+expanded substrings from affecting the number of command-line
+arguments.
+
+For performance reasons, we recommend that the use of back-quoted
+strings be kept to a minimum. Executing external programs is
+relatively expensive, and executing a large number of programs for
+every request can quickly use all of the CPU time in a server. If many
+programs need to be executed, it is suggested that alternative ways to
+achieve the same result be found. In some cases, using a real
+programming language such as `lua`, `perl` or `python` may be better.
+
+.Examples
+
+`{backtick}/bin/echo hello{backtick}`
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/type/string/double.adoc b/doc/antora/modules/unlang/pages/type/string/double.adoc
new file mode 100644
index 0000000..ea87bc5
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/type/string/double.adoc
@@ -0,0 +1,68 @@
+= Double Quoted Strings
+
+.Syntax
+`"string"`
+
+A double-quoted string allows escape sequences and xref:xlat/index.adoc[dynamic
+expansions]. As with xref:type/string/single.adoc[single-quoted strings], text
+within double quotes can include spaces.
+
+The main difference between the single and double quoted strings is
+that the double quoted strings can be dynamically expanded. The syntax
+`${...}` is used for parse-time expansion and `%{...}` is used for
+run-time expansion. The difference between the two methods is that the
+`${...}` form is expanded when the server loads the configuration
+files and is valid anywhere in the configuration files. The `%{...}`
+xref:xlat/index.adoc[string expansion] form is valid only in conditional
+expressions and attribute assignments.
+
+The output of the dynamic expansion can be interpreted as a string,
+a number, or an IP address, depending on its context.
+
+Note that the interpretation of text _strongly_ depends on the
+context. The text `"0000"` can be interpreted as a data type
+"integer", having value zero, or a data type "string", having value
+`"0000"`. In general when a particular piece of text is used, it is
+used with the context of a known attribute. That attribute has a
+xref:type/index.adoc[data type], and the text will be interpreted as that
+data type.
+
+NOTE: Most values retrieved from external datastores will be treated implicitly
+as double-quoted strings.
+
+== Escape sequences
+
+Escape sequences allow the inclusion of characters that may be difficult to
+represent in datastores, or the FreeRADIUS configuration files.
+
+.Escape sequences and their descriptions
+[options="header", cols="15%,85%"]
+|=====
+| Escape sequence | Character represented
+| `\\` | Literal backslash (0x5c)
+| `\r` | Carriage return (0x0d)
+| `\n` | Line feed (0x0a)
+| `\t` | Horizontal tab (0x09)
+| `\"` | Double quote (0x22)
+| `\x<hex><hex>` | A byte whose numerical value is given by `<hex><hex>` interpreted as a hexadecimal number.
+| `\x<oct><oct><oct>` | A byte whose numerical value is given by `<oct><oct><oct>` interpreted as an octal number.
+|=====
+
+.Examples
+
+`"word"` +
+`"a string"' +
+`"foo\"bar\""` +
+`"this is a long string"` +
+`"this has embedded\ncharacters"` +
+`"attribute\tvalue\nusername\t%{User-Name}\nreply-message\t%{reply.Reply-Message}"`
+`"The result of 'SELECT * FROM foo WHERE 1' is: %{sql:SELECT * FROM foo WHERE 1}"`
+
+// Licenced under CC-by-NC 4.0.
+// Copyright (C) 2019 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+// Copyright (C) 2019 The FreeRADIUS project.
+// Copyright (C) 2020 Network RADIUS SAS.
+
+
+
+
diff --git a/doc/antora/modules/unlang/pages/type/string/escaping.adoc b/doc/antora/modules/unlang/pages/type/string/escaping.adoc
new file mode 100644
index 0000000..e63a498
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/type/string/escaping.adoc
@@ -0,0 +1,14 @@
+= Character Escaping
+
+The quotation characters in the above string data types can be
+escaped by using the backslash, or `\,` character. The backslash
+character itself can be created by using `\\`. Carriage returns and
+line feeds can be created by using `\n` and `\r`.
+
+.Examples
+
+`"I say \"hello\" to you"` +
+`"This is split\nacross two lines"`
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/type/string/single.adoc b/doc/antora/modules/unlang/pages/type/string/single.adoc
new file mode 100644
index 0000000..fa2ac05
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/type/string/single.adoc
@@ -0,0 +1,19 @@
+= Single Quoted Strings
+
+.Syntax
+`'string'`
+
+A single-quoted string is interpreted without any dynamic string
+expansion. The quotes allow the string to contain spaces, among other
+special characters. The single quote character can be placed in such a
+string by escaping it with a backslash.
+
+.Examples
+
+`'hello'` +
+`'foo bar`' +
+`'foo\\'bar'` +
+`'this is a long string'`
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/type/string/unquoted.adoc b/doc/antora/modules/unlang/pages/type/string/unquoted.adoc
new file mode 100644
index 0000000..9dd6e55
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/type/string/unquoted.adoc
@@ -0,0 +1,21 @@
+= Unquoted Strings
+
+Where a series of characters cannot be parsed as a decimal number,
+they are interpreted as a simple string composed of one word. This
+word is delimited by spaces, or by other tokens, such as `)` in
+conditional expressions.
+
+This unquoted text is interpreted as simple strings and are generally
+equivalent to placing the string in single quotes.
+
+The interpretation of the text depends on the context, which is
+usually defined by an attribute which has a xref:type/index.adoc[data type].
+
+.Examples
+
+`Hello` +
+`192.168.0.1` +
+`00:01:02:03:04:05`
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/update.adoc b/doc/antora/modules/unlang/pages/update.adoc
new file mode 100644
index 0000000..645f4d8
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/update.adoc
@@ -0,0 +1,160 @@
+= The update Statement
+
+.Syntax
+[source,unlang]
+----
+update [ <list> ] {
+ <server-attribute> <op> <value>
+ ...
+}
+----
+
+The `update` statement adds attributes to, or edits the attributes in,
+the named _<list>_.
+
+The `update` statement consists of the following syntax elements:
+
+<list>:: The attribute list which will be updated. The list is
+usually `request`, `reply`, or `control`.
++
+If the _<list>_ qualifier is omitted, then each entry inside of the
+`update` section *must* be prefixed with a list name. For example,
+`&request.User-Name ...`
+
+<server-attribute>:: The server attribute which is assigned the
+_<value>_.
+
+<op>:: The operator such as `=`, `:=`, etc.
+
+<value>:: The value which is assigned to the attribute. If the field
+is a double-quoted string, it undergoes xref:xlat/index.adoc[string
+expansion], and the resulting value is assigned to the attribute.
+
+The update process is atomic, in that either all of the attributes are
+modified, or none of them are modified. If the `update` fails for any
+reason, then all of the results are discarded, and the `update` does
+not affect any server attributes.
+
+.Example
+[source,unlang]
+----
+update reply {
+ &Reply-Message := "Hello!"
+ &Framed-IP-Address := 192.0.2.4
+}
+----
+
+== Lists
+
+The _<list>_ field sets the attribute list that will be updated. If
+the _<list>_ qualifier is omitted, then each entry inside of the
+`update` section *must* be prefixed with a list name. For example,
+`&request.User-Name ...`
+
+Please see the xref:list.adoc[list] page for valid list names.
+
+== Server Attributes
+
+The _<server-attribute>_ field is an attribute name, such as
+`&Reply-Message`. The attribute name may also be prefixed with a
+_<list>_ qualifier, which overrides the _<list>_ given at the start
+of the `update` section.
+
+NOTE: In version 3, the leading `&` is optional but recommended.
+
+== Editing Operators
+
+The `<op>` field is used to define how the attribute is processed.
+Different operators allow attributes to be added, deleted, or
+replaced, as defined below.
+
+.Editing Operators
+[options="header"]
+[cols="10%,90%"]
+|=====
+| Operator | Description
+| = | Add the attribute to the list, if and only if an attribute of
+the same name is not already present in that list.
+| := | Add the attribute to the list. If any attribute of the same
+name is already present in that list, its value is replaced with the
+value of the current attribute.
+| += | Add the attribute to the tail of the list, even if attributes
+of the same name are already present in the list.
+| ^= | Add the attribute to the head of the list, even if attributes
+of the same name are already present in the list.
+| -= | Remove all attributes from the list that match _<value>_.
+| !* | Delete all occurances of the attribute, no matter what the value.
+|=====
+
+== Filtering Operators
+
+The following operators may also be used in addition to the ones
+listed above. These operators use the _<server-attribute>_ and
+_<value>_ fields to enforce limits on all attributes in the given
+_<list>_, and to edit attributes which have a matching
+_<server-attribute>_ name. All other attributes are ignored.
+
+.Filtering Operators
+[options="header]
+[cols="10%,90%"]
+|=====
+| Operator | Description
+| == | Keep only the attributes in the list that match _<value>_
+| < | Keep only the attributes in the list that have values less than _<value>_.
+| \<= | Keep only the attributes in the list that have values less than or equal to _<value>_.
+| > | Keep only the attributes in the list that have values greater than _<value>_.
+| >= | Keep only the attributes in the list that have values greater than or equal to _<value>_.
+| =~ | Keep only the attributes in the list which match the regular expression given in _<value>_.
+| !~ | Keep only the attributes in the list which do not match the regular expression given in _<value>_.
+|=====
+
+The `==` operator is very different from the `=` operator listed
+above. The `=` operator is used to add new attributes to the list,
+while the `==` operator removes all attributes that do not match the
+given value.
+
+The comparison operators `<`, `<=`, `>`, and `>=` have some additional
+side effects. Any non-matching value is replaced by the _<value>_
+given here. If no attribute exists, it is created with the given
+_<value>_.
+
+For IP addresses, the operators `>`, `>=`, `<`, and `\<=` check for
+membership in a network. The _<value>_ field should then be a IP
+network, given in `address/mask` format.
+
+.Example
+[source,unlang]
+----
+update reply {
+ &Session-timeout := 86400
+}
+----
+
+.Example
+[source,unlang]
+----
+update reply {
+ &Reply-Message += "Rejected: Also, realm does not end with ac.uk"
+}
+----
+
+== Values
+
+The _<value>_ field is the value which is assigned to the
+_<server-attribute>_. The interpretation of the _<value>_ field
+depends on the data type of the contents. For example, if the string
+`"192.0.2.1"` is assigned to an attribute of the `string` data type,
+then the result is an ASCII string containing that value. However, if
+the same string is assigned to an attribute of the `ipaddr` data type,
+then the result is a 32-bit IPv4 address, with binary value `0xc0000201`.
+
+.Example
+[source,unlang]
+----
+update reply {
+ &Session-Timeout <= 3600
+}
+----
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/xlat/alternation.adoc b/doc/antora/modules/unlang/pages/xlat/alternation.adoc
new file mode 100644
index 0000000..adb7604
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/xlat/alternation.adoc
@@ -0,0 +1,24 @@
+= Alternation Syntax
+
+Alternation syntax similar to that used in Unix shells may also be
+used:
+
+`%{%{Foo}:-bar}`
+
+This code returns the value of `%{Foo}`, if it has a value.
+Otherwise, it returns a literal string bar.
+
+`%{%{Foo}:-%{Bar}}`
+
+This code returns the value of `%{Foo}`, if it has a value.
+Otherwise, it returns the expansion of `%{Bar}`.
+
+These conditional expansions can be nested to almost any depth, such
+as with `%{%{One}:-%{%{Two}:-%{Three}}}`.
+
+.Examples
+`%{%{Stripped-User-Name}:-%{User-Name}}` +
+`%{%{Framed-IP-Address}:-<none>}`
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/xlat/attribute.adoc b/doc/antora/modules/unlang/pages/xlat/attribute.adoc
new file mode 100644
index 0000000..a3ee29b
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/xlat/attribute.adoc
@@ -0,0 +1,54 @@
+= Attribute References
+
+Attributes in a list may be referenced via one of the following two
+syntaxes:
+
+`%{Attribute-Name}` +
+`%{<list>:Attribute-Name}`
+
+The `<list>:` prefix is optional. If given, it must be a valid
+reference to an xref:list.adoc[attribute list].
+
+If the `<list>:` prefix is omitted, then the `request` list is
+assumed.
+
+For EAP methods with tunneled authentication sessions (i.e. PEAP and
+EAP-TTLS), the inner tunnel session can refer to a list for the outer
+session by prefixing the list name with `outer.` ; for example,
+`outer.request`.
+
+When a reference is encountered, the given list is examined for an
+attribute of the given name. If found, the variable reference in the
+string is replaced with the value of that attribute. Otherwise, the
+reference is replacedd with an empty string.
+
+.Examples
+
+`%{User-Name}` +
+`%{request.User-Name} # same as above` +
+`%{reply.User-Name}` +
+`%{outer.request.User-Name} # from inside of a TTLS/PEAP tunnel`
+
+Examples of using references inside of a string:
+
+`"Hello %{User-Name}"` +
+`"You, %{User-Name} are not allowed to use %{NAS-IP-Address}"`
+
+== Additional Variations
+
+`%{Attribute-Name[#]}`::
+Returns an integer containing the number of named attributes
+
+`%{Attribute-Name[0]}`::
+
+When an attribute appears multiple times in a list, this syntax allows
+you to address the attributes as with array entries. `[0]` refers to
+the first attributes, `[1]` refers to the second attribute, etc.
+
+`%{Attribute-Name[*]}`::
+
+Returns a comma-separated string containing all values for the named
+attributes.
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/xlat/builtin.adoc b/doc/antora/modules/unlang/pages/xlat/builtin.adoc
new file mode 100644
index 0000000..f236a57
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/xlat/builtin.adoc
@@ -0,0 +1,891 @@
+= Built-In Expansions
+
+In addition to storing attribute references, the server has a number
+of built-in expansions. These expansions act largely as functions
+which operate on inputs, and produce an output.
+
+
+
+== Attribute Manipulation
+
+=== %{length: ... }
+
+The `length` expansion returns the size of the input as an integer.
+When the input is a string, then the output is identical to the
+`strlen` expansion.
+
+When the input is an attribute reference, the output is the size of
+the attributes data as encoded "on the wire".
+
+.Return: _size_
+
+.Determining the length of fixed and variable length attributes
+====
+[source,unlang]
+----
+update control {
+ &Tmp-String-0 := "Caipirinha"
+ &Framed-IP-Address := 192.0.2.1
+}
+
+update reply {
+ &Reply-Message := "The length of %{control:Tmp-String-0} is %{length:&control:Tmp-String-0}"
+ &Reply-Message += "The length of %{control:Framed-IP-Address} is %{length:&control:Framed-IP-Address}"
+}
+----
+
+.Output
+....
+The length of Caipirinha is 10
+The length of 192.168.0.2 is 4
+....
+====
+
+`length` is built in to the server core.
+
+
+
+=== %{integer:<&ref>}
+
+Print the value of the attribute an integer.
+
+In normal operation, `integer` attributes are printed using the name
+given by a `VALUE` statement in a dictionary. Similarly, date
+attributes are printed as dates, i.e., "January 1 2010.
+
+The `integer` expansion applies only to attributes which can be
+converted to an integer. For all other inputs, it returns `0`.
+
+A common usage is to find the difference between two dates.
+
+For example, if a request contains `Service-Type = Login-User`, the
+expansion of `%{integer:&Service-Type}` will yield `1`, which is the
+value associated with the `Login-User` name. Using
+`%{integer:&Event-Timestamp}` will return the event timestamp as an
+unsigned 32-bit number.
+
+.Return: _string_
+
+.Determining the integer value of an enumerated attribute
+====
+[source,unlang]
+----
+update {
+ &control:Service-Type := Login-User
+}
+update reply {
+ &Reply-Message := "The value of Service-Type is %{integer:&control:Service-Type}"
+}
+----
+
+.Output
+
+```
+The value of Service-Type is 1
+```
+====
+
+`integer` is built in to the server core.
+
+
+
+=== %{rand:<number>}
+
+Generate random number from `0` to `<number>-1`.
+
+.Return: _uint64_
+
+.Generating a random number between 0 and 511
+====
+[source,unlang]
+----
+update reply {
+ &Reply-Message := "The random number is %{rand:512}"
+}
+----
+
+.Output
+
+```
+The random number is 347
+```
+====
+
+`rand` is provided by the `rlm_expr` module.
+
+
+
+=== %{tag:<attribute ref>}
+
+CAUTION: This expansion is deprecated and will likely be removed.
+
+Returns a list of tags for any attributes found using ``<attribute ref>``.
+
+.Return: _int8_
+
+.Determining the tag value of the second instance of the `radius.Tunnel-Server-Endpoint` attribute
+====
+[source,unlang]
+----
+update request {
+ &Tunnel-Server-Endpoint := '192.0.1.1'
+ &Tunnel-Server-Endpoint:1 := '192.0.5.2'
+ &Tunnel-Server-Endpoint:1 += '192.0.3.8'
+ &Tunnel-Server-Endpoint:2 := '192.0.2.1'
+ &Tunnel-Server-Endpoint:2 += '192.0.3.4'
+}
+
+update reply {
+ &Reply-Message := "The tag value of the second instance of Tunnel-Server-Enpoint is %{request:Tunnel-Server-Endpoint[1]}"
+}
+----
+
+.Output
+
+```
+The tag value of the second instance of Tunnel-Server-Enpoint is 192.0.5.2
+```
+====
+
+`tag` is built in to the server core.
+
+
+
+=== %{string:<data>}
+
+Convert input to a string (if possible). For _octets_ type attributes, this
+means interpreting the data as a UTF8 string, and inserting octal escape
+sequences where appropriate.
+
+For other types, this means printing the value in its _presentation_ format,
+i.e. dotted quads for IPv4 addresses, link:https://en.wikipedia.org/wiki/ISO_8601[ISO 8601]
+time for date types, enumeration values for attributes such as `radius.Service-Type` etc.
+
+.Return: _string_
+
+.String interpolation using the raw octets value of Tmp-Octets-0, and the stringified version
+====
+[source,unlang]
+----
+update control {
+ &Tmp-Octets-0 := 0x7465737431
+}
+update reply {
+ &Reply-Message := "The string value of %{control:Tmp-Octets-0} is %{string:%{control:Tmp-Octets-0}}"
+}
+----
+====
+
+.Output
+
+```
+The string value of 0x7465737431 is test1
+```
+
+`string` is built in to the server core.
+
+
+
+== Server Manipulation
+
+=== %{config:<key>}
+
+Refers to a variable in the configuration file. See the documentation
+on configuration file references.
+
+.Return: _string_
+
+.Example
+
+[source,unlang]
+----
+"Server installed in %{config:prefix}"
+"Module rlm_exec.shell_escape = %{config:modules.exec.shell_escape}"
+----
+
+.Output
+
+```
+Server installed in /opt/freeradius
+Module rlm_exec.shell_escape = yes
+```
+
+`config` is built in to the server core.
+
+
+
+=== %{client:<key>}
+
+Refers to a variable that was defined in the client section for the
+current client. See the sections `client { ... }` in `clients.conf`.
+
+.Return: _string_
+
+.Example
+
+[source,unlang]
+----
+"The client ipaddr is %{client:ipaddr}"
+----
+
+.Output
+
+```
+The client ipaddr is 192.168.5.9
+```
+
+`client` is built in to the server core.
+
+
+
+=== %{debug:<level>}
+
+Dynamically change the debug level to something high, recording the old level.
+
+.Return: _string_
+
+.Example
+
+[source,unlang]
+----
+authorize {
+ if (&request:User-Name == "bob") {
+ "%{debug:4}"
+ } else {
+ "%{debug:0}"
+ }
+ ...
+}
+----
+
+.Output (_extra informations only for that condition_)
+
+```
+...
+(0) authorize {
+(0) if (&request:User-Name == "bob") {
+(0) EXPAND %{debug:4}
+(0) --> 2
+(0) } # if (&request:User-Name == "bob") (...)
+(0) filter_username {
+(0) if (&State) {
+(0) ...
+(0) }
+...
+```
+
+`debug` is built in to the server core.
+
+
+
+=== %{debug_attr:<list:[index]>}
+
+Print to debug output all instances of current attribute, or all attributes in a list.
+expands to a zero-length string.
+
+.Return: _string_
+
+.Example
+
+[source,unlang]
+----
+authorize {
+ if (&request:User-Name == "bob") {
+ "%{debug_attr:request[*]}"
+ }
+ ...
+}
+----
+
+.Output
+
+```
+...
+(0) authorize {
+(0) if (&request:User-Name == "bob") {
+(0) Attributes matching "request[*]"
+(0) &request:User-Name = bob
+(0) &request:User-Password = hello
+(0) &request:NAS-IP-Address = 127.0.1.1
+(0) &request:NAS-Port = 1
+(0) &request:Message-Authenticator = 0x9210ee447a9f4c522f5300eb8fc15e14
+(0) EXPAND %{debug_attr:request[*]}
+(0) } # if (&request:User-Name == "bob") (...)
+...
+```
+
+`debug_attr` is built in to the server core.
+
+
+
+== String manipulation
+
+=== %{lpad:<&ref> <val> <char>}
+
+Left-pad a string.
+
+.Return: _string_
+
+.Example
+
+[source,unlang]
+----
+update control {
+ &Tmp-String-0 := "123"
+}
+update reply {
+ &Reply-Message := "Maximum should be %{lpad:&control:Tmp-String-0 11 0}"
+}
+----
+
+.Output
+
+```
+Maximum should be 00000000123
+```
+
+`lpad` is provided by the `rlm_expr` module.
+
+
+
+=== %{rpad:<&ref> <val> <char>}
+
+Right-pad a string.
+
+.Return: _string_
+
+.Example
+
+[source,unlang]
+----
+update control {
+ &Tmp-String-0 := "123"
+}
+update reply {
+ &Reply-Message := "Maximum should be %{rpad:&control:Tmp-String-0 11 0}"
+}
+----
+
+.Output
+
+```
+Maximum should be 12300000000
+```
+
+`rpad` is provided by the `rlm_expr` module.
+
+
+
+=== %{pairs:<&list:[*]>}
+
+Serialize attributes as comma-delimited string.
+
+.Return: _string_
+
+.Example
+
+[source,unlang]
+----
+update {
+ &control:Tmp-String-0 := "This is a string"
+ &control:Tmp-String-0 += "This is another one"
+}
+
+update reply {
+ &Reply-Message := "Serialize output: %{pairs:&control[*]}"
+}
+----
+
+.Output
+
+```
+Serialize output: Tmp-String-0 = \"This is a string\"Tmp-String-0 = \"This is another one\"
+```
+
+`pairs` is provided by the `rlm_expr` module.
+
+
+
+=== %{randstr: ...}
+
+Get random string built from character classes.
+
+.Return: _string_
+
+.Example
+
+[source,unlang]
+----
+update reply {
+ &Reply-Message := "The random string output is %{randstr:aaaaaaaa}"
+}
+----
+
+.Output
+
+```
+The random string output is 4Uq0gPyG
+```
+
+`randstr` is provided by the `rlm_expr` module.
+
+
+
+=== %{strlen: ... }
+
+Length of given string.
+
+.Return: _integer_
+
+.Example
+
+[source,unlang]
+----
+update control {
+ &Tmp-String-0 := "Caipirinha"
+}
+update reply {
+ &Reply-Message := "The length of %{control:Tmp-String-0} is %{strlen:&control:Tmp-String-0}"
+}
+----
+
+.Output
+
+```
+The length of Caipirinha is 21
+```
+
+`strlen` is built in to the server core.
+
+
+
+=== %{tolower: ... }
+
+Dynamically expands the string and returns the lowercase version of
+it. This definition is only available in version 2.1.10 and later.
+
+.Return: _string_
+
+.Example
+
+[source,unlang]
+----
+update control {
+ &Tmp-String-0 := "CAIPIRINHA"
+}
+update reply {
+ &Reply-Message := "tolower of %{control:Tmp-String-0} is %{tolower:%{control:Tmp-String-0}}"
+}
+----
+
+.Output
+
+```
+tolower of CAIPIRINHA is caipirinha
+```
+
+`tolower` is provided by the `rlm_expr` module.
+
+
+
+=== %{toupper: ... }
+
+Dynamically expands the string and returns the uppercase version of
+it. This definition is only available in version 2.1.10 and later.
+
+.Return: _string_
+
+.Example
+
+[source,unlang]
+----
+update control {
+ &Tmp-String-0 := "caipirinha"
+}
+update reply {
+ &Reply-Message := "toupper of %{control:Tmp-String-0} is %{toupper:%{control:Tmp-String-0}}"
+}
+----
+
+.Output
+
+```
+toupper of caipirinha is CAIPIRINHA
+```
+
+`toupper` is provided by the `rlm_expr` module.
+
+
+
+== String Conversion
+
+=== %{base64: ... }
+
+Encode a string using Base64.
+
+.Return: _string_
+
+.Example
+
+[source,unlang]
+----
+update control {
+ &Tmp-String-0 := "Caipirinha"
+}
+update reply {
+ &Reply-Message := "The base64 of %{control:Tmp-String-0} is %{base64:%{control:Tmp-String-0}}"
+}
+----
+
+.Output
+
+```
+The base64 of foo is Q2FpcGlyaW5oYQ==
+```
+
+`base64` is provided by the `rlm_expr` module.
+
+
+
+=== %{base64tohex: ... }
+
+Decode a base64 string (e.g. previously encoded using `base64`) to
+hex.
+
+.Return: _string_
+
+.Example
+
+[source,unlang]
+----
+update control {
+ &Tmp-String-0 := "Q2FpcGlyaW5oYQ=="
+}
+update reply {
+ &Reply-Message := "The base64tohex of %{control:Tmp-String-0} is %{base64tohex:%{control:Tmp-String-0}}"
+}
+----
+
+.Output
+
+```
+The base64decode of Q2FpcGlyaW5oYQ== is 436169706972696e6861
+```
+
+`base64tohex` is provided by the `rlm_expr` module.
+
+
+
+=== %{hex: ... }
+
+Convert to hex.
+
+.Return: _string_
+
+.Example
+
+[source,unlang]
+----
+update control {
+ &Tmp-String-0 := "12345"
+}
+update reply {
+ &Reply-Message := "The value of %{control:Tmp-String-0} in hex is %{hex:%{control:Tmp-String-0}}"
+}
+----
+
+.Output
+
+```
+The value of 12345 in hex is 3132333435
+```
+
+`hex` is built in to the server core.
+
+
+
+=== %{urlquote: ... }
+
+Quote URL special characters.
+
+.Return: _string_.
+
+.Example
+
+[source,unlang]
+----
+update {
+ &control:Tmp-String-0 := "http://example.org/"
+}
+update reply {
+ &Reply-Message += "The urlquote of %{control:Tmp-String-0} is %{urlquote:%{control:Tmp-String-0}}"
+}
+----
+
+.Output
+
+```
+The urlquote of http://example.org/ is http%3A%2F%2Fexample.org%2F
+```
+
+`urlquote` is provided by the `rlm_expr` module.
+
+
+
+=== %{urlunquote: ... }
+
+Unquote URL special characters.
+
+.Return: _string_.
+
+.Example
+
+[source,unlang]
+----
+update {
+ &control:Tmp-String-0 := "http%%3A%%2F%%2Fexample.org%%2F" # Attention for the double %.
+}
+update reply {
+ &Reply-Message += "The urlunquote of %{control:Tmp-String-0} is %{urlunquote:%{control:Tmp-String-0}}"
+}
+----
+
+.Output
+
+```
+The urlunquote of http%3A%2F%2Fexample.org%2F is http://example.org/
+```
+
+`urlunquote` is provided by the `rlm_expr` module.
+
+
+
+== Hashing and Encryption
+
+=== %{hmacmd5:<shared_key> <string>}
+
+Generate `HMAC-MD5` of string.
+
+.Return: _octal_
+
+.Example
+
+[source,unlang]
+----
+update {
+ &control:Tmp-String-0 := "mykey"
+ &control:Tmp-String-1 := "Caipirinha"
+}
+update {
+ &control:Tmp-Octets-0 := "%{hmacmd5:%{control:Tmp-String-0} %{control:Tmp-String-1}}"
+}
+
+update reply {
+ &Reply-Message := "The HMAC-MD5 of %{control:Tmp-String-1} in octets is %{control:Tmp-Octets-0}"
+ &Reply-Message += "The HMAC-MD5 of %{control:Tmp-String-1} in hex is %{hex:control:Tmp-Octets-0}"
+}
+----
+
+.Output
+
+```
+The HMAC-MD5 of Caipirinha in octets is \317}\264@K\216\371\035\304\367\202,c\376\341\203
+The HMAC-MD5 of Caipirinha in hex is 636f6e74726f6c3a546d702d4f63746574732d30
+```
+
+`hmacmd5` is provided by the `rlm_expr` module.
+
+
+
+=== %{hmacsha1:<shared_key> <string>}
+
+Generate `HMAC-SHA1` of string.
+
+.Return: _octal_
+
+.Example
+
+[source,unlang]
+----
+update {
+ &control:Tmp-String-0 := "mykey"
+ &control:Tmp-String-1 := "Caipirinha"
+}
+update {
+ &control:Tmp-Octets-0 := "%{hmacsha1:%{control:Tmp-String-0} %{control:Tmp-String-1}}"
+}
+
+update reply {
+ &Reply-Message := "The HMAC-SHA1 of %{control:Tmp-String-1} in octets is %{control:Tmp-Octets-0}"
+ &Reply-Message += "The HMAC-SHA1 of %{control:Tmp-String-1} in hex is %{hex:control:Tmp-Octets-0}"
+}
+----
+
+.Output
+
+```
+The HMAC-SHA1 of Caipirinha in octets is \311\007\212\234j\355\207\035\225\256\372Ê™>R\"\341\351O)
+The HMAC-SHA1 of Caipirinha in hex is 636f6e74726f6c3a546d702d4f63746574732d30
+```
+
+`hmacsha1` is provided by the `rlm_expr` module.
+
+
+
+=== %{md5: ... }
+
+Dynamically expands the string and performs an MD5 hash on it. The
+result is binary data.
+
+.Return: _binary data_
+
+.Example
+
+[source,unlang]
+----
+update control {
+ &Tmp-String-0 := "Caipirinha"
+}
+update reply {
+ &Reply-Message := "md5 of %{control:Tmp-String-0} is octal=%{md5:%{control:Tmp-String-0}}"
+ &Reply-Message := "md5 of %{control:Tmp-String-0} is hex=%{hex:%{md5:%{control:Tmp-String-0}}}"
+}
+----
+
+.Output
+
+```
+md5 of Caipirinha is octal=\024\204\013md||\230\243\3472\3703\330n\251
+md5 of Caipirinha is hex=14840b6d647c7c98a3e732f833d86ea9
+```
+
+`md5` is provided by the `rlm_expr` module.
+
+
+
+== Miscellaneous Expansions
+
+=== +%{0}+..+%{32}+
+
+`%{0}` expands to the portion of the subject that matched the last regular
+expression evaluated. `%{1}`..`%{32}` expand to the contents of any capture
+groups in the pattern.
+
+Every time a regular expression is evaluated, whether it matches or not,
+the numbered capture group values will be cleared.
+
+
+
+=== +%{regex:<named capture group>}+
+
+Return named subcapture value from the last regular expression evaluated.
+
+Results of named capture groups are available using the `%{regex:<named capture
+group>}` expansion. They will also be accessible using the numbered expansions
+described xref:builtin.adoc#_0_32[above].
+
+Every time a regular expression is evaluated, whether it matches or not,
+the named capture group values will be cleared.
+
+[NOTE]
+====
+This expansion is only available if the server is built with libpcre or libpcre2.
+Use the output of `radiusd -Xxv` to determine which regular expression library in use.
+
+....
+...
+Debug : regex-pcre : no
+Debug : regex-pcre2 : yes
+Debug : regex-posix : no
+Debug : regex-posix-extended : no
+Debug : regex-binsafe : yes
+...
+Debug : pcre2 : 10.33 (2019-04-16) - retrieved at build time
+....
+====
+
+`regex` is built in to the server core.
+
+
+
+=== +%{nexttime:<time>}+
+
+Calculate number of seconds until next n hour(`s`), day(`s`), week(`s`), year(`s`).
+
+.Return: _string_
+
+.Example:
+
+With the current time at 16:18, `%{nexttime:1h}` will expand to `2520`.
+
+[source,unlang]
+----
+update reply {
+ &Reply-Message := "You should wait for %{nexttime:1h}s"
+}
+----
+
+.Output
+
+```
+You should wait for 2520s
+```
+
+`nexttime` is provided by the `rlm_expr` module.
+
+
+
+=== +%{Packet-Type}+
+
+The packet type (`Access-Request`, etc.)
+
+
+
+=== +%{Packet-Src-IP-Address} and %{Packet-Src-IPv6-Address}+
+
+The source IPv4 or IPv6 address of the packet. See also the expansions
+`%{client:ipaddr}` and `%{client:ipv6addr}`. The two expansions
+should be identical, unless `%{client:ipaddr}` contains a DNS hostname.
+
+
+
+=== +%{Packet-Dst-IP-Address} and %{Packet-Dst-IPv6-Address}+
+
+The destination IPv4 or IPv6 address of the packet. See also the
+expansions `%{listen:ipaddr}` and `%{listen:ipv6addr}`. If the socket
+is listening on a "wildcard" address, then these two expansions will be
+different, as follows: the `%{listen:ipaddr}` will be the wildcard
+address and `%{Packet-DST-IP-Address}` will be the unicast address to
+which the packet was sent.
+
+
+
+=== +%{Packet-Src-Port} and %{Packet-Dst-Port}+
+
+The source/destination ports associated with the packet.
+
+.Return: _string_.
+
+.Example
+
+[source,unlang]
+----
+update control {
+ &Tmp-String-0 := "user@example.com"
+}
+
+if (&control:Tmp-String-0 =~ /^(?<login>(.*))@(?<domain>(.*))$/) {
+ update reply {
+ &Reply-Message := "The %{control:Tmp-String-0} { login=%{regex:login}, domain=%{regex:domain} }"
+ }
+}
+----
+
+.Output
+
+```
+The user@example.com { login=user, domain=example.com }
+```
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/xlat/character.adoc b/doc/antora/modules/unlang/pages/xlat/character.adoc
new file mode 100644
index 0000000..84a148c
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/xlat/character.adoc
@@ -0,0 +1,80 @@
+= Single Letter Expansions
+
+The following are single letter expansions. These expansions do not
+use the typical `%{...}` format. Instead, they are short-cuts for
+simple, common cases.
+
+== Miscellaneous
+
+`%%`::
+
+Returns `%`.
+
+
+== Current Time
+
+`%c`::
+
+The current Unix epoch time in seconds. This is an unsigned decimal number.
+It should be used with time-based calculations.
+
+`%C`::
+
+The microsecond component of the current epoch time. This is an unsigned
+decimal number. It should be used with time-based calculations.
+
+
+== Request Time
+
+`%l`::
+
+The Unix timestamp of when the request was received. This is an unsigned
+decimal number. It should be used with time-based calculations.
+
+`%Y`::
+
+Four-digit year when the request was received.
+
+`%m`::
+
+Numeric month when the request was received.
+
+`%d`::
+
+Numeric day of the month when the request was received.
+
+`%H`::
+
+Hour of the day when the request was received.
+
+`%G`::
+
+Minute component of the time when the request was received.
+
+`%e`::
+
+Second component of the time when the request was received.
+
+`%M`::
+
+Microsecond component of the time when the request was received.
+
+`%D`::
+
+Request date in the format `YYYYMMDD`.
+
+`%S`::
+
+Request timestamp in SQL format, `YYYY-mmm-ddd HH:MM:SS`.
+
+`%t`::
+
+Request timestamp in _ctime_ format, `Www Mmm dd HH:MM:SS YYYY`.
+
+`%T`::
+
+Request timestamp in ISO format, `YYYY-mm-ddTHH:MM:SS.000`.
+
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/xlat/index.adoc b/doc/antora/modules/unlang/pages/xlat/index.adoc
new file mode 100644
index 0000000..b42f725
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/xlat/index.adoc
@@ -0,0 +1,56 @@
+= String Expansion
+
+String expansion is a feature that allows strings to dynamically
+define their value at run time. For historical reasons, these string
+expansions are called "xlats".
+
+String expansion is performed via the following syntax:
+
+`%{...}`
+
+Where the `%{` signals the start of a dynamic expansion, and `}`
+signals the end of the dynamic expansion. The contents of the
+expansion can be many things:
+
+.String Expansions
+[options="header"]
+|=====
+| Keyword | Description
+| xref:xlat/attribute.adoc[attributes] | Expand the value of a named attribute.
+| xref:xlat/character.adoc[single character] | Single character expansions.
+| xref:xlat/module.adoc[modules] | Pass a string to a module such as `sql`.
+| xref:xlat/alternation.adoc[condition] | Conditionally expand a string.
+| xref:xlat/builtin.adoc[built-in expansions] | Such as string length, tolower, etc...
+|=====
+
+This feature is used to create policies which refer to concepts rather
+than to specific values. For example, a policy can be created that
+refers to the User-Name in a request, via:
+
+`%{User-Name}`
+
+This string expansion is done only for double-quoted strings and for
+the back-tick operator.
+
+== Caveats
+
+Unlike other languages, there is no way to define new variables. All
+of the string expansions must refer to attributes that already exist,
+or to modules that will return a string value.
+
+== Character Escaping
+
+Some characters need to be escaped within a dynamically expanded
+string `%{...}`. The `%` character is used for variable expansion, so a
+literal `%` character can be created by using `%%`.
+
+Other than within a dynamically expanded string, very little
+character escaping is needed. The rules of the enclosing string context
+determine whether or not a space or " character needs to be escaped.
+
+.Example
+
+`Reply-Message := "%{User-Name} with a literal %%`
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/pages/xlat/module.adoc b/doc/antora/modules/unlang/pages/xlat/module.adoc
new file mode 100644
index 0000000..3ce4322
--- /dev/null
+++ b/doc/antora/modules/unlang/pages/xlat/module.adoc
@@ -0,0 +1,18 @@
+= Module References
+
+Individual modules may be referenced via the following syntax:
+
+`%{module:string}`
+
+These references are allowed only by a small number of modules that
+usually perform database lookups. The module name is the actual name of
+the module, as described earlier. The string portion is specific to each
+module and is not documented here. It is, however, usually dynamically
+expanded to allow for additional flexibility.
+
+.Examples
+
+`%{sql:SELECT name FROM mytable WHERE username = %{User-Name}}`
+
+// Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
diff --git a/doc/antora/modules/unlang/partials/rcode_table.adoc b/doc/antora/modules/unlang/partials/rcode_table.adoc
new file mode 100644
index 0000000..e114e74
--- /dev/null
+++ b/doc/antora/modules/unlang/partials/rcode_table.adoc
@@ -0,0 +1,39 @@
+[options="header"]
+[cols="15%,85%"]
+|=====
+| Return code | Description
+| `fail` | The operation failed. Usually as a result of an
+ external dependency like a database being unavailable
+ or an internal error.
+| `handled` | The request has been "handled", no further policies
+ in the current section should be called, and the section
+ should immediately exit.
+| `invalid` | The request, or operation, was invalid. In the case of
+ requests this usually indicates absent or malformed
+ attribute values.
+| `noop` | The operation did nothing.
+| `notfound` | A 'lookup' operation returned no results.
+| `ok` | Operation completed successfully but did not change any
+ attributes in the request.
+| `reject` | The operation indicates the current request should be
+ 'rejected'. What this actually means is different from
+ protocol to protocol. It usually means that access to
+ the requested resource should be denied, or that the
+ current request should be NAKd. Usually returned when
+ provided credentials were invalid.
+| `updated` | The operation completed successfully and updated one
+ or more attributes in the request.
+| `disallow` | Access to a particular resource is
+ denied. This is similar to `reject` but is the result
+ of an authorizational check failing, as opposed to
+ credentials being incorrect.
+| `yield` | Returned by an operation when execution of a request should
+ be suspended.
+|=====
+
+[NOTE]
+====
+In versions ≤ v3.0.x the `disallow` rcode was called `userlock`. `disallow` and
+`userlock` have an identical meaning. `disallow` will be returned in any
+instance where `userlock` was returned in v3.0.x
+====
diff --git a/doc/bugs b/doc/bugs
new file mode 100644
index 0000000..086ca34
--- /dev/null
+++ b/doc/bugs
@@ -0,0 +1,175 @@
+BUGS
+
+0. INTRODUCTION
+
+ The FreeRADIUS web site is at <URL: https://freeradius.org>, and
+ most information referenced in this document can be found there.
+
+ This is primarily for non-developers of the FreeRADIUS server. If you are
+ able to patch the code to work correctly, then we invite you to join the
+ development list to discuss it. If you're the type who know little about
+ how to code, then this is the place for you!
+
+
+1. YOU FOUND A BUG
+
+ Where the server terminates ungracefully due to a bus error,
+ segmentation violation, or other memory error, you should create
+ a new issue in the issue tracker <URL: http://bugs.freeradius.org>,
+ including information from sections 4 and 5.
+
+ For other issues, you should first discuss them on the users list,
+ to see if anyone can reproduce them. Often there's a simple explanation
+ of why the server behaves as it does, and it's not necessarily a bug in
+ the code, so browse the lists' archives of the last two months, and if
+ you don't see messages about it, ask!
+
+ If the behavior is correct but confusing, we think that's a bug too, and
+ you should file a bug against our documentation.
+
+ For more information about the users list, the lists' archives and the
+ faq, please visit https://freeradius.org/support/
+ Please make sure to READ and RESPECT the house-rules. You will get much
+ better response and much faster if you do!
+
+
+2. CORE DUMPS
+
+ If the server, or one of the accompanying programs core dumps, then
+ you should rebuild the server as follows:
+
+ $ ./configure --enable-developer
+ $ make
+ $ make install
+
+ and then run the program again. You may have to to enable core
+ dumps, via:
+
+ $ ulimit -c unlimited
+
+ When it core dumps, do:
+
+ $ gdb /path/to/executable /path/to/core/file
+
+ Enable logging in gdb via the following commands:
+
+ (gdb) set logging file gdb-radiusd.log
+ (gdb) set logging on
+
+ and follow the instructions in section 4, below.
+
+ You can also enable the "panic_action" given in raddb/radiusd.conf.
+ See the comments in that file for more details about automatically
+ collecting gdb debugging information when the server crashes.
+
+
+3. DEBUGGING A LIVE SERVER
+
+ If you can't get a core dump, or the problem doesn't result in a
+ core dump, you may have to run the server under gdb. To do this,
+ ensure that you have symbols in the binaries (i.e. a non-stripped
+ binary) by re-building the server as described in the previous
+ section. Then, run the server under gdb as follows:
+
+ $ gdb radiusd
+
+ Enable logging in gdb via the following commands:
+
+ (gdb) set logging file gdb-radiusd.log
+ (gdb) set logging on
+
+ Tell gdb to pass any necessary command-line arguments to the
+ server:
+
+ (gdb) set args ...
+
+ Where the "..." are the command-line arguments you normally pass to
+ radiusd. For debugging, you probably want to do:
+
+ (gdb) set args -f
+
+ Then, do:
+
+ (gdb) run
+
+ When something interesting happens, you can hit CTRL-C in the
+ window, and you should be back at the gdb prompt:
+
+ (gdb)
+
+ And follow the instructions in section 4, below.
+
+
+4. OBTAINING USEFUL INFORMATION
+
+ Once you have a core dump loaded into gdb, or FreeRADIUS running under
+ gdb, you may use the commands below to get useful information about
+ the state of the server.
+
+ If the server was built with threads, you can do:
+
+ (gdb) info threads
+
+ Which will give you information about the threads. If the server
+ isn't threaded, that command-line will print a message saying so.
+
+ Then, do:
+
+ (gdb) thread apply all bt full
+
+ If the server isn't threaded, the "thread apply" section isn't necessary
+
+ The output should be printed to the screen, and also sent to the
+ gdb-radiusd.log file.
+
+ You should then submit the information from the log file, along with
+ any server output, the output of radiusd -xv, and information about your
+ operating system to:
+
+ http://bugs.freeradius.org/
+
+ Submitting it to the bug database ensures that the bug report won't
+ get forgotten, and that it will be dealt with in due course.
+
+ You should provide the issue number in any mail sent to the user's list.
+
+
+5. VALGRIND
+
+ On Linux systems, "valgrind" is a useful tool that can catch certain
+ classes of bugs. To use it, run the server voa:
+
+$ valgrind --tool=memcheck --leak-check=full radiusd -Xm
+
+ It will print out certain kinds of errors to the screen. There may
+ be a number of errors related to OpenSSL, dlopen(), or libtldl. We
+ cannot do anything about those problems. However, any errors that are
+ inside of the FreeRADIUS source should be brought to our attention.
+
+
+6. RUNNING WITH "SCREEN"
+
+ If the bug is a crash of the server, and it takes a long time for the
+ crash to happen, perform the following steps:
+
+ * log in as root
+ * open a screen session (https://www.gnu.org/software/screen/)
+ $ screen bash
+ * make sure FreeRADIUS is not running
+ * make sure you have all the debug symbols about, or a debugable
+ version of the server installed
+ * configure screen to log to a file; 'Ctrl-A H'
+ * type 'gdb /path/to/radius' (or /path/to/freeradius on Debian)
+ * at the (gdb) prompt, type 'run -X'
+ * detach from screen 'Ctrl-A D'
+ * when you notice FreeRADIUS has died, reconnect to your screen session
+ $ screen -D -r
+ * at the (gdb) prompt type 'where' or for *lots* of info try
+ 'thread apply all bt full'
+ * tell screen to stop logging, 'Ctrl-A H'
+ * logout from screen
+
+--
+
+FreeRADIUS Project, copyright 2014
+$Id$
diff --git a/doc/concepts/aaa.rst b/doc/concepts/aaa.rst
new file mode 100644
index 0000000..5d897c6
--- /dev/null
+++ b/doc/concepts/aaa.rst
@@ -0,0 +1,99 @@
+Authorization, Authentication, and Accounting request handling
+==============================================================
+
+There are a lot of questions about misconfigured FreeRADIUS servers
+because of misunderstanding of FreeRADIUS operations. This document
+explains how the server operates.
+
+Normally there are 2 steps in processing authentication request coming
+from NAS in FreeRADIUS (plus additional steps to proxy request if we
+use FreeRADIUS as a proxy): authorization and authentication.
+
+
+Authorization
+-------------
+
+Authorization is a process of obtaining information about the user
+from external source (file, database or LDAP), and checking that the
+information in request is enough to authenticate user. Authorization
+modules deal with data sources, so ldap, sql, files, passwd are
+authorization modules.
+
+The authentication method is decided during the authorization phase,
+along with any reply attributes. The reason for this behaviour is
+that for example, a user may not be permitted to use a particular
+authentication method. So during the authorize phase, we can deny
+them the ability to use that kind of authentication.
+
+Authentication
+--------------
+
+Authentication is simply a process of comparing user's credentials in
+request with credentials stored in database. Authentication usually
+deals with password encryption. PAP, CHAP, MS-CHAP are authentication
+modules. Few modules act as both authorization and authentication.
+For example, the MS-CHAP module is normally authentication only, but it
+may be used during authorization to verify that request contains
+MS-CHAP related attribute and only in this case perform MS-CHAP based
+authentication. LDAP is normally an authorization module, but it may
+be used for authentication (In this case FreeRADIUS will authenticate
+user in case he can connect to LDAP server with his account). SQL is
+only an authorization module, as dial-in users are not normally given
+passwords to access an SQL server.
+
+
+Request Processing
+------------------
+
+During authorization and authentication processes, there are 3 lists
+of RADIUS attributes supported by FreeRADIUS: request items, config
+items and reply items. (See 'man 5 users' for additional
+information.) Attributes from the RADIUS authentication request
+packet are included into request items list. Both authorization and
+authentication modules can add attributes into reply items list. These
+attributes will be added to reply will be sent by RADIUS server to
+NAS. There is third list, called config items. It's used for
+internal FreeRADIUS operations, for example to pass some data from
+authorization to authentication module.
+
+Before authorization begins FreeRADIUS creates request items list with
+attributes from request and empty config and reply lists.
+
+An authorization module searches a database with attributes
+(e.g. User-Name) taken from request list as a key, and fetches all
+relevant records. It retrieves 3 types of attributes: check
+attributes, configure attributes and reply attributes. It compares the
+check attributes with attributes from request items. If none of
+database record for this User-Name matches in check attributes with
+request items authorization will fail. If a matching record is found,
+then the configure attributes will be added to configure items, and
+the reply attributes will be added to reply items list. The check
+list may be required if we need to authenticate users with same name
+for different services (for example to treat User1 from NAS1 and User1
+from NAS2 as different users).
+
+There should be at list one configure attribute provided by
+authorization module, called Auth-Type (since this attribute is from
+config items list it can't be in request or reply). This attribute
+decides which module will be used to authenticate the user. The
+Config items also contains information from database required to
+authenticate user, for example valid user's password or it's hash,
+login restrictions, etc.
+
+A quite common mistake is to place the attributes in the wrong lists,
+for example placing Auth-Type, Password, NT-Password etc in the check
+list, or in the reply list. When run in debugging mode, the server
+will normally issue 'WARNING' messages saying that the attributes are
+in the wrong list.
+
+If you place Password into check list and user does cleartext
+authentication it may work, because authorization module compares 2
+cleartext passwords. But if user does some encrypted authentication
+(for example MS-CHAP), then the authorization will fail, because the
+Password in the request items will not match the password in the check
+attributes. You should place Password attribute obtained from
+database into configure items and also place Auth-Type attribute with
+value of 'MS-CHAP' into same list. The same goes for NT-Password
+(before calling MS-CHAP Password attribute should be converted to
+NT-Password, it may be achieved by calling mschap module in
+authorization section after module which does actual authorization).
diff --git a/doc/concepts/proxy.rst b/doc/concepts/proxy.rst
new file mode 100644
index 0000000..1af597a
--- /dev/null
+++ b/doc/concepts/proxy.rst
@@ -0,0 +1,118 @@
+FreeRADIUS as a proxy RADIUS server.
+====================================
+
+
+Introduction
+------------
+
+It is possible to use FreeRADIUS as a proxy RADIUS server. This
+means that it can consult a remote RADIUS server to validate a user.
+This is handy for roaming setups, or for renting ports to someone else.
+
+Files
+-----
+
+If a user logs in with a defined realm syntax, the "realm" portion is
+matched against the configuration to determine how the request should
+be handled. Common realm formats are:
+
+::
+
+ username@realm
+ realm/username
+ username%realm
+ realm\username
+
+The realm parsing syntax ( and search order ) is user definable via the
+realm module config in the ``/etc/raddb/mods-available/realm`` configuration
+file.
+
+You can define multiple instances of the realm module to support multiple
+realm syntax's at the same time. Be sure to pay close attention to the
+search order that you define, as you may inadvertently get unexpected
+behaviour ( by having a user use ``realm1/username@realm2`` for instance ).
+If you need to proxy to IPASS, it should go first, because usernames will
+be in the form ``IPASS/username@realm`` and you want to proxy these users to
+IPASS, not to the realm behind the ``@``.
+
+The realms are configured in the file ``/etc/raddb/proxy.conf``, which is
+included by ``radiusd.conf``. The formats and sample configurations are
+included as comments.
+
+The realm ``DEFAULT`` (without the quotes) matches all realms.
+The realm ``NULL`` matches any requests WITHOUT a realm.
+
+If you set the remote server to ``LOCAL``, the request will be handled
+locally as usual, without sending it to a remote radius server.
+
+There are several options you can add in ``/etc/raddb/proxy.conf``:
+
+- nostrip:
+ By default the realm is stripped from the username before sending it
+ on to the remote radius server. By specifying the "nostrip" option
+ the @realm suffix will not be stripped.
+- hints
+ By default the original username is sent to the remote radius
+ server. By specifying the "hints" option the username will be
+ sent as it is after the "hints" file was processed.
+- notrealm:
+ By default if a realm is matched, it will be proxied to the server
+ specified. However, if you are using Replication functionality, you
+ may want to override this behaviour. This option will prevent a
+ user who enters ``user@foobar`` from being proxied if the ``foobar``
+ realm configuration contains ``notrealm``. This function used to be
+ called ``notsuffix``, and the old syntax is still supported.
+
+The ``/etc/raddb/realms`` file is deprecated and should not be used anymore.
+If you use the ``/etc/raddb/realms`` file to enter realm configurations you will
+need to add the hostname and secret for the remote server in the
+file ``/etc/raddb/clients.conf``.
+It is not recommended to use both the realms file and the proxy.conf file,
+as that could cause confusion.
+
+Accounting
+----------
+
+All accounting data for proxied requests does `not` get stored in the
+standard logfiles, but in a separate directory. The name of this
+directory is the name of the remote radius server.
+
+Remote Server
+----------------
+
+When your server proxies requests to another server, it acts as a NAS for
+the remote server. On the remote server, you need to add the hostname of
+your server and the same secret to ``/etc/raddb/clients.conf`` as well.
+
+As you might not control the remote radius server, you might want to
+control the attributes sent back by the remote server in an Access-Accept
+packet. Have a look at the attrs file for this!
+
+What Happens
+---------------
+The exact thing that happens is this:
+
+- A user logs in with a realm
+- The hints file gets processed as usual
+- The user is checked against the huntgroups file. At this point
+ the user `might` already be rejected.
+- The realm is looked up in the realms file. If it isn't defined,
+ the users file is processed normally.
+- If the ``notrealm`` option is defined, the user is processed
+ locally.
+- The realm is stripped from the username unless ``nostrip`` was
+ set, and the request is sent to a remote radius server. Note that
+ any stripping done in the hints file doesn't have an effect on the
+ username sent to the remote radius server unless you set the
+ ``hints`` option.
+- The remote server replies with Access-Accept or Access-Reject
+
+::
+
+ On Access-Accept: The initial Auth-Type is set to Accept
+ On Access-Reject: The initial Auth-Type is set to Reject
+
+Then the users file is processed as usual. The username used at
+this point is the one after hints file processing (regardless of
+the ``hints`` option). It also includes the realm (regardless of the
+setting of the ``nostrip`` option) unless the realm is ``LOCAL``.
diff --git a/doc/configuration/acct_type.rst b/doc/configuration/acct_type.rst
new file mode 100644
index 0000000..e8abff4
--- /dev/null
+++ b/doc/configuration/acct_type.rst
@@ -0,0 +1,71 @@
+Acct-Type
+=========
+
+FreeRADIUS supports the Acct-Type attribute to select between
+accounting methods based on arbitrary attribute/value pairs contained
+in an accounting packet. Its use follows the same general configuration
+syntax as Auth-Type and Autz-Type. The main difference in configuration
+between Acct-Type and Auth/Autz-Type lies in where the Acct-Type
+method is assigned. With Auth/Autz-Type, the method is typically
+assigned in the 'users' file. The 'users' file, naturally, is not
+processed during the handling of the accounting {} section. However,
+part of the default files {} module is the 'acct_users' file, which
+serves the same purpose as the 'users' file, but applies to accounting
+packets.
+
+For example, a server administrator is responsible for handling the
+accounting data for two different realms, foo.com and bar.com, and
+wishes to use different instances of the SQL module for each. In
+addition, there is one RADIUS client sending accounting data that is
+to be logged only to a specific detail file. Everything else should
+use a third SQL instance.
+
+The acct_users file would look something like this::
+
+ DEFAULT Realm == "foo.com", Acct-Type := "SQLFOO"
+
+ DEFAULT Realm == "bar.com", Acct-Type := "SQLBAR"
+
+ DEFAULT Client-IP-Address == "10.0.0.1", Acct-Type := "OTHERNAS"
+
+And in radiusd.conf::
+
+ $INCLUDE ${confdir}/sql0.conf # Instance named 'sql0'.
+ $INCLUDE ${confdir}/sql1.conf # Instance named 'sql1'.
+ $INCLUDE ${confdir}/sql2.conf # Instance named 'sql2'.
+
+ detail othernas {
+ filename = ${radacctdir}/10.0.0.1/detail-%Y%m%d
+ }
+
+ preacct {
+ suffix # Add the Realm A/V pair.
+ files # Add the Acct-Type A/V pair based on the Realm A/V pair.
+ }
+
+ accounting {
+
+ # If Acct-Type is SQLFOO use the 'sql1' instance of the SQL module.
+
+ Acct-Type SQLFOO {
+ sql1
+ }
+
+ # If Acct-Type is SQLBAR, use the 'sql2' instance of the SQL module.
+
+ Acct-Type SQLBAR {
+ sql2
+ }
+
+ # If Acct-Type is OTHERNAS, use the 'othernas' instance of the detail
+ # module
+
+ Acct-Type OTHERNAS {
+ othernas
+ }
+
+ # If we've made it this far, we haven't matched an Acct-Type, so use
+ # the sql0 instance.
+
+ sql0
+ }
diff --git a/doc/configuration/autz_type.rst b/doc/configuration/autz_type.rst
new file mode 100644
index 0000000..a91e4e5
--- /dev/null
+++ b/doc/configuration/autz_type.rst
@@ -0,0 +1,88 @@
+Autz-Type
+=========
+
+Like Auth-Type for authentication method selection freeradius also
+supports the Autz-Type to select between authorization methods. The only
+problem is that authorization is the first thing to be called when an
+authentication request is handled. As a result we first have to call the
+authorize section without checking for Autz-Type. After that we check for
+Autz-Type and if it exists we call the corresponding subsection in the
+authorize section. In other words the authorize section in radiusd.conf
+should look like this::
+
+ authorize{
+ suffix
+ preprocess
+ # whatever other authorize modules here
+ Autz-Type Ldap{
+ ldap
+ }
+ Autz-Type SQL{
+ sql
+ }
+ files
+ }
+
+What happens is that the first time the authorize section is examined the
+suffix, preprocess and files modules are executed. If Autz-Type is set
+after that the server core checks for any matching Autz-Type subsection.
+If one is found it is called. The users file should look something
+like this::
+
+ DEFAULT Called-Station-Id == "123456789", Autz-Type := Ldap
+
+ DEFAULT Realm == "other.example.com", Autz-Type := SQL
+
+Autz-Type could also be used to select between multiple instances of
+a module (ie sql or ldap) which have been configured differently. For
+example based on the user realm different ldap servers (belonging to
+different companies) could be queried. If Auth-Type was also set then we
+could do both Authentication and Authorization with the user databases
+belonging to other companies. In detail:
+
+radiusd.conf::
+
+ authenticate{
+ Auth-Type customer1{
+ ldap1
+ }
+ Auth-Type customer2{
+ ldap2
+ }
+ }
+
+ authorize{
+ preprocess
+ suffix
+ Autz-Type customer1{
+ ldap1
+ }
+ Autz-Type customer2{
+ ldap2
+ }
+ files
+ }
+
+The users file::
+
+ DEFAULT Realm == "customer1", Autz-Type := customer1, Auth-Type := customer1
+
+ DEFAULT Realm == "customer2", Autz-Type := customer2, Auth-Type := customer2
+
+
+Apart from Autz-Type the server also supports the use of
+Acct-Type, Session-Type and Post-Auth-Type for the corresponding sections.
+The corresponding section names in the radiusd.conf file are the same. So for example:
+
+users file::
+
+ DEFAULT Called-Station-Id == "236473", Session-Type := SQL
+
+radiusd.conf::
+
+ session {
+ radutmp
+ Session-Type SQL {
+ sql
+ }
+ }
diff --git a/doc/configuration/configurable_failover.rst b/doc/configuration/configurable_failover.rst
new file mode 100644
index 0000000..4e21335
--- /dev/null
+++ b/doc/configuration/configurable_failover.rst
@@ -0,0 +1,476 @@
+Configurable Module Fail Over
+=============================
+
+Before configurable module failover, we had this kind of entry in
+``radiusd.conf``:
+
+::
+
+ #---
+ authorize {
+ preprocess
+ files
+ }
+ #---
+
+This entry instructed the ``authorize`` section to first process the
+request through the ``preprocess`` module, and if that returned success,
+to process it through ``files`` module. If that sequence returned
+success, then the ``authorize`` stage itself would then return success.
+Processing was strictly linear and if one module failed, the whole
+section would fail immediately.
+
+Configurable failover provides more flexibility. It takes advantage
+of the tree structure of radiusd.conf to support a configuration
+language that allows you to ``group`` modules that should work together
+in ways other than simple lists. You can control the flow of any
+stage (e.g. ``authorize``) to fit your needs, without touching C code,
+just by altering radiusd.conf.
+
+This configurable fail-over has a convenient short-hand, too.
+Administrators commonly want to say things like "try SQL1, if it's
+down, try SQL2, otherwise drop the request."
+
+For example:
+
+::
+
+ #---
+ modules {
+ sql sql1 {
+ # configuration to connect to SQL database one
+ }
+ sql sql2 {
+ # configuration to connect to SQL database two
+ }
+ always handled {
+ rcode = handled
+ }
+ }
+
+ # Handle accounting packets
+ accounting {
+ detail # always log to detail, stopping if it fails
+ redundant {
+ sql1 # try module sql1
+ sql2 # if that's down, try module sql2
+ handled # otherwise drop the request as
+ # it's been ``handled`` by the ``always``
+ # module (see doc/rlm_always)
+ }
+ }
+ #---
+
+The ``redundant`` section is a configuration directive which tells the
+server to process the second module if the first one fails. Any
+number of modules can be listed in a ``redundant`` section. The server
+will process each in turn, until one of the modules succeeds. It will then stop processing the ``redundant`` list.
+
+Rewriting results for single modules
+------------------------------------
+
+Normally, when a module fails, the entire section (``authorize``,
+``accounting``, etc.) stops being processed. In some cases, we may want
+to permit "soft failures". That is, we may want to tell the server
+that it is "ok" for a module to fail, and that the failure should not
+be treated as a fatal error.
+
+In this case, the module is treated as a "section", rather than just
+as a single lne in ``radiusd.conf``. The configuration entries for
+that section are taken from the ``configurable fail-over`` code, and not
+from the configuration information for that module.
+
+For example, the ``detail`` module normally returns ``fail`` if it is
+unable to write its information to the ``detail`` file. As a test, we
+can configure the server so that it continues processing the request,
+even if the ``detail`` module fails. The following example shows how:
+
+::
+
+ #--
+ # Handle accounting packets
+ accounting {
+ detail {
+ fail = 1
+ }
+ redundant {
+ sql1
+ sql2
+ handled
+ }
+ }
+ #--
+
+The ``fail = 1`` entry tells the server to remember the ``fail`` code,
+with priority ``1``. The normal configuration is ``fail = return``, which
+means ``if the detail module fails, stop processing the accounting
+section``.
+
+Fail-over configuration entries
+-------------------------------
+
+Modules normally return on of the following codes as their result:
+
++-----------+-----------------------------------------------------+
+|Code | Meaning |
++===========+=====================================================+
+|notfound | the user was not found |
++-----------+-----------------------------------------------------+
+|noop | the module did nothing |
++-----------+-----------------------------------------------------+
+|ok | the module succeeded |
++-----------+-----------------------------------------------------+
+|updated | the module updated information in the request |
++-----------+-----------------------------------------------------+
+|fail | the module failed |
++-----------+-----------------------------------------------------+
+|reject | the module rejected the user |
++-----------+-----------------------------------------------------+
+|userlock | the user was locked out |
++-----------+-----------------------------------------------------+
+|invalid | the user's configuration entry was invalid |
++-----------+-----------------------------------------------------+
+|handled | the module has done everything to handle the request|
++-----------+-----------------------------------------------------+
+
+In a configurable fail-over section, each of these codes may be
+listed, with a value. If the code is not listed, or a configurable
+fail-over section is not defined, then values that make sense for the
+requested ``group`` (group, redundant, load-balance, etc) are used.
+
+The special code ``default`` can be used to set all return codes to
+the specified value. This value will be used with a lower priority
+than ones that are explicitly set.
+
+The values for each code may be one of two things:
+
++---------+---------------------------------------------------------------+
+|Value | Meaning |
++=========+===============================================================+
+|<number> | Priority for this return code. |
++---------+---------------------------------------------------------------+
+|return | Stop processing this configurable fail-over list. |
++---------+---------------------------------------------------------------+
+|reject | Stop processing this configurable fail-over list and |
+| | immediately return a reject. |
++---------+---------------------------------------------------------------+
+
+The ``<number>`` used for a value may be any decimal number between 1
+and 99999. The number is used when processing a list of modules, to
+determine which code is returned from the list. For example, if
+``module1`` returns ``fail`` with priority ``1``, and a later ``module2``
+returns ``ok`` with priority ``3``, the return code from the list of
+modules will be ``ok``, because it has higher priority than ``fail``.
+
+This configurability allows the administrator to permit some modules
+to fail, so long as a later module succeeds.
+
+
+More Complex Configurations
+---------------------------
+
+The ``authorize`` section is normally a list of module names. We can
+create sub-lists by using the section name ``group``. The ``redundant``
+section above is just a short-hand for ``group``, with a set of default
+return codes, which are different than the normal ``stop processing the
+list on failure``.
+
+For example, we can configure two detail modules, and allow either
+to fail, so long as one of them succeeds.
+
+::
+
+ #--
+ # Handle accounting packets
+ accounting {
+ group {
+ detail1 {
+ fail = 1 # remember ``fail`` with priority 1
+ ok = return # if we succeed, don't do ``detail2``
+ }
+ detail2 {
+ fail = 1 # remember ``fail`` with priority 1
+ ok = return # if we succeed, return ``ok``
+ # if ``detail1`` returned ``fail``
+ }
+ } # returns ``fail`` only if BOTH modules returned ``fail``
+ redundant {
+ sql1
+ sql2
+ handled
+ }
+ }
+ #--
+
+This configuration says:
+
+ - log to ``detail1``, and stop processing the ``group`` list if ``detail1`` returned OK.
+
+ - If ``detail1`` returned ``fail``, then continue, but remember the ``fail`` code, with priority 1.
+
+ - If ``detail2`` fails, then remember ``fail`` with priority 1.
+
+ - If ``detail2`` returned ``ok``, return ``ok`` from the ``group``.
+
+The return code from the ``group`` is the return code which was either
+forced to return (e.g. ``ok`` for ``detail1``), or the highest priority
+return code found by processing the list.
+
+This process can be extended to any number of modules listed in a
+``group`` section.
+
+
+Virtual Modules
+---------------
+
+Some configurations may require using the same list of modules, in
+the same order, in multiple sections. For those systems, the
+configuration can be simplified through the use of ``virtual`` modules.
+These modules are configured as named sub-sections of the
+``instantiate`` section, as follows:
+
+::
+
+ instantiate {
+ ...
+
+ redundant sql1_or_2 {
+ sql1
+ sql2
+ }
+ }
+
+The name ``sql1_or_2`` can then be used in any other section, such as
+``authorize`` or ``accounting``. The result will be *exactly* as if that
+section was placed at the location of the ``sql1_or_2`` reference.
+
+These virtual modules are full-fledged objects in and of themselves.
+One virtual module can refer to another virtual module, and they can
+contain ``if`` conditions, or any other configuration permitted in a
+section.
+
+
+Redundancy and Load-Balancing
+-----------------------------
+
+See ``man unlang`` or ``doc/load-balance`` for information on simple
+redundancy (fail-over) and load balancing.
+
+
+The Gory Details
+-----------------
+
+The fundamental object is called a MODCALLABLE, because it is something that
+can be passed a specific radius request and returns one of the RLM_MODULE_*
+results. It is a function - if you can accept the fact that pieces of
+radiusd.conf are functions. There are two kinds of MODCALLABLEs: GROUPs and
+SINGLEs.
+
+A SINGLE is a reference to a module instance that was set up in the modules{}
+section of radiusd.conf, like ``preprocess`` or ``sql1``. When a SINGLE is
+called, the corresponding function in the rlm is invoked, and whichever
+RLM_MODULE_* it returns becomes the RESULT of the SINGLE.
+
+A GROUP is a section of radiusd.conf that includes some MODCALLABLEs.
+Examples of GROUPs above include ``authorize{...}``, which implements the C
+function module_authorize, and ``redundant{...}``, which contains two SINGLEs
+that refer to a couple of redundant databases. Note that a GROUP can contain
+other GROUPs - ``Auth-Type SQL{...}`` is also a GROUP, which implements the C
+function module_authenticate when Auth-Type is set to SQL.
+
+Now here's the fun part - what happens when a GROUP is called? It simply runs
+through all of its children in order, and calls each one, whether it is
+another GROUP or a SINGLE. It then looks at the RESULT of that child, and
+takes some ACTION, which is basically either ``return that RESULT immediately``
+or ``Keep going``. In the first example, any ``bad`` RESULT from the preprocess
+module causes an immediate return, and any ``good`` RESULT causes the
+authorize{...} GROUP to proceed to the files module.
+
+We can see the exact rules by writing them out the long way:
+
+::
+
+ authorize {
+ preprocess {
+ notfound = 1
+ noop = 2
+ ok = 3
+ updated = 4
+ fail = return
+ reject = return
+ userlock = return
+ invalid = return
+ handled = return
+ }
+ files {
+ notfound = 1
+ noop = 2
+ ok = 3
+ updated = 4
+ fail = return
+ reject = return
+ userlock = return
+ invalid = return
+ handled = return
+ }
+ }
+
+This is the same as the first example, with the behavior explicitly
+spelled out. Each SINGLE becomes its own section, containing a list of
+RESULTs that it may return and what ACTION should follow from them. So
+preprocess is called, and if it returns for example RLM_MODULE_REJECT,
+then the reject=return rule is applied, and the authorize{...} GROUP
+itself immediately returns RLM_MODULE_REJECT.
+
+If preprocess returns RLM_MODULE_NOOP, the corresponding ACTION is ``2``. An
+integer ACTION serves two purposes - first, it tells the parent GROUP to go
+on to the next module. Second, it is a hint as to how desirable this RESULT
+is as a candidate for the GROUP's own RESULT. So files is called... suppose
+it returns RLM_MODULE_NOTFOUND. The ACTION for notfound inside the files{...}
+block is ``1``. We have now reached the end of the authorize{...} GROUP and we
+look at the RESULTs we accumulated along the way - there is a noop with
+preference level 2, and a notfound with preference level 1, so the
+authorize{...} GROUP as a whole returns RLM_MODULE_NOOP, which makes sense
+because to say the user was not found at all would be a lie, since preprocess
+apparently found him, or else it would have returned RLM_MODULE_NOTFOUND too.
+
+We could use the ``default`` code to simplify the above example a
+little. The following two configurations are identical:
+
+::
+
+ files {
+ notfound = 1
+ noop = 2
+ ok = 3
+ updated = 4
+ default = return
+ }
+
+
+When putting the ``default`` first, later definitions over-ride it's
+return code:
+
+::
+
+ files {
+ default = return
+ notfound = 1
+ noop = 2
+ ok = 3
+ updated = 4
+ }
+
+[Take a deep breath - the worst is over]
+
+That RESULT preference/desirability stuff is pretty complex, but my hope is
+that it will be complex enough to handle the needs of everyone's real-world
+imperfect systems, while staying out of sight most of the time since the
+defaults will be right for the most common configurations.
+
+So where does redundant{...} fit in with all that? Well, redundant{...} is
+simply a group that changes the default ACTIONs to something like
+
+::
+
+ fail = 1
+ everythingelse = return
+
+so that when one module fails, we keep trying until we find one that doesn't
+fail, then return whatever it returned. And at the end, if they all failed,
+the redundant GROUP as a whole returns RLM_MODULE_FAIL, just as you'd want it
+to (I hope).
+
+There are two other kinds of grouping: ``group{...}`` which does not have any
+specialized default ACTIONs, and ``append{...}``, which should be used when you
+have separate but similarly structured databases that are guaranteed not to
+overlap.
+
+That's all that really needs to be said. But now a few random notes:
+
+GROUPs may have RESULT=ACTION
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+It would look like this:
+
+::
+
+ authorize {
+ preprocess
+ redundant {
+ sql1
+ sql2
+ notfound = return
+ }
+ files
+ }
+
+which would prevent ``files`` from being called if neither of the SQL
+instances could find the user.
+
+redundant{...} and append{...} are just shortcuts
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+You could write:
+
+::
+
+ group {
+ sql1 {
+ fail = 1
+ notfound = 2
+ noop = return
+ ok = return
+ updated = return
+ reject = return
+ userlock = return
+ invalid = return
+ handled = return
+ }
+ sql2 {
+ fail = 1
+ notfound = 2
+ noop = return
+ ok = return
+ updated = return
+ reject = return
+ userlock = return
+ invalid = return
+ handled = return
+ }
+ }
+ instead of
+ redundant {
+ sql1
+ sql2
+ }
+
+but the latter is just a whole lot easier to read.
+
+``authenticate{...}`` is not a GROUP
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+even though it contains a list of ``Auth-Type`` GROUPs, because its
+semantics are totally different - it uses ``Auth-Type`` to decide which of
+its members to call, and their order is irrelevant.
+
+The default rules are context-sensitive
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For ``authorize``, the defaults are
+what you saw above - notfound, noop, ok, and updated are considered
+success, and anything else has an ACTION of ``return``. For authenticate, the
+default is to return on success *or* reject, and only try the second and
+following items if the first one fails. You can read all the default ACTIONs
+in modcall.c (int defaultactions[][][]), or just trust me. They do the right
+thing.
+
+There are some rules that can't be implemented in this language
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+things like ``notfound = 1-reject``, ``noop = 2-ok``, ``ok = 3-ok``, etc. But I don't feel
+justified adding that complexity in the first draft.
+There are already enough things here that may never see real-world usage.
+Like append{...}
+
+-- Pac. 9/18/2000
diff --git a/doc/configuration/dynamic_home_servers.md b/doc/configuration/dynamic_home_servers.md
new file mode 100644
index 0000000..7518f0a
--- /dev/null
+++ b/doc/configuration/dynamic_home_servers.md
@@ -0,0 +1,238 @@
+# Dynamic Home Servers in v3
+
+FreeRADIUS has some support for dynamic home servers, with certain
+limitations.
+
+
+## Configuration
+
+The configuration needs to have dynamic home servers enabled, by
+editing `proxy.conf`.
+
+```
+proxy server {
+ ...
+ dynamic = true
+ ...
+}
+```
+
+This configuration item causes the internal data structures to become
+thread-safe for updates. This change means that there will be more
+lock contention on the data structures holding home servers. As a
+result, high-load proxy may see slowdowns.
+
+Once dynamic home servers are enabled, they should be placed into
+a subdirectory. FreeRADIUS should be told which subdirectory the
+home servers are located in:
+
+```
+proxy server {
+ ...
+ dynamic = true
+ ...
+ directory = ${raddb}/home_servers/
+}
+```
+
+This directory should contain nothing other than definitions for
+dynamic home servers. These definitions are simply normal
+`home_server` definitions:
+
+```
+home_server example.com {
+ ...
+}
+```
+
+Each file in the directory should be named for the home server domain
+name. In the above example, the filename should be
+`${raddb}/home_servers/example.com`. The name of the home server in
+the file should be the same as the filename which contains the home
+server definition.
+
+Each file in the directory should have one, and only one,
+`home_server` definition.
+
+Home servers which use RadSec can `$INCLUDE tls.conf` in this
+directory to use a common site-local TLS configuration. The script
+`freeradius-naptr-to-home-server.sh` referenced below assumes that
+this file exists. If you are not using that file, it is safe to just
+replace it with an empty file.
+
+### The Control Socket
+
+The virtual server `sites-enabled/control` *must* be enabled for
+dynamic home servers to work. The `radmin` program *must* have
+read/write permission in order for dynamic home servers to work.
+
+Please see that `sites-enabled/control` file for information on
+configuring that virtual server.
+
+
+## Starting FreeRADIUS
+
+When FreeRADIUS starts, it will read each file in the
+`${raddb}/home_servers/` directory. The file will parsed in order to
+define a dynamic `home_server`.
+
+
+## Adding a new Home Server
+
+In order to add a new home server while FreeRADIUS is running, simply
+add a new `home_server` definition file to the
+`${raddb}/home_servers/` directory.
+
+Then, tell FreeRADIUS that the home server is available in a new file:
+
+```
+$ radmin -e "add home_server file /path/to/raddb/home_servers/example/com"
+```
+
+If all goes well, the home server will be added. If there are issues,
+`radmin` will print a descriptive error.
+
+Once a dynamic home server has been added, it can be used just like
+any other home server.
+
+
+## Deleting a Home Server
+
+Dynamically created home servers can be deleted via `radmin`. Note
+also that dynamic home servers which are loaded when FreeRADIUS starts
+can be deleted.
+
+```
+$ radmin -e "del home_server file <name> <type>"
+```
+
+Note that this command deletes the home server by name and type, not
+by filename. This difference from the `add home_server` command is
+due to internal limitations in the server core.
+
+```
+home_server <name> {
+ type = <type>
+}
+```
+
+
+## Listing a Home Server
+
+It is possible to list all home servers and know which is dynamic or no.
+
+```
+$ radmin -e "show home_server list all"
+```
+
+
+## Limitations
+
+Note that due to internal limitations, dynamic home servers are _not_
+freed. So repeatedly adding and deleted home servers _will_ cause
+FreeRADIUS to gradually use more memory.
+
+Other internal limitations means that it is impossible to add dynamic
+home servers to a `home_server_pool`. In short, dynamic home servers
+exist by themselves, with no associated realm, pool, or failover
+capability.
+
+
+## Proxying to a Home Server
+
+The new attribute `Home-Server-Name` controls proxying to a particular
+home server. The home server just has to exist, it does not need to
+be a dynamic home server.
+
+```
+authorize {
+ ...
+
+ update control {
+ Home-Server-Name := "example.com"
+ }
+ ...
+}
+```
+
+
+## Checking if a Dynamic Home Server exists
+
+You can see if a dynamic home server exists through the following
+dynamic string expansion:
+
+```
+%{home_server_dynamic:name}
+```
+
+This expansion looks up the home server by name, and returns whether
+or not the home server exists, and is dynamic.
+
+The return values are:
+
+* empty string - the home server does not exist.
+* `0` - the home server exists, and is statically defined.
+* `1` - the home server exists, and is dynamically defined
+
+```
+authorize {
+ ...
+ if (User-Name =~ /@(.*)$/) {
+ switch "%{home_server_dynamic:%{1}}" {
+ case "1" {
+ # Proxy to this one particular home server
+ update control {
+ &Home-Server-Name := "%{1}"
+ }
+ }
+
+ case "0" {
+ # Proxy with home server pool, failover, etc.
+ update control {
+ &Proxy-To-Realm := "%{1}"
+ }
+ }
+
+ case {
+ # no home server exists, ask DNS
+ update control {
+ # you can add a parameter for the NAPTR tag to look up, e.g. "aaa+auth:radius.tls.tcp" (RFC7585, OpenRoaming)
+ # if the third parameter is omitted, it defaults to "x-eduroam:radius.tls"
+ &Temp-Home-Server-String := `%{config:confdir}/mods-config/realm/freeradius-naptr-to-home-server.sh -d %{config:confdir} %{1}`
+ }
+ if ("%{control:Temp-Home-Server-String}" == "" ) {
+ reject
+ } else {
+ update control {
+ &Home-Server-Name := "%{1}"
+ }
+ }
+ }
+ }
+ }
+ ...
+}
+```
+
+## Maintenance of Dynamic Home Servers
+
+Dynamic home servers are discovered from DNS, and DNS has TTLs. These
+TTLs are not tracked by FreeRADIUS, as they are not available when
+using the standard DNS APIs.
+
+Dynamic realms should be regularly deleted, so that they can be
+recreated with updated information. The server should be restarted
+with an empty home_server directory regularly, for two reasons:
+
+* Entries in DNS may change over time, or be removed, and the server should learn this.
+ If the entries are not removed, the server will not discover any changes.
+* dynamic home servers are often RADIUS/TLS based with client and server certificates,
+ and the server should refresh CRL information regularly
+
+As a result, we recommend emptying the home_servers directory (except
+for the `tls.conf` file), refreshing CRLs and then restarting the server
+once per day. e.g.
+
+```
+rm -f $(ls -1 raddb/home_servers | egrep -v tls.conf)
+```
diff --git a/doc/configuration/load_balance.rst b/doc/configuration/load_balance.rst
new file mode 100644
index 0000000..7926444
--- /dev/null
+++ b/doc/configuration/load_balance.rst
@@ -0,0 +1,172 @@
+Load Balancing
+==============
+
+As of version 2.0.0, the load balance documentation is in the
+available in the "unlang" man page. The text below may not be up to
+date, and is here only for historical purposes.
+
+As of version 1.1.0, FreeRADIUS supports load balancing in module
+sections. Please see the "configurable_failover" file in this
+directory for a more complete description of module sections.
+
+The short summary is that you can use a "load-balance" section in
+any place where a module name may be used. The semantics of the
+"load-balance" section are that one of the modules in the section will
+be chosen at random, evenly spread over the modules in the list.
+
+An example is below::
+
+ accounting {
+ load-balance {
+ sql1
+ sql2
+ sql2
+ }
+ }
+
+In this case, 1/3 of the RADIUS requests will be processed by
+"sql1", one third by "sql2", and 1/3 by "sql3".
+
+The "load-balance" section can be nested in a "redundant" section,
+or vice-versa::
+
+ accounting {
+ load-balance { # between two redundant sections below
+ redundant {
+ sql1
+ sql2
+ }
+ redundant {
+ sql2
+ sql1
+ }
+ }
+ }
+
+This says "load balance between sql1 and sql2, but if sql1 is down,
+use sql2, and if sql2 is down, use sql1". That way, you can guarantee
+both that load balancing occurs, and that the requests are *always*
+logged to one of the databases::
+
+ accounting {
+ redundant {
+ load-balance {
+ sql1
+ sql2
+ }
+ detail
+ }
+ }
+
+This says "load balance between sql1 and sql2, but if the one being
+used is down, then log to detail".
+
+And finally::
+
+ accounting {
+ redundant { # between load-balance & detail
+ load-balance { # between two redundant sections
+ redundant {
+ sql1
+ sql2
+ }
+ redundant {
+ sql2
+ sql1
+ }
+ }
+ detail
+ }
+ }
+
+This says "try to load balance between sql1 and sql2; if sql1 is down,
+use sql2; if sql2 is down use sql1; if both sql1 and sql2 are down,
+then log to the detail file"
+
+
+More complicated scenarios
+--------------------------
+
+If you want to do redundancy and load-balancing among three
+modules, the configuration is quite complex::
+
+ load-balance {
+ redundant {
+ sql1
+ load-balance {
+ redundant {
+ sql2
+ sql3
+ }
+ redundant {
+ sql3
+ sql2
+ }
+ }
+ } # sql1, etc.
+ redundant {
+ sql2
+ load-balance {
+ redundant {
+ sql3
+ sql1
+ }
+ redundant {
+ sql1
+ sql3
+ }
+ }
+ } # sql2, etc.
+ redundant {
+ sql3
+ load-balance {
+ redundant {
+ sql1
+ sql2
+ }
+ redundant {
+ sql2
+ sql1
+ }
+ }
+ } # sql3, etc.
+ }
+
+For four or more modules, it quickly becomes unmanageable.
+
+The solution is to use the "redundant-load-balance" section, which
+combines the features of "load-balance", with "redundant" fail-over
+between members. The above complex configuration for three modules
+then becomes::
+
+ redundant-load-balance {
+ sql1
+ sql2
+ sql3
+ }
+
+
+Which means "load-balance evenly among all three servers. If the
+one picked for load-balancing is down, load-balance among the
+remaining two. If that one is down, pick the one remaining 'live'
+server".
+
+The "redundant-load-balance" section can contain any number of
+modules.
+
+
+Interaction with "if" and "else"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It's best to have "if" and "else" blocks contain "load-balance" or
+"redundant-load-balance" sections, rather than the other way around.
+The "else" and "elsif" sections cannot appear inside of a
+"load-balance" or "redundant-load-balance" section, because the "else"
+condition would be chose as one of the modules for load-balancing,
+which is not what you want.
+
+It's OK to have a plain "if" block inside of a "load-balance" or
+"redundant-load-balance" section. In that case, the "if" condition
+checks the return code of the module or group that executed just
+before the "load-balance" section. It does *not* check the return
+code of the previous module in the section.
diff --git a/doc/configuration/post_auth_type b/doc/configuration/post_auth_type
new file mode 100644
index 0000000..7492324
--- /dev/null
+++ b/doc/configuration/post_auth_type
@@ -0,0 +1,44 @@
+This is now called Post-Auth-Type, for consistency.
+
+O.INTRODUCTION
+
+ Post-Auth-Type is used to select between groupings of
+ modules in the post-auth stanza using arbitrary attributes.
+ It is functionally identical to Acct-Type, apart from
+ the name of the attribute and its dealing with rejected
+ requests.. This means that (unlike Autz-Type) the attribute
+ must be set before the stanza is run. Changes to
+ Post-Auth-Type during post-auth will have no effect.
+
+1.HOW IT WORKS
+
+ If a request has been rejected, the value of Post-Auth-Type
+ is overwritten with REJECT automatically, so anonymous
+ modules outside the REJECT substanza will not be run, only
+ modules within the appropriate substanza will be run.
+
+2.EXAMPLES
+
+ In the example below, when a request has been rejected, the
+ module my_ippool will not be run, only the module my_detail
+ will be run.
+ If the request is not rejected, the my_ippool module will be
+ run, but not the my_detail module
+
+ post-auth {
+ my_ippool
+ Post-Auth-Type REJECT {
+ my_detail
+ }
+ }
+
+ In the following example, 2 different sql modules are used
+ to store accepted requests and rejected requests.
+
+ post-auth {
+ my_sql_accept
+ Post-Auth-Type REJECT {
+ my_sql_reject
+ }
+ }
+
diff --git a/doc/configuration/session_type b/doc/configuration/session_type
new file mode 100644
index 0000000..9efcd7c
--- /dev/null
+++ b/doc/configuration/session_type
@@ -0,0 +1,10 @@
+Session-Type is used to select between groupings of
+modules in the session stanza using arbitrary attributes.
+It is functionally identical to Acct-Type, apart from
+the name of the attribute. This means that (unlike
+Autz-Type) the attribute must be set before the stanza
+is run. Changes to Session-Type during session will
+have no effect.
+
+This allows Simultaneous-Use checking behaviour to be very flexible.
+
diff --git a/doc/configuration/simultaneous_use b/doc/configuration/simultaneous_use
new file mode 100644
index 0000000..5639738
--- /dev/null
+++ b/doc/configuration/simultaneous_use
@@ -0,0 +1,173 @@
+
+ FreeRADIUS server and the Simultaneous-Use parameter.
+
+
+0. INTRODUCTION
+
+ Lots of people want to limit the number of times one user account can
+ login, usually to one. This is hard to do with the radius protocol;
+ the nature of the accounting stuff is such that the idea the radius server
+ has about the list of logged-in users might be different from the idea
+ the terminal server has about it.
+
+ However, most terminal servers have an alternative way to get a list
+ of logged-in users. Most support some way through telnet, some have
+ a finger-daemon builtin and a lot of them support SNMP. So if the
+ radius server thinks that someone is trying to login a second time,
+ it is possible to check on the terminal server itself if the first
+ login is indeed still active. Only then access is denied for the
+ second login.
+
+
+1. PREREQUISITES
+
+ You need to have perl installed.
+
+ For SNMP checks, you have 2 options. You can use the `snmpget' program
+ from the cmu-snmp tools. You can probably get precompiled ones,
+ maybe even packaged for your system (Debian/Linux, Redhat/Linux, FreeBSD
+ ports collection etc). The source code is at
+ http://www.net.cmu.edu/projects/snmp/snmpapps/. The Linux-specific
+ version of this is at http://www.gaertner.de/snmp/
+
+ The other option is to install the SNMP_Session and BER modules that
+ for example the well known `mrtg' package uses. This is recommended.
+ In that case you need no external snmpget program, checkrad will
+ speak SNMP directly. See http://www.switch.ch/misc/leinen/snmp/perl/
+
+ The checkroutine for USR/3Com Total Control racks uses the Net::Telnet
+ module from CPAN, at least version 3.00. If you need that, obtain it from
+ your local CPAN mirror (or see http://www.perl.com/CPAN/). The checkrad.pl
+ perl script will autodetect if that module is installed.
+
+2. USAGE.
+
+ It works by adding the `check' parameter "Simultaneous-Use" to the entry
+ for a users or DEFAULT in /etc/raddb/users. It should be at least one;
+ it defines the maximum number of users logged in with the same account name.
+ For example:
+
+ #
+ # Simultaneous use restrictions.
+ #
+ DEFAULT Group == "staff", Simultaneous-Use := 4
+ Fall-Through = 1
+ DEFAULT Group == "business", Simultaneous-Use := 2
+ Fall-Through = 1
+ DEFAULT Simultaneous-Use := 1
+ Fall-Through = 1
+
+
+ NOTE!!! The "Simultaneous-Use" parameter is in the "check" A/V pairs,
+ and not in the Reply A/V pairs (it _is_ a check).
+
+ For SQL, after creating and populating your schema, you should
+ execute the following statement (for MySQL, others may vary):
+
+ INSERT INTO radgroupcheck (GroupName, Attribute, op, Value) values("dialup", "Simultaneous-Use", ":=", "1");
+
+ Once that is done, your users should be limited to only one login at a time.
+
+3. IMPLEMENTATION
+
+ The server keeps a list of logged-in users in the /var/log/radutmp file.
+ This is also called "the session database". When you execute "radwho",
+ all that radwho really does is list the entries in this file in a pretty
+ format. Only when someone tries to login who _already_ has an active
+ session according to the radutmp file, the server executes the perl
+ script /usr/local/sbin/checkrad (or /usr/sbin/checkrad, it checks for
+ the presence of both and in that order). This script queries the terminal
+ server to see if the user indeed already has an active session.
+
+ The script uses SNMP for Livingston Portmasters and Ciscos, finger for
+ Portslave, Computone and Ascend, and Net::Telnet for USR/3Com TC.
+
+ Since the script has been witten in perl, it's easy to adjust for
+ any type of terminal server. There are implementations in the script for
+ checks using SNMP, finger, and telnet, so it should be easy to add
+ your own check routine if your terminal server is not supported yet.
+
+ You can find the script in the file src/checkrad.pl.
+
+ You need to set the correct type in the file /etc/raddb/naslist so that
+ checkrad KNOWS how it should interrogate the terminal server. At this
+ time you can define the following types:
+
+ type Vendor Uses method needs Need naspasswd
+ ==== ====== =========== ===== ==============
+ ascend Lucent SNMP SNMP No
+ bay Nortel finger finger command No
+ cisco Cisco SNMP SNMP Optional [1]
+ computone Computone finger finger command No
+ cvx Nortel SNMP SNMP No
+ digitro Digitro rusers rusers command No
+ livingston Livingston SNMP SNMP No [2]
+ max40xx Lucent finger finger command No
+ netserver USR/3com telnet CPAN Net::Telnet Yes
+ pathras Cyclades telnet CPAN Net::Telnet Yes
+ patton Patton SNMP SNMP No
+ portslave ? finger finger command No
+ pr3000 Cyclades SNMP snmpwalk command No
+ pr4000 Cyclades SNMP snmpwalk command No
+ tc USR/3com telnet CPAN Net::Telnet Yes
+ usrhyper USR/3com SNMP SNMP No [3]
+ versanet VersaNet SNMP SNMP No
+
+ other none N/A - No
+
+ [1] In naspasswd file: set username to SNMP, password is community.
+ [2] Needs at least ComOS 3.5, SNMP enabled.
+ [3] Set "Reported Port Density" to 256 (default)
+
+ "other" means "don't bother checking, I believe what radutmp says".
+ This really is not recommended, if a user has a "stuck" entry in the
+ session database she will not be able to login again - hence the
+ extra check that "checkrad" does.
+
+4. IF IT DOESN'T WORK
+
+ Note that you need to add the Simultaneous-Use parameter to the
+ check item (first line), not the reply item, using the ':=' operator.
+
+ You can edit the `checkrad' perl script and turn on debugging. Then
+ watch the debug file. The `radius.log' file also gives some hints.
+
+ You can also run the "checkrad" script manually, use the "-d"
+ switch to get debug output on standard output instead of in the log.
+
+ See also:
+
+ http://wrath.geoweb.ge/simult.html
+
+ which has a good discussion of the use of Simultaneous-Use.
+
+
+5. CAVEATS
+
+ This solution checks the radutmp file. This file is kept up-to-date from
+ the Accounting records the NAS sends. Since some NASes delay these records
+ for quite some time, it is possible to get a double login by logging in
+ twice at _exactly_ the same time (plus or minus the mentioned delay time),
+ since neither of the logins are registered yet.
+
+ The solution would be to create a small 1-minute cache of Authentication
+ records, that is also checked for double login attempts. Perhaps in the
+ next version.
+
+ When implementing this one thing was considered the most important: when
+ trying to detect double logins, we always try to err on the safe side. So
+ in rare cases, a double login is possible but we try never to limit access
+ for a legitimate login.
+
+6. PROBLEMS WITH DROPPED CONNECTIONS
+
+ Our PM3, with 2 ISDN-30 lines coming into it, had the habit of sometimes
+ dropping connections. In a few cases, the portmaster thought the session was
+ still alive so if the user tried to login again, he or she was denied access.
+ In our case, this problem was caused by a bad PRI line from the phone
+ company.
+
+ We tried to compensate this by setting the Idle-Timeout to 15 minutes. That
+ way, even if a user did get locked out the portmaster would clear the rogue
+ session within 15 minutes and the user could login again.
+
diff --git a/doc/configuration/snmp b/doc/configuration/snmp
new file mode 100644
index 0000000..713cb83
--- /dev/null
+++ b/doc/configuration/snmp
@@ -0,0 +1,58 @@
+INSTALL
+-------
+
+Installing the SNMP patch is straightforward:
+
+$ tar -zxf freeradius-server-2.1.11.tar.gz
+$ cd freeradius-server-2.1.11
+$ patch -p1 < ../snmp.patch
+$ ./configure --args....
+$ make
+$ make install
+
+MIB Installation
+----------------
+
+The traps *REQUIRE* that the files in the "mibs" directory be copied
+to the global mibs directory, usually /usr/share/snmp/mibs/.
+If this is not done, the "snmptrap" program has no idea what information
+to send, and will not work. The MIB installation is *NOT* done as
+part of the default installation, so that step *MUST* be done manually.
+
+The global MIB directory can be found by running the following command:
+
+ $ snmptranslate -Dinit_mib .1.3 2>&1 | grep MIBDIR | sed "s/' .*//;s/.* '//;s/.*://"
+
+Or maybe just:
+
+ $ snmptranslate -Dinit_mib .1.3 2>&1 | grep MIBDIR
+
+If you have copied the MIBs to that directory, you can test the
+FreeRADIUS MIBs by running the following command:
+
+ $ snmptranslate -m +FREERADIUS-NOTIFICATION-MIB -IR -On serverStart
+
+It should print out:
+
+ .1.3.6.1.4.1.11344.4.1.1
+
+As always, run the server in debugging mode after enabling the
+traps. You will see the "snmptrap" command being run, and it will
+print out any errors or issues that it encounters. Those need to
+be fixed before running the server in daemon mode.
+
+We also suggest running in debugging mode as the "radiusd" user, if
+you have "user/group" set in radiusd.conf. The "snmptrap" program
+may behave differently when run as "root" or as the "radiusd" user.
+
+You will also need to edit "radiusd.conf", and uncomment the line saying
+
+ # $INCLUDE trigger.conf
+
+That will enable the triggers.
+
+
+More Documentation
+------------------
+
+See raddb/trigger.conf for complete documentation.
diff --git a/doc/configuration/variables.rst b/doc/configuration/variables.rst
new file mode 100644
index 0000000..4a2f28c
--- /dev/null
+++ b/doc/configuration/variables.rst
@@ -0,0 +1,151 @@
+Run-time variables
+==================
+
+See "man unlang" for more complete documentation on the run-time
+variables. This file is here only for historical purposes.
+
+The above variable expansions also support the following
+meta-attributes. These are not normal RADIUS attributes, but are
+created by the server to be used like them, for ease of use. They can
+only be queried, and cannot be assigned.
+
++-----------------------+-------------------------------------------------+
+| Packet-Type | RADIUS packet type (Access-Request, etc.) |
++-----------------------+-------------------------------------------------+
+| Packet-Src-IP-Address | IP address from which the packet was sent |
++-----------------------+-------------------------------------------------+
+| Packet-Dst-IP-Address | IP address to which the packet was sent. |
+| | This may be "0.0.0.0", if the server |
+| | was configured with ``bind_address = *``. |
++-----------------------+-------------------------------------------------+
+| Packet-Src-Port | UDP port from which the packet was sent |
++-----------------------+-------------------------------------------------+
+| Packet-Dst-Port | UDP port to which the packet was sent. |
++-----------------------+-------------------------------------------------+
+
+``%{config:section.subsection.item}``
+ Corresponding value in ``radiusd.conf`` for the string value of that item.
+
+The ``%{config:...}`` variables should be used VERY carefully, as they
+may leak secret information from your RADIUS server, if you use them
+in reply attributes to the NAS!
+
+::
+
+ DEFAULT User-Name =~ "^([^@]+)@(.*)"
+ All-That-Matched = `%{0}`
+ Just-The-User-Name = `%{1}`
+ Just-The-Realm-Name = `%{2}`
+
+
+The variables are used in dynamically translated strings. Most of the
+configuration entries in ``radiusd.conf`` (and related files) will do
+dynamic string translation. To do the same dynamic translation in a
+RADIUS attribute (when pulling it from a database, or "users" file),
+you must put the string into an back-quoted string:
+
+::
+
+ Session-Timeout = `%{expr: 2 + 3}`
+
+To do the dynamic translation in the ``radiusd.conf`` (or some other
+configuration files), just use the variable as-is. See
+``radiusd.conf`` for examples.
+
+
+Attributes as environment variables in executed programs
+--------------------------------------------------------
+
+When calling an external program (e.g. from ``rlm_exec`` module), these
+variables can be passed on the command line to the program. In
+addition, the server places all of the attributes in the RADIUS
+request into environment variables for the external program. The
+variables are renamed under the following rules:
+
+ #. All letters are made upper-case.
+ #. All hyphens '-' are turned into underscores '_'
+
+so the attribute ``User-Name`` can be passed on the command line to the
+program as ``%{User-Name}``, or used inside the program as the environment
+variable ``USER_NAME`` (or ``$USER_NAME`` for shell scripts).
+
+If you want to see the list of all of the variables, try adding a line
+``printenv > /tmp/exec-program-wait`` to the script. Then look in the
+file for a complete list of variables.
+
+One-character variables
+-----------------------
+
+The following one-character variables were defined. They were duplicates of the
+previous general cases, and were only provided for backwards compatibility.
+They are in the process of being removed, this table documents the old variables
+and their new equivalents.
+(i.e. ``:-``, as described above.
+
++-----------+---------------------------+-----------------------+
+| Variable | Description | Proper Equivalent |
++===========+===========================+=======================+
+|%a |Protocol (SLIP/PPP) |%{Framed-Protocol} |
++-----------+---------------------------+-----------------------+
+|%c |Callback-Number |%{Callback-Number} |
++-----------+---------------------------+-----------------------+
+|%d |request day (DD) | |
++-----------+---------------------------+-----------------------+
+|%f |Framed IP address |%{Framed-IP-Address} |
++-----------+---------------------------+-----------------------+
+|%i |Calling Station ID |%{Calling-Station-Id} |
++-----------+---------------------------+-----------------------+
+|%l |request timestamp | |
++-----------+---------------------------+-----------------------+
+|%m |request month (MM) | |
++-----------+---------------------------+-----------------------+
+|%n |NAS IP address |%{NAS-IP-Address} |
++-----------+---------------------------+-----------------------+
+|%p |Port number |%{NAS-Port} |
++-----------+---------------------------+-----------------------+
+|%s |Speed (PW_CONNECT_INFO) |%{Connect-Info} |
++-----------+---------------------------+-----------------------+
+|%t |request in ctime format | |
++-----------+---------------------------+-----------------------+
+|%u |User name |%{User-Name} |
++-----------+---------------------------+-----------------------+
+|%A |radacct_dir |%{config:radacctdir} |
++-----------+---------------------------+-----------------------+
+|%C |clientname | |
++-----------+---------------------------+-----------------------+
+|%D |request date (YYYYMMDD) | |
++-----------+---------------------------+-----------------------+
+|%G |request minute | |
++-----------+---------------------------+-----------------------+
+|%H |request hour | |
++-----------+---------------------------+-----------------------+
+|%I |request ID | |
++-----------+---------------------------+-----------------------+
+|%L |radlog_dir |%{config:logdir} |
++-----------+---------------------------+-----------------------+
+|%M |MTU |%{Framed-MTU} |
++-----------+---------------------------+-----------------------+
+|%R |radius_dir |%{config:raddbdir} |
++-----------+---------------------------+-----------------------+
+|%S |request timestamp | |
+| |in SQL format | |
++-----------+---------------------------+-----------------------+
+|%T |request timestamp | |
+| |in database format | |
++-----------+---------------------------+-----------------------+
+|%U |Stripped User name |%{Stripped-User-Name} |
++-----------+---------------------------+-----------------------+
+|%V |Request-Authenticator | |
+| |(Verified/None) | |
++-----------+---------------------------+-----------------------+
+|%v |Server Version | |
++-----------+---------------------------+-----------------------+
+|%Y |request year (YYYY) | |
++-----------+---------------------------+-----------------------+
+|%Z |All request attributes | |
+| |except password | |
+| |(must have a big buffer) | |
++-----------+---------------------------+-----------------------+
+
+
+ $Id$
diff --git a/doc/deployment/CYGWIN.rst b/doc/deployment/CYGWIN.rst
new file mode 100644
index 0000000..da61d49
--- /dev/null
+++ b/doc/deployment/CYGWIN.rst
@@ -0,0 +1,283 @@
+FreeRADIUS for EAP under CygWin
+===============================
+
+From: "Philip Blow" <philipb@simplywireless.com.au>
+To: <freeradius-users@lists.cistron.nl>
+Date: Wed, 29 Jan 2003 15:23:45 +1100
+
+Here are some brief notes I but together for compiling FreeRADIUS 0.8.1
+on Windows XP with EAP/TLS support.
+
+Configuring FreeRADIUS for EAP under CygWin.
+--------------------------------------------
+
+#. Installing CygWin
+
+ Install the latest version of CygWin (at time of writing 1.3.19-1) from http://www.cygwin.com
+
+#. Install the following packages (make as minimum list)
+
+ +--------------------+-----------------+
+ | Package | Version |
+ +====================+=================+
+ | _update-info-dir | 00126-1 |
+ +--------------------+-----------------+
+ | ash | 20020731-1 |
+ +--------------------+-----------------+
+ | autoconf | 2.54-1 |
+ +--------------------+-----------------+
+ | autoconf-devel | 2.57-1 |
+ +--------------------+-----------------+
+ | autoconf-stable | 2.13-4 |
+ +--------------------+-----------------+
+ | automake | 1.7.1-1 |
+ +--------------------+-----------------+
+ | automake-devel | 1.7.2-1 |
+ +--------------------+-----------------+
+ | automake-stable | 1.4p5-5 |
+ +--------------------+-----------------+
+ | base-files | 1.1-1 |
+ +--------------------+-----------------+
+ | base-passwd | 1.0-1 |
+ +--------------------+-----------------+
+ | bash | 2.05b-8 |
+ +--------------------+-----------------+
+ | bc | 1.06-1 |
+ +--------------------+-----------------+
+ | binutils | 20021117-1 |
+ +--------------------+-----------------+
+ | byacc | 1.9-1 |
+ +--------------------+-----------------+
+ | bzip2 | 1.0.2-2 |
+ +--------------------+-----------------+
+ | crypt | 1.0-1 |
+ +--------------------+-----------------+
+ | cygrunsrv | 0.95-1 |
+ +--------------------+-----------------+
+ | cygutils | 1.1.3-1 |
+ +--------------------+-----------------+
+ | cygwin | 1.3.19-1 |
+ +--------------------+-----------------+
+ | cygwin-doc | 1.3-2 |
+ +--------------------+-----------------+
+ | diff | 1.0-1 |
+ +--------------------+-----------------+
+ | diffutils | 2.8.1-1 |
+ +--------------------+-----------------+
+ | ed | 0.2-1 |
+ +--------------------+-----------------+
+ | file | 3.37-1 |
+ +--------------------+-----------------+
+ | fileutils | 4.1-1 |
+ +--------------------+-----------------+
+ | findutils | 4.1.7-4 |
+ +--------------------+-----------------+
+ | gawk | 3.1.1-5 |
+ +--------------------+-----------------+
+ | gcc | 3.2-3 |
+ +--------------------+-----------------+
+ | gcc-mingw | 20020817-5 |
+ +--------------------+-----------------+
+ | gcc2 | 2.95.3-10 |
+ +--------------------+-----------------+
+ | gdb | 20021218-1 |
+ +--------------------+-----------------+
+ | gdbm | 1.8.0-4 |
+ +--------------------+-----------------+
+ | gettext | 0.11.5-1 |
+ +--------------------+-----------------+
+ | grep | 2.5-1 |
+ +--------------------+-----------------+
+ | groff | 1.18.1-2 |
+ +--------------------+-----------------+
+ | gzip | 1.3.3-4 |
+ +--------------------+-----------------+
+ | inetutils | 1.3.2-20 |
+ +--------------------+-----------------+
+ | initscripts | 0.9-1 |
+ +--------------------+-----------------+
+ | less | 378-1 |
+ +--------------------+-----------------+
+ | libbz2_0 | 1.0.2-1 |
+ +--------------------+-----------------+
+ | libbz2_1 | 1.0.2-2 |
+ +--------------------+-----------------+
+ | libiconv2 | 1.8-2 |
+ +--------------------+-----------------+
+ | libintl | 0.10.38-3 |
+ +--------------------+-----------------+
+ | libintl1 | 0.10.40-1 |
+ +--------------------+-----------------+
+ | libintl2 | 0.11.5-1 |
+ +--------------------+-----------------+
+ | libltdl3 | 20030103-1 |
+ +--------------------+-----------------+
+ | libncurses5 | 5.2-1 |
+ +--------------------+-----------------+
+ | libncurses6 | 5.2-8 |
+ +--------------------+-----------------+
+ | libpng10 | 1.0.14-2 |
+ +--------------------+-----------------+
+ | libpng12 | 1.2.4-2 |
+ +--------------------+-----------------+
+ | libpopt0 | 1.6.4-4 |
+ +--------------------+-----------------+
+ | libreadline4 | 4.1-2 |
+ +--------------------+-----------------+
+ | libreadline5 | 4.3-2 |
+ +--------------------+-----------------+
+ | libtool | 20020202a-1 |
+ +--------------------+-----------------+
+ | libtool-devel | 20021227-1 |
+ +--------------------+-----------------+
+ | libtool-stable | 1.4.2-2 |
+ +--------------------+-----------------+
+ | libxml2 | 2.4.23-1 |
+ +--------------------+-----------------+
+ | login | 1.7-1 |
+ +--------------------+-----------------+
+ | m4 | 1.4-1 |
+ +--------------------+-----------------+
+ | make | 3.79.1-7 |
+ +--------------------+-----------------+
+ | man | 1.5j-1 |
+ +--------------------+-----------------+
+ | mingw-runtime | 2.3-1 |
+ +--------------------+-----------------+
+ | mktemp | 1.4-1 |
+ +--------------------+-----------------+
+ | more | 2.11o-1 |
+ +--------------------+-----------------+
+ | nasm | 0.98.35-1 |
+ +--------------------+-----------------+
+ | ncurses | 5.2-8 |
+ +--------------------+-----------------+
+ | newlib-man | 20020801 |
+ +--------------------+-----------------+
+ | openssh | 3.5p1-3 |
+ +--------------------+-----------------+
+ | openssl | 0.9.7-1 |
+ +--------------------+-----------------+
+ | openssl-devel | 0.9.7-1 |
+ +--------------------+-----------------+
+ | openssl096 | 0.9.6h-1 |
+ +--------------------+-----------------+
+ | patch | 2.5.8-2 |
+ +--------------------+-----------------+
+ | pcre | 3.7-1 |
+ +--------------------+-----------------+
+ | perl | 5.6.1-2 |
+ +--------------------+-----------------+
+ | readline | 4.3-2 |
+ +--------------------+-----------------+
+ | sed | 4.0.5-1 |
+ +--------------------+-----------------+
+ | sh-utils | 2.0.15-3 |
+ +--------------------+-----------------+
+ | sharutils | 4.2.1-2 |
+ +--------------------+-----------------+
+ | sysvinit | 2.84-3 |
+ +--------------------+-----------------+
+ | tar | 1.13.25-1 |
+ +--------------------+-----------------+
+ | tcltk | 20021218-1 |
+ +--------------------+-----------------+
+ | termcap | 20020930-1 |
+ +--------------------+-----------------+
+ | terminfo | 5.2-3 |
+ +--------------------+-----------------+
+ | texinfo | 4.2-4 |
+ +--------------------+-----------------+
+ | textutils | 2.0.21-1 |
+ +--------------------+-----------------+
+ | tiff | 3.5.7-1 |
+ +--------------------+-----------------+
+ | time | 1.7-1 |
+ +--------------------+-----------------+
+ | unzip | 5.50-1 |
+ +--------------------+-----------------+
+ | vim | 6.1-2 |
+ +--------------------+-----------------+
+ | w32api | 2.1-1 |
+ +--------------------+-----------------+
+ | wget | 1.8.2-2 |
+ +--------------------+-----------------+
+ | which | 1.5-1 |
+ +--------------------+-----------------+
+ | xinetd | 2.3.9-1 |
+ +--------------------+-----------------+
+ | zip | 2.3-2 |
+ +--------------------+-----------------+
+ | zlib | 1.1.4-1 |
+ +--------------------+-----------------+
+
+#. Download
+
+ Download the FreeRADIUS source code from http://www.freeradius.org/
+
+#. Expand the FreeRADIUS source file.
+
+#. Make the following changes to the source code
+ (the diffs are reversed)
+
+ ::
+
+ src/main/Makefile.in
+
+ 145,148c145,148
+ < $(LIBTOOL) --mode=install $(INSTALL) -m 755 $(INSTALLSTRIP)
+ radiusd.exe $(R)$(sbindir)
+ < $(INSTALL) -m 755 $(INSTALLSTRIP) radwho.exe
+ $(R)$(bindir)
+ < $(INSTALL) -m 755 $(INSTALLSTRIP) raduse.exe
+ $(R)$(bindir)
+ < $(INSTALL) -m 755 $(INSTALLSTRIP) radzap.exe
+ $(R)$(bindir)
+ ---
+ > $(LIBTOOL) --mode=install $(INSTALL) -m 755 $(INSTALLSTRIP)
+ radiusd $(R)$(sbindir)
+ > $(INSTALL) -m 755 $(INSTALLSTRIP) radwho
+ $(R)$(bindir)
+ > $(INSTALL) -m 755 $(INSTALLSTRIP) raduse
+ $(R)$(bindir)
+ > $(INSTALL) -m 755 $(INSTALLSTRIP) radzap
+ $(R)$(bindir)
+ 150,151c150,151
+ < $(INSTALL) -m 755 radclient.exe $(R)$(bindir)
+ < $(INSTALL) -m 755 radrelay.exe $(R)$(bindir)
+ ---
+ > $(INSTALL) -m 755 radclient $(R)$(bindir)
+ > $(INSTALL) -m 755 radrelay $(R)$(bindir)
+
+ src/modules/rlm_dbm/Makefile.in
+
+ 22,23c22,23
+ < $(INSTALL) -m 755 $(INSTALLSTRIP) rlm_dbm_parser.exe
+ $(R)$(bindir)
+ < $(INSTALL) -m 755 $(INSTALLSTRIP) rlm_dbm_cat.exe
+ $(R)$(bindir)
+ ---
+ > $(INSTALL) -m 755 $(INSTALLSTRIP) rlm_dbm_parser $(R)$(bindir)
+ > $(INSTALL) -m 755 $(INSTALLSTRIP) rlm_dbm_cat $(R)$(bindir)
+
+ src/modules/rlm_mschap/Makefile
+
+ 20c20
+ < $(INSTALL) -m 755 $(INSTALLSTRIP) smbencrypt.exe $(R)$(bindir)
+ ---
+ > $(INSTALL) -m 755 $(INSTALLSTRIP) smbencrypt $(R)$(bindir)
+
+#. Run configure with the following command line
+
+ ::
+
+ ./configure -without-snmp -disable-shared -enable-static
+
+#. Execute make and then make install
+
+::
+
+ Philip Blow
+ Senior Technical Manager
+ Simply Wireless
+ philipb@simplywireless.com.au
diff --git a/doc/deployment/MACOSX b/doc/deployment/MACOSX
new file mode 100644
index 0000000..39ebaec
--- /dev/null
+++ b/doc/deployment/MACOSX
@@ -0,0 +1,12 @@
+ Installing FreeRADIUS on MAC OSX
+ --------------------------------
+
+1) download, unzip and untar freeradius.tar.gz
+
+2) $ ./configure
+
+3) $ make
+
+4) $ make install
+
+It's what the developers use, so we make sure it works.
diff --git a/doc/deployment/OS2 b/doc/deployment/OS2
new file mode 100644
index 0000000..fc676c9
--- /dev/null
+++ b/doc/deployment/OS2
@@ -0,0 +1,22 @@
+Compiling FreeRADIUS under OS/2
+
+To compile FreeRADIUS unde OS/2 you must have a full EMX environment with GNU
+utilities (like make,sh)
+
+The EMX can be get from http://hobbes.nmsu.edu
+
+To work with CVS repository you must install cvs110.zip from hobbes also
+
+before entering in sh.exe you must do
+SET SHELL=sh.exe
+
+before running ./configure you must set the shell the variables :
+export CC=gcc
+export MAKE=fullpathofyourmake.exe
+export PERL=fullpathofyourperl.exe
+
+
+The OS/2 version of FreeRADIUS can't directly execute checkrad.pl then the
+program will execute a checkrad.cmd
+
+
diff --git a/doc/deployment/performance-testing b/doc/deployment/performance-testing
new file mode 100644
index 0000000..71945c1
--- /dev/null
+++ b/doc/deployment/performance-testing
@@ -0,0 +1,168 @@
+
+Radius Test Procedures
+
+0. INTRODUCTION
+
+This document describes how to test your radius server authentication
+using random usernames and passwords with the 'radclient' program.
+
+1. WHY TEST
+
+Many people want to see the difference in efficiency behind the various
+authentication methods, compilation methods, etc of their radius server.
+Before now, this was difficult to do efficiently across a large number
+of users. However, with this document, you'll be able to test your
+radius server and determine the best options to use for your system.
+
+2. GETTING STARTED
+
+First thing we have to do is generate a large number of users. You'll
+want to do this even if you have a large passwd file you want to use
+from your system, because the create user script sets up other files
+you need for testing. So head to the scripts/ directory, and do this:
+
+Make a tmp dir
+# mkdir tmp
+# cp create-users.pl tmp
+# cd tmp
+
+Run the script to create 10,000 (or however many you want) random users
+and passwords
+# ./create-users.pl 10000
+
+Output from the script will include several files:
+ passwd : A standard passwd file you can append to /etc/passwd
+ shadow : A standard shadow file you can append to /etc/shadow
+passwd.nocrypt : A file with *unencrypted* users & passes in form "user:pass"
+ radius.test : File you'll use as input for radclient
+ radius.users : A standard radius 'users' file
+
+So, equipped with lots of users and passwords, there's several methods of
+authentication you can test:
+
+ o System users (Auth-Type:=System)
+ o Local users (Auth-Type:=Local)
+ o Cached system (passwd) users
+ o Others
+
+NOTE: Before moving on, you will probably want to add '/dev/null' to
+/etc/shells *temporarily* so that default system authentication will
+work. REMEMBER TO TAKE IT OUT!
+
+3. TEST PROCEDURES
+
+ A. System (/etc/passwd) users testing
+
+ 1. Append the 'passwd' file from create-users.pl onto your
+ system passwd file:
+
+ # cat ./passwd >> /etc/passwd
+
+ 2. If you have shadow, append the shadow file onto /etc/shadow
+
+ # cat ./shadow >> /etc/shadow
+
+ 3. Make sure you have a DEFAULT user similar to the following
+ in your radius 'users' file:
+
+ DEFAULT Auth-Type:=System
+ Reply-Message = "Success!"
+
+ 4. Start radiusd
+
+ # /usr/local/sbin/radiusd
+
+ 5. Run radclient with 'radius.test' as the input file.
+
+ NOTE: First you need to setup a secret for your local
+ machine in the 'clients' file and use that secret below
+
+ # time /usr/local/bin/radclient -q -s -f radius.test \
+ <yourhostname> auth <secret>
+
+ NOTE: The above is to be put all on one line.
+
+ NOTE: Some systems do not have the 'time' command,
+ so you may need to break out the stopwatch instead :)
+
+ Take note of the output of radclient. If there were lots of
+ failures, something is wrong. All authentications should
+ succeed.
+
+ 6. Take note of the output from the above 'time' command.
+ The output format should be something similar to the
+ following (on linux, this for example only!):
+
+ 1.72user 0.53system 5:11.34elapsed 0%CPU
+ (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs
+ (340major+29minor)pagefaults 0swaps
+
+ This means it took 5:11 (311 seconds) to authenticate
+ 10,000 users. Simple division tells us this is:
+
+ 10,000 auths / 311 seconds = 32.1543 auths/second
+
+ B. Local users testing
+
+ 1. Copy the 'radius.users' file from the script over your 'users'
+ file. Make sure you do NOT have a DEFAULT entry or you will
+ invalidate this test.
+
+ 2. Restart radiusd (kill and restart)
+
+ 3. Run radclient (See A-5 above for NOTES on this):
+
+ # time /usr/local/bin/radclient -q -s -f radius.test \
+ <yourhostname> auth <secret>
+
+ 4. Take note of the output from the above 'time' command, and
+ divide the number of auths (10,000 in this case) with the
+ number of seconds it took to complete. See A6 above for
+ more info.
+
+ C. Cached system users
+
+ 1. Set 'cache=yes' in your radiusd.conf file
+
+ 2. Restart radiusd (ie, kill it and restart, not just a HUP)
+
+ 3. Perform the same steps outlined above for testing System users (A)
+
+ D. Other methods
+
+ There is no reason why you can't use some of this to test modules
+ for PAM, SQL, LDAP, etc, but that will require a little extra
+ work on your end (ie, getting the users/passes you generated into
+ the corresponding database). However, by now you should have a
+ good idea of how to test once you do that.
+
+ Also, play around with compile options like --with-thread,
+ --with-thread-pool, etc. Run radiusd with '-s' so it runs
+ one process only, etc etc. Play around with it.
+
+4. CAVEATS
+
+The above test procedures make no allowances for users that login with
+incorrect usernames or passwords. If you want a true test of performance,
+you should add in lots of bad usernames and passwords to the radius.test
+file and then re-run 'radclient' with that file as input.
+
+Additionally, these tests make no reference to how the pre-authenticate,
+post-authenticate, and accounting methods you choose could affect server
+performance. For example, checking for simultaneous use after authenti-
+cating the user is obviously going to slow down authenticate performance.
+
+The numbers you get from this test are raw authentications/second in a
+perfect environment. Do not expect this kind of result in the real world.
+However, having tested in this manner, you will have a good idea of which
+authentication methods and compilation options give you the best base to
+start from, which is key to an efficient server.
+
+5. RESULTS
+
+I'd really rather not post results because they will vary tremendously
+with other system-specific configuration. This is exactly the reason
+you should run tests of this nature, to find what's best for *your*
+system. Good luck!
+
+
diff --git a/doc/deployment/supervise-radiusd.rst b/doc/deployment/supervise-radiusd.rst
new file mode 100644
index 0000000..e4922ed
--- /dev/null
+++ b/doc/deployment/supervise-radiusd.rst
@@ -0,0 +1,163 @@
+
+Supervising the Radiusd Daemon
+==============================
+
+Introduction
+------------
+
+We all hope that our radius daemons won't die in the middle of the
+nite stranding customer and beeping beepers. But, alas, it's going to
+happen, and when you least expect it. That's why you want a another
+process watching your radius daemon, restarting it if and when it
+dies.
+
+This text describes how to setup both the free radius daemon so that
+it is automatically restarted if the process quits unexpectedly. To
+do this, we'll use either Dan Bernstein's 'daemontools' package or the
+inittab file. Note: The radwatch script that used to be part of this
+distribution, is depreciated and SHOULD NOT BE USED.
+
+Setting Up Daemontools
+----------------------
+
+First, download (and install) daemontools from:
+
+ http://cr.yp.to/daemontools.html
+
+The latest version as of this writing is 0.70. It would be well worth
+your while to read all the documentation at that site too, as you can
+do much more with daemontools than I describe here.
+
+Next, we'll need a directory for the radius 'service' to use with
+daemontools. I usually create a dir '/var/svc' to hold all my
+daemontool supervised services. i.e.::
+
+ $ mkdir /var/svc
+ $ mkdir /var/svc/radiusd
+
+Now we just need a short shell script called 'run' in our new service
+directory that will start our daemon. The following should get you
+started::
+
+ #!/bin/sh
+ # Save as /var/svc/radiusd/run
+ exec /usr/local/sbin/radiusd -s -f
+
+Of course you'll want to make that 'run' file executable::
+
+ $ chmod +x /var/svc/radiusd/run
+
+Note, you *MUST* use the '-f' option when supervising. That option
+tells radiusd not to detach from the tty when starting. If you don't
+use that option, the daemontools will always think that radiusd has
+just died and will (try to) restart it. Not good.
+
+Now the only left to do is to start the 'supervise' command that came
+with daemontools. Do that like so::
+
+ $ supervise /var/svc/radiusd
+
+Maintenance With Daemontools
+----------------------------
+
+ Any maintenance you need to do with almost certainly be done with the
+ 'svc' program in the deamontools package. i.e.::
+
+ Shutdown radiusd:
+ $ svc -d /var/svc/radiusd
+
+ Start it back up:
+ $ svc -u /var/svc/radiusd
+
+ Send HUP to radiusd:
+ $ svc -h /var/svc/radiusd
+
+ Shutdown and stop supervising radiusd:
+ $ svc -dx /var/svc/radiusd
+
+Supervising With Inittab
+------------------------
+
+This is really pretty easy, but it is system dependent. I strongly
+suggest you read the man pages for your 'init' before playing with
+this. You can seriously hose your system if you screw up your
+inittab.
+
+Add this line (or something similar to it) to your inittab::
+
+ fr:23:respawn:/usr/local/sbin/radiusd -f -s &> /dev/null
+
+Now all that's left is to have the system reread the inittab. Usually
+that's done with one of the following::
+
+ $ telinit Q
+
+or::
+
+ $ init q
+
+Now you should see a 'radiusd' process when you issue a 'ps'. If you
+don't, try to run the radiusd command you put in inittab manually. If
+it works, that means you didn't tell the system to reread inittab
+properly. If it doesn't work, that means your radius start command is
+bad and you need to fix it.
+
+Acknowledgements
+----------------
+
+ Document author : Jeff Carneal
+ daemontools auther : Dan Bernstein
+ Further daemontool notes (below): Antonio Dias
+ Radwatch note : Andrey Melnikov
+
+Further Daemontools notes
+=========================
+
+Here are some notes by Antonia Dias sent to the free radius mailing
+list. Some of you may find this useful after reading the above and the
+docs for daemontools.
+
+Daemontools Instructions
+------------------------
+
+I am running radiusd under supervise from daemontools without
+problems. The only thing I am missing right now is an option to force
+radiusd to send log to stderr so I can manage logs better with
+multilog (also included in daemontools package). Here is the procedure
+I've been following (for Cistron RADIUS)::
+
+ $ groupadd log
+ $ useradd -g log log
+ $ mkdir /etc/radiusd
+ $ mkdir /etc/radiusd/log
+ $ mkdir /etc/radiusd/log/main
+ $ chmod +t+s /etc/radiusd /etc/radiusd/log
+ $ chown log.log /etc/radiusd/log/main
+
+Here are the contents of run files from '/etc/radiusd' and '/etc/radiusd/log'::
+
+ $ cd /etc/radiusd
+ $ cat run
+ #!/bin/sh
+ exec 2>&1
+ exec /usr/sbin/radiusd -fyzx
+ $ cd /etc/radiusd/log
+ $ cat run
+ #!/bin/sh
+ exec setuidgid log multilog t ./main
+
+ To make service wake-up do::
+
+ $ ln -sf /etc/radiusd /service
+
+ Hang-up (to reload config) it using::
+
+ $ svc -h /service/radiusd
+
+Disable (down) it using::
+
+ $ svc -d /service/radiusd
+
+Reenable (up) it using::
+
+ $ svc -u /service/radiusd
diff --git a/doc/deployment/tuning_guide b/doc/deployment/tuning_guide
new file mode 100644
index 0000000..264749f
--- /dev/null
+++ b/doc/deployment/tuning_guide
@@ -0,0 +1,58 @@
+------------ MAIN SERVER -------------
+o If you have a large user base and/or many authentication requests try
+ using a scalable authentication mechanism like ldap or sql.
+o Enable noatime on all the freeradius log files or better yet on the
+ freeradius log directory.
+o Always use the latest cvs version. It will probably contain a few
+ fixes and enhancements.
+o Always try to use the least modules possible. In particular if you
+ can avoid it do not use the detail and radwtmp (files) modules.
+ They will slow down your accounting.
+o Use the users file to only set default profiles. Do not place any
+ users there. Keep it as small as possible. Always set default
+ attributes in the users file and don't fill the user entries in
+ ldap/sql with default values. In general the ldap/sql user profiles
+ should contain user attributes only in special user cases.
+o Tune thread pool parameters to match your size requirements.
+ Set max_requests_per_server to zero to avoid server thread restarts.
+o Enlarge the timeout (10 secs) and retries (5-7) in the access servers
+ for accounting. That way you won't lose any accounting information.
+o Use well tuned Fast Ethernet connections to minimize latency.
+o freeradius is multi threaded and i/o bound. That means you should use
+ the latest OS kernels/patches for improved multi processor and
+ network performance.
+
+------------ LDAP MODULE -------------
+o Try to maximize caching in the ldap server. In particular *always*
+ enable indexing of the uid attribute (equality index) and the
+ cn attribute (equality index - the cn attribute is used to search
+ for groups). Make the ldap server entry/directory cache memory sizes
+ as large as possible. In general try allocating as much memory as you
+ can afford to your ldap server.
+o Put default profiles in ldap. User entries should only contain
+ non standard values in order to remain small and maximize the gains
+ of caching the user default/regular profiles.
+o Enable group caching in groups.
+
+------------ SQL MODULE --------------
+o Use the sql module in the session section instead of the radutmp module.
+ It works *much* quicker.
+o Create a multi column index for the (UserName,AcctStopTime) attributes especially
+ if you are using sql for double login detection.
+o If you are using mysql and you do a lot of accounting try using InnoDB for the radacct
+ table instead of MyISAM (this should be the default in all schemas)
+o Add AcctUniqueId in the accounting_stop query. Especially if you have a lot of access
+ servers or your NAS does not send very random Session-Ids. That way you will always have
+ one candidate row to search for, instead of all the rows that have the same AcctSessionId
+
+------------ COUNTER MODULE ----------
+o Enable noatime on the counter db files.
+o Tune the cache_size configuration directive to match your needs.
+ The cache size should be set to 2-3 * number of available nas ports.
+o Keep the database in a memory mapped file if you can help. Backup the
+ file every 10 mins to the disk and copy it to the memory mapped one
+ on server startup.
+
+------------ RADUTMP MODULE ----------
+o Enable noatime on the radutmp file
+o Don't use it
diff --git a/doc/developer/autotools.md b/doc/developer/autotools.md
new file mode 100644
index 0000000..a7ad6a8
--- /dev/null
+++ b/doc/developer/autotools.md
@@ -0,0 +1,179 @@
+FreeRADIUS use of GNU autotools
+===============================
+
+The full autotools suite includes many utilities, which we do not
+need or want to use. Especially libtool, for which we use the
+faster replacement, jlibtool.
+
+In a normal autotools setup, one would run "autoreconf" to rebuild
+all of the configure scripts, which will perform at least the
+following tasks:
+
+ - aclocal
+ - autoconf
+ - autoheader
+ - automake
+ - libtoolize
+
+Specifically, all we really want to run is `autoconf`, to rebuild
+the configure scripts.
+
+We have a more complicated setup than most. There is normally just
+one `configure` script, in the top-level directory. In FreeRADIUS
+there are also configure scripts in most RLM module directories as
+well. Autotools is not really set up to handle this well,
+preferring to treat every sub-directory as a separate project.
+
+This means that e.g. cache files are not shared, and include files
+(for configure macros) are not found as they are expected to be in
+the current directory.
+
+What's more, autoconf macros can be found in multiple places - the
+automake install directory, the system aclocal directory, and in
+multiple places in the FreeRADIUS source (mainly `m4/`, but also
+`acinclude.m4`, both potentially in multiple places).
+
+In our setup we want to run the following only:
+
+ - autoconf, to generate configure files and `all.mk` make files.
+ - autoheader, to generate header files.
+
+
+autoconf
+--------
+
+`autoconf` expands a `configure.ac` file to create a `configure`
+script, with optionally also a Makefile. We generate a makefile
+called `all.mk` to work with the boilermake system.
+
+Being based on m4, autoconf needs to find macro definitions from
+somewhere, which will be expanded as needed upon invocation.
+autoconf has several search paths for macros, including some
+system paths for its own internal macros.
+
+Notably within the project, autoconf looks in `aclocal.m4` to find
+"local" macros to add. These days, `aclocal.m4` is supposed to be
+written by the `aclocal` script, so autotools added the concept of
+`acinclude.m4` to put local macros. `aclocal` will add an include
+directive at the bottom of `aclocal.m4` to include the
+`acinclude.m4` file, if it is found in the current directory.
+
+When `aclocal` is run it will scan `configure.ac` for anything
+that looks like a macro to expand. It will then search project
+directories, the automake system directory and the aclocal system
+directory, to find any macros that match. These are copied into
+the `aclocal.m4` file. `autoconf` will then pick up these macro
+definitions and use them when expanding `configure.ac`. Notably,
+macros can be in `*.m4` files in given search directories and
+`aclocal` will extract the macros and copy them over.
+
+`autoconf` itself will not look in `*.m4` files, only in
+`aclocal.m4` and, if that is not found, `acinclude.m4`.
+
+We therefore have, _within one level directory_:
+
+ - `acinclude.m4`, local macro definitions;
+ - `aclocal.m4`, macros collated by `aclocal`;
+ - `m4/` or other directories, macro files searched by `aclocal`;
+ - `configure.ac` the input configure script;
+ - `all.mk`, `configure`, etc as outputs from `autoconf`.
+
+The GNU Autotools manual these days recommends splitting macros
+up, one file per macro, and putting them in the `m4/` directory
+rather than in the `acinclude.m4` file. This makes them much
+easier to maintain.
+
+
+FreeRADIUS sub-directories
+--------------------------
+
+All the above is not too much of an issue for the top-level
+configure script. We can have an automatically generated
+`aclocal.m4` file, macros in `m4/` and extra components in
+`acinclude.m4` if needed. However, the sub-directory configure
+scripts really want to be kept as small as possible. There is no
+real need for a separate `aclocal.m4` file if all of the configure
+scripts could be scanned together. The top-level `m4/` directory and
+`acinclude.m4` file can be used.
+
+Unfortunately, autotools doesn't like to work like this. It wants
+all files to be in one directory, and `aclocal` won't scan more
+than one `configure.ac` file.
+
+The two compromise solutions seem to be:
+
+ - Don't use `aclocal`.
+
+ - Nearly all local macros are put in the top-level
+ `acinclude.m4` file.
+ - A few local macros can go in `m4/`, but they have to
+ explicitly included in configure.ac scripts with
+ `m4_include()`.
+ - `autoconf` has to be run with multiple `-I` include args to
+ capture all the places where macros could be.
+ - Any missing macros won't get pulled in from system
+ locations, because `aclocal` noramally does that.
+ - Sub-directories are relatively clean, e.g. no `aclocal.m4`
+ or `acinclude.m4` files all over the place.
+
+ - Use `aclocal`.
+
+ - The `-I` arg can be passed to `aclocal` which makes it
+ search multiple project directories for local macros to copy
+ to `aclocal.m4`.
+ - All directories with a `configure` script must have an
+ `aclocal.m4` file to collate macros from the top-level `m4/`
+ directory.
+ - The top-level `acinclude.m4` file is ignored except in the
+ top-level configure script, meaning it needs to be symlinked
+ or copied everywhere else.
+ - If `autoconf` finds an `aclocal.m4` file it no longer seems
+ to look for macros elsewhere.
+ - Sub-directories get messy with `aclocal.m4` and
+ `acinclude.m4` files, though these don't need to be checked
+ into the repository.
+ - The top-level `m4/` directory can contain all macros as
+ separate files, which is much cleaner than `acinclude.m4`.
+ - System macros will be found and used.
+
+We pretty much need to use `aclocal` - it removes the need for an
+`acinclude.m4` file (tidier), picks up macros from `m4/`
+automatically (tidier), removes the need for `m4_include()` macros
+(tidier), and means that macros will be found that might not be
+shipped in the FreeRADIUS distribution (easier).
+
+That comes with some downsides as above - we will end up with
+`aclocal.m4` files all over the place, and have to handle the case
+where things were originally in `acinclude.m4` and are not macros.
+
+Fixing `aclocal.m4` files can be done by either including them in
+git (unnecessary) or hiding them with `.gitignore` (best).
+
+Picking up non-macro definitions from `acinclude.m4` can be done
+by adding a new macro, `FR_INIT()`, which defines anything needed.
+In fact, as long as that macro is included, the _entire_
+`m4/fr_init.m4` file will be included by `aclocal`. This means the
+extra definition doesn't even need to be inside the macro.
+
+
+Rebuilding the configure scripts
+================================
+
+The normal way to rebuild all of the autotools outputs is to run
+`autoreconf`. This must not be run with FreeRADIUS as it will
+initialise and use libtool and other things we do not want.
+
+Instead, we have a make target to rebuild everything needed.
+
+ make reconfig
+
+This will rebuild any configure files that are out of date.
+However, sometimes everything needs to be forced, e.g. due to some
+macros changing that are missed by the Make dependencies (maybe
+from the system directories). In this case a forced rebuild can be
+undertaken with:
+
+ find . -name configure.ac | xargs touch
+ make reconfig
+
+This will ensure that _all_ configure scripts are rebuilt.
diff --git a/doc/developer/coding-methods.rst b/doc/developer/coding-methods.rst
new file mode 100644
index 0000000..444696d
--- /dev/null
+++ b/doc/developer/coding-methods.rst
@@ -0,0 +1,242 @@
+Helpful coding methods
+======================
+
+The following is a short set of guidelines to follow while
+programming. It does not address coding styles, function naming
+methods, or debugging methods. Rather, it describes the processes
+which SHOULD go on in the programmers mind, while he is programming.
+
+Coding standards apply to function names, the look of the code, and
+coding consistency. Coding methods apply to the daily practices used
+by the programmer to write code.
+
+
+
+1. Comment your code.
+
+ If you don't, you'll be forced to debug it 6 months later, when
+ you have no clue as to what it's doing.
+
+ If someone REALLY hates you, you'll be forced to debug
+ un-commented code that someone else wrote. You don't want to do
+ that.
+
+ For FreeRADIUS use doxygen @style comments so you get the benefits
+ of docs.freeradius.org.
+
+2. Give things reasonable names.
+
+ Variables and functions should have names. Calling them 'x',
+ 'xx', and 'xxx' makes your life hell. Even 'foo' and 'i' are
+ problematic.
+
+ Avoid smurfs. Don't re-use struct names in field names i.e.
+ struct smurf {
+ char *smurf_pappa_smurf;
+ }
+
+ If your code reads as full english sentences, you're doing it
+ right.
+
+
+3. Check input parameters in the functions you write.
+
+ Your function CANNOT do anything right if the user passed in
+ garbage, and you were too lazy to check for garbage input.
+
+ assert() (rad_assert()) is ugly. Use it.
+
+ GIGO is wrong. If your function gets garbage input, it
+ should complain loudly and with great descriptiveness.
+
+
+4. Write useful error messages.
+
+ "Function failed" is useless as an error message. It makes
+ debugging the code impossible without source-level instrumentation.
+
+ If you're going to instrument the code at source level for error
+ messages, leave the error messages there, so the next sucker won't
+ have to do the same work all over again.
+
+
+5. Check error conditions from the functions you call.
+
+ Your function CANNOT do anything right if you called another
+ function, and they gave you garbage output.
+
+ One of the most common mistakes is::
+
+ fp = fopen(...);
+ fgetc(fp); /* core dumps! */
+
+ If the programmer had bothered to check for a NULL fp (error
+ condition), then he could have produced a DESCRIPTIVE error
+ message, instead of having his program core dump.
+
+
+6. Core dumps are for weenies.
+
+ If your program core dumps accidentally, you're a bad programmer.
+ You don't know what your program is doing, or what it's supposed
+ to be doing when anything goes wrong.
+
+ If it hits an assert() and calls abort(), you're a genius. You've
+ thought ahead to what MIGHT go wrong, and put in an assertion to
+ ensure that it fails in a KNOWN MANNER when something DOES go
+ wrong. (As it usually does...)
+
+
+7. Initialize your variables.
+
+ memset() (talloc_zero()) is your friend. 'ptr = NULL' is
+ nice, too.
+
+ Having variables containing garbage values makes it easy for the
+ code to do garbage things. The contents of local variables are
+ inputs to your function. See #3.
+
+ It's also nearly impossible for you to debug any problems, as you
+ can't tell the variables with garbage values from the real ones.
+
+
+8. Don't allow buffer over-runs.
+
+ They're usually accidental, but they cause core dumps.
+ strcpy() and strcat() are ugly. Use them under duress.
+
+ sizeof() is your friend.
+
+
+9. 'const' is your friend.
+
+ If you don't mean to modify an input structure to your function,
+ declare it 'const'. Declare string constants 'const'. It can't
+ hurt, and it allows more errors to be found at compile time.
+
+ Use 'const' everywhere. Once you throw a few into your code, and
+ have it save you from stupid bugs, you'll blindly throw in 'const'
+ everywhere. It's a life-saver.
+
+
+10. Use C compiler warnings.
+
+ Turn on all of the C compiler warnings possible. You might have
+ to turn some off due to broken system header files, though. But
+ the more warnings the merrier.
+
+ Getting error messages at compile time is much preferable to
+ getting core dumps at run time. See #7.
+
+ Notice that the C compiler error messages are helpful? You should
+ write error messages like this, too. See #4.
+
+
+11. Avoid UNIXisms and ASCIIisms and visualisms.
+
+ You don't know under what system someone will try to run your code.
+ Don't demand that others use the same OS or character set as you use.
+
+ Never assign numbers to pointers. If foo is a char*, and you want it
+ to be be null, assign NULL, not 0. The zeroth location is perfectly
+ as addressable as any other on plenty of OSes. Not all the world
+ runs on Unix (though it should :) ).
+
+ Another common mistake is to assume that the zeroth character in the
+ character set is the string terminator. Instead of terminating a
+ string with 0, use '\0', which is always right. Similarly, memset()
+ with the appropriate value: NULL, '\0', or 0 for pointers, chars,
+ and numbers.
+
+ Don't put tabs in string constants, either. Always use '\t' to
+ represent a tab, instead of ASCII 9. Literal tabs are presented to
+ readers of your code as arbitrary whitespace, and it's easy to mess
+ up.
+
+
+12. Make conditionals explicit.
+
+ Though it's legal to test "if (foo){}", if you test against the
+ appropriate value (like NULL or '\0'), your code is prettier and
+ easier for others to read without having to eyeball your prototypes
+ continuously to figure out what you're doing (especially if your
+ variables aren't well-named). See #2.
+
+
+13. Test your code.
+
+ Even Donald Knuth writes buggy code. You'll never find all of the
+ bugs in your code unless you write a test program for it.
+
+ This also means that you'll have to write your code so that it
+ will be easily testable. As a result, it will look better, and be
+ easier to debug.
+
+Hints, Tips, and Tricks
+-----------------------
+
+This section lists many of the common "rules" associated with code
+submitted to the project. There are always exceptions... but you must
+have a really good reason for doing so.
+
+ 1. Read the Documentation and follow the CodingStyle
+
+ The FreeRADIUS server has a common coding style. Use real tabs
+ to indent. There is whitespace in variable assignments.
+ (i = 1, NOT i=1).
+
+ When in doubt, format your code to look the same as code already
+ in the server. If your code deviates too much from the current
+ style, it is likely to be rejected without further review, and
+ without comment.
+
+ 2. #ifdefs are ugly
+
+ Code cluttered with ifdefs is difficult to read and
+ maintain. Don't do it. Instead, put your ifdefs in a header, and
+ conditionally define 'static inline' functions, or macros, which
+ are used in the code. Let the compiler optimize away the "no-op"
+ case.
+
+ Simple example, of poor code::
+
+ #ifdef CONFIG_MY_FUNKINESS
+ init_my_stuff(foo);
+ #endif
+
+ Cleaned-up example:
+
+ (in header)::
+
+ #ifndef CONFIG_MY_FUNKINESS
+ static inline void init_my_stuff(char *foo) {}
+ #endif
+
+ (in the code itself)::
+
+ init_my_stuff(dev);
+
+ 3. 'static inline' is better than a macro
+
+ Static inline functions are greatly preferred over macros. They
+ provide type safety, have no length limitations, no formatting
+ limitations, and under gcc they are as cheap as macros.
+
+ Macros should only be used for cases where a static inline is
+ clearly suboptimal [there a few, isolated cases of this in fast
+ paths], or where it is impossible to use a static inline
+ function [such as string-izing].
+
+ 'static inline' is preferred over 'static __inline__', 'extern
+ inline', and 'extern __inline__'.
+
+ 4. Don't over-design.
+
+ Don't try to anticipate nebulous future cases which may or may
+ not be useful: "Make it as simple as you can, and no simpler"
+
+ Split up functionality as much as possible. If your code needs
+ to do two unrelated things, write two functions. Mashing two
+ kinds of work into one function makes the server difficult to
+ debug and maintain.
+
diff --git a/doc/developer/contributing.rst b/doc/developer/contributing.rst
new file mode 100644
index 0000000..b40f98c
--- /dev/null
+++ b/doc/developer/contributing.rst
@@ -0,0 +1,127 @@
+Submitting patches or diff's to the FreeRADIUS project
+======================================================
+
+For a person or company wishing to submit a change to the FreeRADIUS project
+the process can sometimes be daunting if you're not familiar with "the system."
+This text is a collection of suggestions which can greatly increase the chances
+of your change being accepted.
+
+Note: Only trivial patches will be accepted via email. Large patches, or
+patches that modify a number of files MUST be submitted as a pull-request via
+GitHub.
+
+Hints and tips
+--------------
+
+1. Describe your changes
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Describe the technical detail of the change(s) your patch or commit includes.
+
+Be as specific as possible. The WORST descriptions possible include things like
+"update file X", "bug fix for file X", or "this patch includes updates for
+subsystem X. Please apply."
+
+If your description starts to get long, that's a sign that you probably need to
+split up your commit. See #3, next.
+
+2. Separate your changes
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Separate each logical change into its own commit.
+
+For example, if your changes include both bug fixes and performance
+enhancements for a single module, separate those changes into two or more
+patches.
+
+On the other hand, if you make a single change to numerous files, group those
+changes into a single commit. Thus a single LOGICAL change is contained within
+a single commit.
+
+If one commit depends on another commit in order for a change to be complete,
+that is OK. Simply note "this commit depends on commit X" in the extended
+commit description.
+
+If your commit includes significant whitespace changes these should also be
+broken out into another, separate, commit.
+
+Submitting patches via GitHub
+-----------------------------
+
+See the following links for more details about submitting via github:
+
+- https://help.github.com/articles/fork-a-repo
+- http://wiki.freeradius.org/contributing/GitHub
+
+Submitting patches via email
+----------------------------
+
+1. "diff -u"
+~~~~~~~~~~~~
+Use ``diff -u`` or ``diff -urN`` to create patches.
+
+All changes to the source occur in the form of patches, as generated by
+diff(1). When creating your patch, make sure to create it in "unified diff"
+format, as supplied by the '-u' argument to diff(1). Patches should be based in
+the root source directory, not in any lower subdirectory.
+
+To create a patch for a single file, it is often sufficient to do::
+
+ SRCTREE=/home/user/src/freeradiusd/
+ MYFILE=src/modules/rlm_foo/foo.c
+
+ cd $SRCTREE
+ cp $MYFILE $MYFILE.orig
+ vi $MYFILE # make your change
+ diff -u $MYFILE.orig $MYFILE > /tmp/patch
+
+To create a patch for multiple files, you should unpack a "vanilla", or
+unmodified source tree, and generate a diff against your own source tree. For
+example::
+
+ MYSRC=/home/user/src/freeradiusd-feature/
+
+ gunzip freeradiusd-version.tar.gz
+ tar xvf freeradiusd-version.tar
+ diff -urN freeradiusd-version $MYSRC > ~/feature-version.patch
+
+
+2. Select e-mail destination
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you are on the developers mailing list, send the patch there.
+freeradius-devel@lists.freeradius.org
+
+Otherwise, send the patch to 'patches@freeradius.org'
+
+3. No MIME, no links, no compression, no attachments. Just plain text
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The developers need to be able to read and comment on the changes you are
+submitting. It is important for a developer to be able to "quote" your changes,
+using standard e-mail tools, so that they may comment on specific portions of
+your code.
+
+For this reason, all patches should be submitting e-mail "inline".
+
+Do not attach the patch as a MIME attachment, compressed or not. Many popular
+e-mail applications will not always transmit a MIME attachment as plain text,
+making it impossible to comment on your code. A MIME attachment also takes a
+bit more time to process, decreasing the likelihood of your MIME-attached
+change being accepted.
+
+Compressed patches are generally rejected outright. If the developer has to do
+additional work to read your patch, the odds are that it will be ignored
+completely.
+
+4. E-mail size
+~~~~~~~~~~~~~~
+
+Large changes are not appropriate for mailing lists, and some maintainers. If
+your patch, exceeds 5Kb in size, you must submit the patch via GitHub instead.
+
+5. Name the version of the server
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is important to note, either in the subject line or in the patch
+description, the server version to which this patch applies.
diff --git a/doc/developer/module_interface.rst b/doc/developer/module_interface.rst
new file mode 100644
index 0000000..2fc510e
--- /dev/null
+++ b/doc/developer/module_interface.rst
@@ -0,0 +1,186 @@
+
+RLM Module Interface (for developers)
+=====================================
+
+Overview
+--------
+
+Intent of the server
+^^^^^^^^^^^^^^^^^^^^
+
+FreeRADIUS is an authentication server. It does RADIUS authorization,
+authentication, and accounting. It does NOT do database management,
+user configuration updates, or email. All of those functions may be
+more easily (and correctly) performed in programs outside of the
+server.
+
+The only functionality performed by the server is:
+
+- receive a RADIUS request
+ - process the request
+ - look up information one or more databases
+- store information in one or more databases (proxying can be viewed this way)
+- respond to the request
+
+There is no room, or need, for timers, listening on sockets, or
+anything else in the server. Adding those functions to the server
+means that it will become more complex, more unstable, more insecure,
+and more difficult to maintain.
+
+
+Intent of the modules
+^^^^^^^^^^^^^^^^^^^^^
+
+The intent of modules is that they do small, simple, well-defined
+things when RADIUS packets are received. If the module does things
+when RADIUS packets are NOT received, then it has no business being in
+the server. Similarly, the module infrastructure is NOT intended to
+allow servers, applications, timed events, or anything other than
+handling incoming RADIUS packets.
+
+Modules perform an action when RADIUS packets are received. Modules
+which do more (creating threads, forking programs) will NOT be added
+to the server, and the server core will NOT be modified to enable
+these kinds of modules. Those functions more properly belong in a
+separate application.
+
+Modules ARE permitted to open sockets to other network programs, and
+to send and receive data on those sockets. Modules are NOT permitted
+to open sockets, and to listen for requests. Only the server core has
+that functionality, and it only listens for RADIUS requests.
+
+
+Module outline
+^^^^^^^^^^^^^^
+
+The fundamental concepts of the rlm interface are module, instance,
+and component.
+
+A module is a chunk of code that knows how to deal with a particular
+kind of database, or perhaps a collection of similar
+databases. Examples:
+
+- rlm_sql contains code for talking to MySQL or Postgres, and for mapping RADIUS records onto SQL tables
+- rlm_unix contains code for making radiusd fit in well on unix-like systems, including getpw* authentication and utmp/wtmp-style logging.
+
+An instance specifies the actual location of a collection data that
+can be used by a module. Examples:
+
+- /var/log/radutmp
+- "the MySQL database on bigserver.theisp.com.example"
+
+A module can have multiple components which act on
+RADIUS requests at different stages. The components are:
+
+- authorization: check that a user exists, decide on an authentication method or proxy realm, and possibly apply some attributes to be returned in the reply packet.
+- authentication: verify that the password is correct.
+- preaccounting: decide whether to proxy the request, and possibly add attributes that should be included in any logs
+- accounting: record the request in the log
+- checksimul: count the number of active sessions for the user
+- postauth: process the response before it's sent to the NAS
+- preproxy: process a request before it's proxied
+- postproxy: filter attributes from a reply to a proxied request
+
+A module declares which components it supports by putting function
+pointers in its "module_t rlm_*" structure.
+
+
+Module configuration
+^^^^^^^^^^^^^^^^^^^^
+
+The administrator requests the creation of a module instance by adding
+it inside the modules{} block in radiusd.conf. The instance definition
+looks like this::
+
+ module_name [instance_name] {
+ param1 = value1
+ param2 = value2
+ param3 = value3
+ ...
+ }
+
+The module_name is used to load the module. To see the names of the available
+modules, look for the rlm\_\*.so files in $installprefix/lib. The module_name
+is that, minus the rlm\_ and the .so.
+
+instance_name is an identifier for distinguishing multiple instances of the
+same module. If you are only loading a module once, you can leave out the
+instance_name and it will be assumed to be the same as the module_name.
+
+The parameters inside the module block are passed without interpretation to
+the module and generally point to the exact location of a database or enable
+optional features of the module. Each module should document what parameters
+it accepts and what they do.
+
+For each Access-Request that comes to the server, the authorize{}
+block is called. Then one of the Auth-Type{} blocks from authenticate{}
+is called, depending on the Auth-Type attribute that was chosen by
+authorize{}. Finally, the post-auth{} block is called. If authorize{}
+set the Proxy-To-Realm attribute, then proxying takes over via
+pre-proxy{} and post-proxy{}, and the local authenticate{} phase is
+skipped.
+
+For each Accounting-Request that comes to the server, the preacct{} block is
+called, followed by the accounting{} block. accounting{} is skipped if
+preacct{} sets Proxy-To-Realm.
+
+For an explanation of what "calling" a config block means, see
+the "configurable_failover" file.
+
+
+The lifetime of a module
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+When the server is starting up, or reinitializing itself as a result of a
+SIGHUP, it reads the modules{} section. Each configured module will be loaded
+and its init() method will be called::
+
+ int init(void)
+
+The init() method should take care of
+any setup that is not tied to a specific instance. It will only be called
+once, even if there are multiple instances configured.
+
+For each configured instance, after the init() method, the instantiate()
+method is called. It is given a handle to the configuration block holding its
+parameters, which it can access with cf_section_parse().::
+
+ int instantiate(CONF_SECTION \*cs, void \**instance)
+
+The instantiate() function should look up options in the config section, and
+open whatever files or network connections are necessary for the module to do
+its job. It also should create a structure holding all of the persistent
+variables that are particular to this instance (open file descriptors,
+configured pathnames, etc.) and store a pointer to it in \*instance. That
+void \* becomes a handle (some would call it a "cookie") representing this
+instance. The instance handle is passed as a parameter in all subsequent
+calls to the module's methods, so they can determine which database they are
+supposed to act on.
+
+The authorize(), authenticate(), preaccounting(), and accounting() functions
+are all called the same way::
+
+ int authorize(void \*instance, REQUEST \*request)
+ int authenticate(void \*instance, REQUEST \*request)
+ int preaccounting(void \*instance, REQUEST \*request)
+ int accounting(void \*instance, REQUEST \*request)
+
+they each receive the instance handle and the request, and are expected to
+act on the request using the database pointed to by the instance handle
+(which was set by the instantiate() function).
+
+When the server is being shut down (as the first part of SIGHUP for example)
+detach() is called for each module instance.::
+
+ int detach(void \*instance)
+
+The detach() method should release whatever resources were allocated by the
+instantiate() method.
+
+After all instances are detached, the destroy() method is called.::
+
+ int destroy(void)
+
+It should release resources that were acquired by the init() method.
+
+--Alan Curry <pacman@world.std.com>
diff --git a/doc/developer/release-method.rst b/doc/developer/release-method.rst
new file mode 100644
index 0000000..6e218c0
--- /dev/null
+++ b/doc/developer/release-method.rst
@@ -0,0 +1,42 @@
+Release Method
+==============
+
+As of 2.0, the release process is much simpler. Edit the
+Changelog with the version number and any last updates.
+
+vi doc/ChangeLog
+git commit doc/ChangeLog
+
+
+ Change version numbers in the VERSION file:
+
+vi VERSION
+git commit VERSION
+
+
+ Make the files
+
+ Note that it also does "make dist-check", which checks
+ the build rules for various packages.
+
+make dist
+
+
+ Validate that the packages are OK. If so, tag the release.
+
+ Note that this does NOT actually do the tagging! You will
+ have to run the command it prints out yourself.
+
+make dist-tag
+
+
+ Sign the packages. You will need the correct GPG key for this
+ to work.
+
+make dist-sign
+
+
+ Push to the FTP site. You will need write access to the FTP site
+ for this to work.
+
+make dist-publish
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000..c8ef10a
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,29 @@
+.. FreeRADIUS documentation master file, created by
+ sphinx-quickstart on Tue May 18 13:26:39 2010.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+.. toctree::
+ :maxdepth: 2
+
+ aaa
+ ldap_howto
+ load-balance
+
+ coding-methods
+ DIFFS
+ release-method
+
+ cisco
+ configurable_failover
+ processing_users_file
+ proxy
+ variables
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/doc/modules/RADIUS-LDAP-eDirectory b/doc/modules/RADIUS-LDAP-eDirectory
new file mode 100644
index 0000000..abfa1cf
--- /dev/null
+++ b/doc/modules/RADIUS-LDAP-eDirectory
@@ -0,0 +1,18 @@
+"Integrating Novell eDirectory with FreeRADIUS"
+
+Overview
+You can integrate Novell® eDirectoryTM 8.7.1 or later with FreeRADIUS
+1.0.2 onwards to allow wireless authentication for eDirectory users.
+By integrating eDirectory with FreeRADIUS, you can do the following:
+* Use universal password for RADIUS authentication.
+ Universal password provides single login and authentication for
+ eDirectory users. Therefore, the users need not have a separate
+ password for RADIUS and eDirectory authentication.
+* Enforce eDirectory account policies for users.
+ The existing eDirectory policies on the user accounts can still be
+ applied even after integrating with RADIUS. Also, you can make use
+ of the intruder lockout facility of eDirectory by logging the
+ failed logins into eDirectory.
+
+For configuration information please refer to the Novell documentation
+ http://www.novell.com/documentation/edir_radius/index.html
diff --git a/doc/modules/ldap_howto.rst b/doc/modules/ldap_howto.rst
new file mode 100644
index 0000000..53f0f3e
--- /dev/null
+++ b/doc/modules/ldap_howto.rst
@@ -0,0 +1,1648 @@
+LDAP Configuration
+==================
+
+This document describes how to setup Freeradius on a Freebsd machine
+using LDAP as a backend. This is by no means complete and your
+mileage may vary. If you are having any problems with the setup of
+your freeradius installation, please read the documentation that comes
+with Freeradius first as that is where all the information for this
+project came from. If you find any bugs, typos, alternative ideas, or
+just plain wrong information, please let me know by sending an email
+to the address above.
+
+The radius servers in this document are built on Freebsd 4.8, using
+Freeradius .81 with OpenLDAP 2.0.27 as the backend. The servers are
+designed to support customers for multiple services. In this document
+we will use regular dialup and dialup ISDN as examples of two
+different services using the same radius server for authentication.
+
+OVERVIEW
+--------
+
+The radius servers are to be provisioned by a some sort of system we
+will call Billing. Billing could simply be a script, a web front-end,
+or an actual integration into a billing system. Billing will provision
+to the master LDAP server. The master LDAP server is running slurpd,
+which will replicate all changes to the other radius servers. Each
+radius server will run a local instance of LDAP.
+
+The radius servers will be accepting Radius auth packets and Radius
+acct packets. The accounting packets will be stored locally on each
+radius server and then forwarded to the Accounting radius server,
+using radrelay. The Accounting radius server will store all the
+radius information in some sort of database such as MySQL, Postgres,
+or Oracle. The configuration of the actual Accounting radius server
+is outside the scope of this document. Please refer to the freeradius
+documentation for setting up that server.
+
+The Accounting radius server will help to provide a searchable
+interface to the accounting data for billing and usage purposes and
+could allow a web front-end to be built for helpdesk/customer service
+usage. If that is not needed for your purposes, then disregard all
+details about the Accounting radius server.
+
+In order to make sure no data is lost in the event of the Accounting
+radius server going down, the replication of data will take place
+using radrelay. Radrelay will do the equivalent of a tail on the
+detail file and will continually attempt to duplicate each radius
+packet that is stored in the detail file and send it off to the
+recipient(s) specified. Upon receipt of an accounting_response packet
+radrelay will consider that packet completed and continue working on
+the others. Each radius server will also be storing its own copy of
+all accounting packets that are sent to it.
+
+Each NAS will be setup with a primary radius server and a failover
+radius server. We will spread the load among the group of radius
+servers that we have so some are acting as a primary to some NAS's and
+acting as a secondary to others. In the event of a radius failure,
+the NAS should failover to the backup radius server. How to configure
+this is dependent on the particular NAS being used.
+
+::
+
+ Will use Radius acct data Billing will provision
+ for real-time billing out to the Master LDAP
+ server over LDAP
+ +------------+
+ | Accounting | +---------+
+ | Radius | | Billing |
+ +------------+ +----+----+
+ /|\ |
+ | |
+ | |
+ | |
+ | Provisioning
+ | Message
+ | |
+ Duplicate |
+ Acct |
+ | |
+ | \|/
+ | +------------+
+ | +------------------| LDAP Master|
+ | | +------------+
+ | | |
+ | Slurpd Slurpd Replication
+ | Replication |
+ | | |
+ | | \|/
+ | | +------------+
+ | | | Radius2 |
+ The Radius servers | | | LDAP Slave |
+ will create a local | \|/ +------------+
+ copy of all acct +-------------+
+ packets and then | Radius1 |
+ fwd a copy back | LDAP Slave | All Radius servers run a
+ to accounting +-------------+ local copy of LDAP for
+ /|\ /|\ Authorization and Authentication
+ | |
+ | |
+ | |
+ | |
+ Auth Acct
+ | |
+ | |
+ | |
+ | |
+ | |
+ \|/ \|/
+ +-----------+
+ | |
+ | |
+ | NAS |
+ | |
+ +-----------+
+ The NAS will be setup to
+ use one of the Radius servers
+ as primary and the others as failover
+
+
+LDAP
+----
+
+The LDAP directory is designed to start with the top level of
+dc=mydomain,dc=com. The next level of the tree contains the different
+services that will be stored within the ldap server. For the radius
+users, it will be specified as ou=radius. Below ou=radius, will be
+the different types of accounts. For example, ou=users will store the
+users and ou=profiles will store the default radius profiles. The
+profiles are entries that will be used to store group-wide radius
+profiles. The group ou=admins will be a place to enter the users for
+Billing, Freeradius, and any other administrative accounts that are
+needed.
+
+::
+
+ +---------------------+
+ | |
+ | Dc=mydomain,dc=com |Objectclass:organizationalUnit
+ | |Objectclass:dcObject
+ +---------------------+
+ |
+ |
+ \|/
+ +---------------+
+ | |
+ | Ou=radius | Objectclass:organizationalUnit
+ | |
+ +---------------+
+ |
+ +-----------------------+-------------------------|
+ | | |
+ \|/ \|/ \|/
+ +---------+ +---------------+ +-------------+
+ | | | | | |
+ |Ou=users | | Ou=profiles | | Ou=admins |
+ | | | | | |
+ +---------+ +---------------+ +------|------+
+ | | |
+ | | |
+ \|/ | \|/
+ ----- Objectclass: | ----- Objectclass:
+ // \\ radiusprofile | // \\ person
+ | | | | |
+ \\ // | \\ //
+ ----- \|/ ----- Dn:cn=freeradius
+ Dn: uid=example,ou=users, ----- ObjectClass: ou=admins,ou=radius
+ dc=mydomain,dc=com // \\ radiusprofile dc=mydomain,dc=com
+ | |
+ | |
+ \\ //
+ -----
+ Dn: uid=dial,ou=profiles,ou=radius,dc=mydomain,dc=com
+
+
+An example LDIF file is below.
+NOTE: There are unique radius attribute types and objectclasses, these will be
+explained in the configuration section.
+
+::
+
+ dn: dc=mydomain,dc=com
+ objectClass: dcObject
+ objectClass: organizationUnit
+ ou: Mydomain.com Radius
+ dc: mydomain
+
+ dn: ou=radius,dc=mydomain,dc=com
+ objectclass: organizationalunit
+ ou: radius
+
+ dn: ou=profiles,ou=radius,dc=mydomain,dc=com
+ objectclass: organizationalunit
+ ou: profiles
+
+ dn: ou=users,ou=radius,dc=mydomain,dc=com
+ objectclass: organizationalunit
+ ou: users
+
+ dn: ou=admins,ou=radius,dc=mydomain,dc=com
+ objectclass: organizationalunit
+ ou: admins
+
+ dn: uid=dial,ou=profiles,ou=radius,dc=mydomain,dc=com
+ objectclass: radiusprofile
+ uid: dial
+ radiusServiceType: Framed-User
+ radiusFramedProtocol: PPP
+ radiusFramedIPNetmask: 255.255.255.0
+ radiusFramedRouting: None
+
+ dn: uid=isdn,ou=profiles,ou=radius,dc=mydomain,dc=com
+ objectclass: radiusprofile
+ uid: isdn
+ radiusServiceType: Framed-User
+ radiusFramedProtocol: PPP
+ radiusFramedIPNetmask: 255.255.255.0
+ radiusFramedRouting: None
+
+ dn: uid=example,ou=users,ou=radius,dc=mydomain,dc=com
+ objectclass: radiusProfile
+ uid: example
+ userPassword: test
+ radiusGroupName: dial
+ radiusGroupName: isdn
+
+ dn: cn=freeradius,ou=admins,ou=radius,dc=mydomain,dc=com
+ objectclass: person
+ sn: freeradius
+ cn: freeradius
+ userPassword: freeradius
+
+ dn: cn=billing,ou=admins,ou=radius,dc=mydomain,dc=com
+ objectclass: person
+ sn: freeradius
+ cn: freeradius
+ userPassword: billing
+
+ dn: cn=replica,ou=admins,ou=radius,dc=mydomain,dc=com
+ objectclass: person
+ sn: replica
+ cn: replica
+ userPassword: replica
+
+In order to configure the ldap server to understand the radius schema that we
+are using, the attribute types and objectclasses must be defined in slapd.conf.
+The file is included with the following line in slapd.conf::
+
+ include /usr/local/etc/openldap/schema/RADIUS-LDAPv3.schema
+
+Below is the complete Schema::
+
+ ----Begin RADIUS-LDAPv3.schema----
+
+ #################################################
+ ##### custom radius attributes ##################
+
+ objectIdentifier myOID 1.1
+ objectIdentifier mySNMP myOID:1
+ objectIdentifier myLDAP myOID:2
+ objectIdentifier myRadiusFlag myLDAP:1
+ objectIdentifier myObjectClass myLDAP:2
+
+ attributetype
+ ( myRadiusFlag:1
+ NAME 'radiusAscendRouteIP'
+ DESC 'Ascend VSA Route IP'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ (myRadiusFlag:2
+ NAME 'radiusAscendIdleLimit'
+ DESC 'Ascend VSA Idle Limit'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ (myRadiusFlag:3
+ NAME 'radiusAscendLinkCompression'
+ DESC 'Ascend VSA Link Compression'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ (myRadiusFlag:4
+ NAME 'radiusAscendAssignIPPool'
+ DESC 'Ascend VSA AssignIPPool'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+
+ attributetype
+ (myRadiusFlag:5
+ NAME 'radiusAscendMetric'
+ DESC 'Ascend VSA Metric'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ #################################################
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.1
+ NAME 'radiusArapFeatures'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.2
+ NAME 'radiusArapSecurity'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.3
+ NAME 'radiusArapZoneAccess'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.44
+ NAME 'radiusAuthType'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.4
+ NAME 'radiusCallbackId'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.5
+ NAME 'radiusCallbackNumber'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.6
+ NAME 'radiusCalledStationId'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.7
+ NAME 'radiusCallingStationId'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.8
+ NAME 'radiusClass'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.45
+ NAME 'radiusClientIPAddress'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.9
+ NAME 'radiusFilterId'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.10
+ NAME 'radiusFramedAppleTalkLink'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.11
+ NAME 'radiusFramedAppleTalkNetwork'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.12
+ NAME 'radiusFramedAppleTalkZone'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.13
+ NAME 'radiusFramedCompression'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.14
+ NAME 'radiusFramedIPAddress'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.15
+ NAME 'radiusFramedIPNetmask'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.16
+ NAME 'radiusFramedIPXNetwork'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.17
+ NAME 'radiusFramedMTU'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.18
+ NAME 'radiusFramedProtocol'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.19
+ NAME 'radiusFramedRoute'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.20
+ NAME 'radiusFramedRouting'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.46
+ NAME 'radiusGroupName'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.47
+ NAME 'radiusHint'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.48
+ NAME 'radiusHuntgroupName'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.21
+ NAME 'radiusIdleTimeout'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.22
+ NAME 'radiusLoginIPHost'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.23
+ NAME 'radiusLoginLATGroup'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.24
+ NAME 'radiusLoginLATNode'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.25
+ NAME 'radiusLoginLATPort'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.26
+ NAME 'radiusLoginLATService'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.27
+ NAME 'radiusLoginService'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.28
+ NAME 'radiusLoginTCPPort'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.29
+ NAME 'radiusPasswordRetry'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.30
+ NAME 'radiusPortLimit'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.49
+ NAME 'radiusProfileDn'
+ DESC ''
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.31
+ NAME 'radiusPrompt'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.50
+ NAME 'radiusProxyToRealm'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.51
+ NAME 'radiusReplicateToRealm'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.52
+ NAME 'radiusRealm'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.32
+ NAME 'radiusServiceType'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.33
+ NAME 'radiusSessionTimeout'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.34
+ NAME 'radiusTerminationAction'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.35
+ NAME 'radiusTunnelAssignmentId'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.36
+ NAME 'radiusTunnelMediumType'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.37
+ NAME 'radiusTunnelPassword'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.38
+ NAME 'radiusTunnelPreference'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.39
+ NAME 'radiusTunnelPrivateGroupId'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.40
+ NAME 'radiusTunnelServerEndpoint'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.41
+ NAME 'radiusTunnelType'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.42
+ NAME 'radiusVSA'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.43
+ NAME 'radiusTunnelClientEndpoint'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+
+ #need to change asn1.id
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.53
+ NAME 'radiusSimultaneousUse'
+ DESC ''
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.54
+ NAME 'radiusLoginTime'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.55
+ NAME 'radiusUserCategory'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.56
+ NAME 'radiusStripUserName'
+ DESC ''
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.57
+ NAME 'dialupAccess'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.58
+ NAME 'radiusExpiration'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.59
+ NAME 'radiusCheckItem'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+ attributetype
+ ( 1.3.6.1.4.1.3317.4.3.1.60
+ NAME 'radiusReplyItem'
+ DESC ''
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+
+ objectclass
+ ( 1.3.6.1.4.1.3317.4.3.2.1
+ NAME 'radiusprofile'
+ SUP top STRUCTURAL
+ DESC ''
+ MUST ( uid )
+ MAY ( userPassword $
+ radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $
+ radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $
+ radiusCalledStationId $ radiusCallingStationId $ radiusClass $
+ radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $
+ radiusFramedAppleTalkNetwork $ radiusFramedAppleTalkZone $
+ radiusFramedCompression $ radiusFramedIPAddress $
+ radiusFramedIPNetmask $ radiusFramedIPXNetwork $
+ radiusFramedMTU $ radiusFramedProtocol $
+ radiusCheckItem $ radiusReplyItem $
+ radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $
+ radiusGroupName $ radiusHint $ radiusHuntgroupName $
+ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $
+ radiusLoginLATPort $ radiusLoginLATService $ radiusLoginService $
+ radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $
+ radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $
+ radiusRealm $ radiusReplicateToRealm $ radiusServiceType $
+ radiusSessionTimeout $ radiusStripUserName $
+ radiusTerminationAction $ radiusTunnelAssignmentId $
+ radiusTunnelClientEndpoint $ radiusIdleTimeout $
+ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $
+ radiusLoginLATPort $ radiusLoginLATService $ radiusLoginService $
+ radiusLoginTCPPort $ radiusPasswordRetry $ radiusPortLimit $
+ radiusPrompt $ radiusProfileDn $ radiusServiceType $
+ radiusSessionTimeout $ radiusSimultaneousUse $
+ radiusTerminationAction $ radiusTunnelAssignmentId $
+ radiusTunnelClientEndpoint $ radiusTunnelMediumType $
+ radiusTunnelPassword $ radiusTunnelPreference $
+ radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $
+ radiusTunnelType $ radiusUserCategory $ radiusVSA $
+ radiusExpiration $ dialupAccess $
+ radiusAscendRouteIP $ radiusAscendIdleLimit $
+ radiusAscendLinkCompression $
+ radiusAscendAssignIPPool $ radiusAscendMetric )
+ )
+ ----End RADIUS-LDAPv3.schema----
+
+
+Now we need to setup the permissions on the ldap server. Notice above we
+created three users in the admin ou. These users will be specific for billing,
+freeradius, and replication.
+
+On the master ldap server, we will set the following permissions::
+
+ access to attr=userPassword
+ by self write
+ by dn="cn=billing,ou=admins,ou=radius,dc=mydomain,dc=com" write
+ by anonymous auth
+ by * none
+
+ access to *
+ by self write
+ by dn="cn=billing,ou=admins,ou=radius,dc=mydomain,dc=com" write
+ by anonymous auth
+ by * none
+
+This will give the billing user write access to add/delete users. For security
+we will not give read access to any other users. You can easily add another
+read-only user to this setup if you want to build some sort of web interface to
+do only reads.
+
+Now on the slave ldap servers (aka the radius servers) we will setup the
+following permissions::
+
+ access to attr=userPassword
+ by self write
+ by dn="cn=replica,ou=admins,ou=radius,dc=mydomain,dc=com" write
+ by anonymous auth
+ by * none
+
+ access to dn="ou=users,ou=radius,dc=mydomain,dc=com"
+ by dn="cn=replica,ou=admins,ou=radius,dc=mydomain,dc=com" write
+ by dn="cn=freeradius,ou=admins,ou=radius,dc=mydomain,dc=com" read
+ by anonymous auth
+ by * none
+
+ access to *
+ by self write
+ by dn="cn=replica,ou=admins,ou=radius,dc=mydomain,dc=com" write
+ by anonymous auth
+ by * none
+
+
+This will give the replica user write access. This user will be discussed
+below and it is involved in the process of replicating the master server to the
+slaves. The freeradius user only needs read access to do the lookups for
+authorization.
+
+Now we will want to setup indexes to speed up searches. At the minimum, below
+will work. Since all radius lookups are currently using the uid, we will want
+to index that. It is also a good idea to index the objectclass attribute.
+
+# Indices to maintain
+index objectClass eq
+index uid eq
+
+Now we need to setup the replication from the master to the slave servers. To
+do this, we will add the following to the slapd.conf file on the master:
+
+On the master LDAP server::
+ replica host=radius1.mydomain.com
+ binddn=cn=replica,ou=admins,ou=radius,dc=mydomain,dc=com
+ bindmethod=simple credentials=replica
+
+ replica host=radius2.mydomain.com
+ binddn=cn=replica,ou=admins,ou=radius,dc=mydomain,dc=com
+ bindmethod=simple credentials=replica
+
+We will need to add a replica for each slave LDAP server. The binddn is the
+name that is used to bind to the slave server, and the credentials is the
+secret for that user.
+
+On the slave LDAP servers::
+
+ updatedn cn=replica,ou=admins,ou=radius,dc=mydomain,dc=com
+ updateref ldap://ldapmaster.mydomain.com
+
+Those will determine what name is allowed to update the LDAP server and if an
+update is attempted directly, what server to refer the update to.
+
+RADIUS
+------
+
+The radius server is setup to use LDAP for both Authorization and
+Authentication. This section will describe what events will take place during
+an AAA session with a NAS. When the NAS sends a access_request to the radius
+server, the radius server will perform authorization and authentication based
+on a series of modules that are defined in radiusd.conf. For example, the
+module defined as ldap, will be used to make connections to the LDAP directory.
+
+An example is seen in raddb/mods-config/ldap::
+
+The first thing that is done is authorization of the user. The radius server
+will process the modules in the order specified in the authorization section of
+radiusd.conf. Currently, they are in the following order.
+
+1) preprocess
+2) suffix
+3) files
+4) ldap
+
+The first module will be preprocess. This will first check the huntgroups of
+the user coming in. The huntgroups are defined in the file huntgroups and they
+are a group listing of the NAS-IP-Addresses that make the access_request. This
+is useful in creating specific actions based on the NAS-IP that the request is
+made from. An example, is below::
+
+ isdncombo NAS-IP-Address == 10.10.10.1
+ dialup NAS-IP-Address == 10.10.10.2
+ dialup NAS-IP-Address == 10.10.10.3
+
+We will have one NAS that is used for both ISDN and regular dialup customers,
+the other NAS's will be only used for dialup.
+
+The preprocess module may also use the hints file, to load hints to the radius
+server, and add additional hacks that are based on the type of request that
+comes in. This is to help with certain NAS's that don't conform to radius
+RFC's. Check the comments in radiusd.conf for an explanation on those.
+
+The second module is suffix. This event will determine which realm the user is
+in, based on the User-Name attribute. It is currently setup to split the
+username at the occurence of the @symbol. For example, the username of
+example@mydomain.com, will be split into example and mydomain.com. The realm
+is then checked against the file proxy.conf, which will determine what actions
+should be taken for that realm. Certain realms can be setup to be proxied to a
+different radius server or set to authenticate locally. Also, the username can
+be setup to be stripped from the realm or left intact. An example of
+proxy.conf, is listed below. If the realm is to be proxied, then a secret is
+needed, which is the secret of the radius server it is to be proxied to.
+By default the User-Name will be stripped, unless the nostrip option is set.
+
+Currently we will not be using realms with our users, but adding this ability
+in the future will be much easier with already incorporating proxy.conf into the
+setup::
+
+ proxy server {
+ synchronous = no
+ retry_delay = 5
+ retry_count = 3
+ dead_time = 120
+ servers_per_realm = 15
+ default_fallback = yes
+ }
+
+ realm NULL {
+ type = radius
+ authhost = LOCAL
+ accthost = LOCAL
+ #secret = testing123
+ }
+
+ realm DEFAULT {
+ type = radius
+ authhost = LOCAL
+ accthost = LOCAL
+ #secret = testing123
+ }
+
+The next module is files, which is commonly know as the users file. The users
+file will start with either a username to determine how to authorize a specific
+user, or a DEFAULT setting. In each line it will define what items must be
+present for there to be a match in the form of attribute == value. If all the
+required attributes are matched, then attributes specified with attribute :=
+value will be set for that user. If no match is found the users file will
+continue to be processed until there is a match. The last DEFAULT setting will
+be set as a catch-all, in case there is no previous match. If a match is made,
+the statement of Fall-Through determines if the users file should continue to
+be processed or if it should stop right there.
+
+The Ldap-Group corresponds to the LDAP attribute of radiusGroupName (see ldap
+configuration above). The user may be assigned multiple radiusGroupNames, one
+for each of the services that the user is authorized for. If the user does
+belong to the correct group, then the user will be authorized for that type of
+access. If the user does not belong to that group, then there will not be a
+match and the users file will continue to be processed. If a match is made and
+there is a User-Profile set, then the radius server will lookup the attributes
+that exist in that User-Profile in the LDAP directory. These are radius
+attributes that will be sent to the NAS as a reply-item.
+
+An example users file is below::
+
+ DEFAULT Ldap-Group == disabled, Auth-Type := Reject
+ Reply-Message = "Account disabled. Please call the helpdesk."
+
+ DEFAULT Huntgroup-Name == isdncombo, NAS-Port-Type == Async, Ldap-Group == dial,
+ User-Profile := "uid=dial,ou=profiles,ou=radius,dc=mydomain,dc=com"
+ Fall-Through = no
+
+ DEFAULT Huntgroup-Name == isdncombo, NAS-Port-Type == ISDN, Ldap-Group == isdn,
+ User-Profile := "uid=isdn,ou=profiles,ou=radius,dc=mydomain,dc=com"
+ Fall-Through = no
+
+ DEFAULT Huntgroup-Name == dial, Ldap-Group == dial,
+ User-Profile := "uid=dial,ou=profiles,ou=radius,dc=mydomain,dc=com"
+ Fall-Through = no
+
+ DEFAULT Auth-Type := Reject
+ Reply-Message = "Please call the helpdesk."
+
+Notice that the catchall DEFAULT is set to Reject the user. This will stop the
+authorization and immediately send back an access_reject message. Because
+business rules are applied above to each scenario where the user will be
+authorized for access, if no match is found, then we will want to stop the
+process immediately to save resources.
+
+By using the Ldap-Group feature we can limit user logins to only the services
+they are subscribed to. Some examples of possible user setups are below::
+
+ #user with access to dial-up
+ dn: uid=user1,ou=users,ou=radius,dc=mydomain,dc=com
+ objectclass: radiusprofile
+ uid: user1
+ userPassword: whatever
+ radiusgroupname: dial
+
+ #user with access to ISDN and dial
+ dn: uid=user2,ou=users,ou=radius,dc=mydomain,dc=com
+ objectclass: radiusprofile
+ uid: user2
+ userPassword: whatever
+ radiusgroupname: dial
+ radiusgroupname: isdn
+
+ #same user as above that was suspended for not paying
+ dn: uid=user2,ou=users,ou=radius,dc=mydomain,dc=com
+ objectclass: radiusprofile
+ uid: user2
+ userPassword: whatever
+ radiusgroupname: dial
+ radiusgroupname: isdn
+ radiusgroupname: disabled
+
+Now that we have authorized the user, the final piece is to authenticate the
+user. Authentication is currently done by checking if the password sent in the
+access_request packet is correct. This action will be done with an attempted
+bind to the LDAP server using the User-Name and User-Password attributes
+passed to it from the access_request. If the user is successfully authorized,
+then an access_accept message will be sent back to the NAS, with any reply
+items that were defined in the authorization section. If the user did not
+supply the correct password, then an access_reject message will be sent to the
+user.
+
+If the NAS is sent an access_accept packet then the user will be given access
+to the service and the NAS will then send an acct_request packet. This will be
+a request packet to start a radius accounting session. The way the server will
+log the accounting packets is determined in the detail module in the
+radiusd.conf file. Since we will be storing a local copy and forwarding on all
+accounting to the Accounting radius server, we will store two local copies on
+the machine. The first one is done in a regular detail file as defined in the
+following::
+
+ detail detail1 {
+ filename = ${radacctdir}/%{Client-IP-Address}/detail-%Y%m%d
+ permissions = 0600
+ dir_permissions = 0755
+ }
+
+The second detail file will be used by the program radrelay to relay a copy of
+all accounting packets to the Accounting radius server. This file is stored as
+a catchall for all accounting packets. The radrelay program will basically do
+a tail on that file and will then attempt to send a copy of each addition to it
+to the Accounting server. If the copy is successfully sent, then it will be
+deleted from this file. If the Accounting server were to go down, then this
+file will continue to build up entries. As soon as the Accounting server is
+back online, an attempt to re-send the packets to the Accounting server will
+made. This file is defined in the following section of radiusd.conf::
+
+ detail detail2 {
+ filename = ${radacctdir}/detail-combined
+ permissions = 0600
+ dir_permissions = 0755
+ locking = yes
+ }
+
+INSTALLATION
+------------
+
+The new radius servers are currently built on Freebsd 4.8. As the version may
+eventually change, these instructions may no longer apply. The steps for
+building the server are the following:
+
+* Install FreeBSD
+* Install other FreeBSD items
+* Install OpenLDAP *NOTE: this must be done before installing Freeradius*
+* Install FreeRadius
+
+Under the assumption that FreeBSD is already installed and the kernel rebuilt
+to the specifications needed for the machine, there are several other things
+that may be needed at this time and the purpose of this is just as a reminder.
+
+install cvsup-without-gui from the ports collection
+
+run cvsup on all to update the ports to the most recent versions
+
+might be a good idea to upgrade the src
+
+edit and run cvsup on /usr/share/examples/cvsup/standard-supfile
+
+cd /usr/src - vi Makefile and follow instructions
+
+install sendmail from ports to keep up to date with the most recent versions.
+In the ports collection /ports/mail/sendmail run make; make install; make
+mailer.conf. Then edit rc.conf and change to sendmail_enable=NO
+radius servers only need the local interface to send daily reports
+
+edit rc.conf to make sure inetd_enable=NO
+
+no reason to have extra services running
+
+if you rebuilt the kernel to add support for IPFIREWALL, then remember to add a
+firewall rule to rc.conf
+
+firewall_enable=YES
+firewall_type=OPEN (or actually create a real firewall rule)
+
+add crontab to keep date accurate for accounting::
+
+ 15 03 * * * /usr/sbin/ntpdate -s thetimeserver.mydomain.com
+
+install openldap from ports
+
+download the freeradius source as the ports collection is often outdated
+the default settings are /usr/local/etc/raddb, /var/log/radius.log, /var/log/radacct
+
+since openldap was installed first, you should not need any special flags to
+add ldap support
+
+Now its time to configure openlap and freeradius. First we will be looking at
+configuring OpenLDAP
+
+
+copy RADIUS-LDAPv3.schema to /usr/local/etc/openldap/schema
+
+edit /usr/local/etc/openldap/slapd.conf
+
+::
+
+ ----Begin slapd.conf----
+ # $OpenLDAP: pkg/ldap/servers/slapd/slapd.conf,v 1.23.2.7 2003/03/24 03:54:12
+ #kurt Exp $
+ #
+ # See slapd.conf(5) for details on configuration options.
+ # This file should NOT be world readable.
+ #
+ include /usr/local/etc/openldap/schema/core.schema
+ include /usr/local/etc/openldap/schema/RADIUS-LDAPv3.schema
+
+ # Define global ACLs to disable default read access.
+
+ # Do not enable referrals until AFTER you have a working directory
+ # service AND an understanding of referrals.
+ #referral ldap://root.openldap.org
+
+ loglevel 296
+
+ pidfile /var/run/slapd.pid
+ argsfile /var/run/slapd.args
+
+ # Load dynamic backend modules:
+ # modulepath /usr/local/libexec/openldap
+ # moduleload back_bdb.la
+ # moduleload back_ldap.la
+ # moduleload back_ldbm.la
+ # moduleload back_passwd.la
+ # moduleload back_shell.la
+
+ password-hash {SSHA}
+
+ access to attr=userPassword
+ by self write
+ by dn="cn=replica,ou=admins,ou=radius,dc=mydomain,dc=com" write
+ by anonymous auth
+ by * none
+
+ access to dn="ou=users,ou=radius,dc=mydomain,dc=com"
+ by dn="cn=replica,ou=admins,ou=radius,dc=mydomain,dc=com" write
+ by dn="cn=freeradius,ou=admins,ou=radius,dc=mydomain,dc=com" read
+ by anonymous auth
+ by * none
+
+ access to *
+ by self write
+ by dn="cn=replica,ou=admins,ou=radius,dc=mydomain,dc=com" write
+ by anonymous auth
+ by * none
+
+
+ #######################################################################
+ # ldbm database definitions
+ #######################################################################
+
+ database bdb
+ suffix "dc=mydomain,dc=com"
+ rootdn "cn=root,dc=mydomain,dc=com"
+ # Cleartext passwords, especially for the rootdn, should
+ # be avoid. See slappasswd(8) and slapd.conf(5) for details.
+ # Use of strong authentication encouraged.
+ rootpw {SSHA}Eu5EwPxTrwhEGrXQ9SaQZyfpu4iHt3NP
+ # The database directory MUST exist prior to running slapd AND
+ # should only be accessible by the slapd and slap tools.
+ # Mode 700 recommended.
+ directory /var/db/openldap-data
+ # Indices to maintain
+ index objectClass eq
+ index uid eq
+ mode 0600
+ cachesize 2000
+
+ # replica one for each
+ #replica host=radius1.mydomain.com
+ # binddn="cn=replica,ou=admins,ou=radius,dc=mydomain,dc=com"
+ # bindmethod=simple credentials=secret
+
+ replogfile /var/db/openldap-slurp/replog
+
+ ## REMEMBER TO ADD THIS TO THE SLAVES
+ updatedn "cn=freeradius,ou=admins,ou=radius,dc=mydomain,dc=com"
+ updateref ldap://ldapmaster.mydomain.com
+ ----End slapd.conf----
+
+
+To create a rootdn that is not stored in plain text, enter the following command::
+
+ $ slappasswd
+
+it will ask for password and verification::
+
+ New password:
+ Re-enter new password::
+
+while in the shell create the directory for the ldap database, this must be created before slapd can start::
+
+ $ mkdir /var/db/openldap-data
+
+move the slapd.sh.sample file to slapd.sh in /usr/local/etc/rc.d::
+
+ $ mv /usr/local/etc/rc.d/slapd.sh.sample slapd.sh
+
+enable logging in /etc/syslog.conf by adding the following::
+
+ local4.* /var/log/ldap.log
+ restart syslogd
+
+start it up on both the master and slave ldap servers::
+
+ $ /usr/local/etc/rc.d/slapd start
+
+create the structural ldif, schema.ldif::
+
+ ----Begin schema.ldif----
+ dn: dc=mydomain,dc=com
+ objectClass: dcObject
+ objectClass: organizationUnit
+ ou: Mydomain.com Radius
+ dc: mydomain
+
+ dn: ou=radius,dc=mydomain,dc=com
+ objectclass: organizationalunit
+ ou: radius
+
+ dn: ou=profiles,ou=radius,dc=mydomain,dc=com
+ objectclass: organizationalunit
+ ou: profiles
+
+ dn: ou=users,ou=radius,dc=mydomain,dc=com
+ objectclass: organizationalunit
+ ou: users
+
+ dn: ou=admins,ou=radius,dc=mydomain,dc=com
+ objectclass: organizationalunit
+ ou: admins
+
+ dn: uid=dial,ou=profiles,ou=radius,dc=mydomain,dc=com
+ objectclass: radiusprofile
+ uid: dial
+ radiusServiceType: Framed-User
+ radiusFramedProtocol: PPP
+ radiusFramedIPNetmask: 255.255.255.0
+ radiusFramedRouting: None
+
+ dn: uid=isdn,ou=profiles,ou=radius,dc=mydomain,dc=com
+ objectclass: radiusprofile
+ uid: isdn
+ radiusServiceType: Framed-User
+ radiusFramedProtocol: PPP
+ radiusFramedIPNetmask: 255.255.255.0
+ radiusFramedRouting: None
+
+ dn: uid=example,ou=users,ou=radius,dc=mydomain,dc=com
+ objectclass: radiusProfile
+ uid: example
+ userPassword: test
+ radiusGroupName: dial
+ radiusGroupName: isdn
+
+ dn: cn=freeradius,ou=admins,ou=radius,dc=mydomain,dc=com
+ objectclass: person
+ sn: freeradius
+ cn: freeradius
+ userPassword: freeradius
+
+ dn: cn=billing,ou=admins,ou=radius,dc=mydomain,dc=com
+ objectclass: person
+ sn: freeradius
+ cn: freeradius
+ userPassword: billing
+
+ dn: cn=replica,ou=admins,ou=radius,dc=mydomain,dc=com
+ objectclass: person
+ sn: replica
+ cn: replica
+ userPassword: replica
+ ----End schema.ldif----
+
+add the organizational structure to the master ldap database::
+
+ $ ldapadd -D uid=billing,ou=admins,ou=radius,dc=mydomain,dc=com -w billing -f
+ schema.ldif -h ldapmaster.mydomain.com
+
+run slapcat to see what the directory looks like::
+
+ $ slapcat
+
+If all went well the LDAP directory should be up and running and propagated to
+the slaves. Now you can add your users to the master.
+
+Now its time to setup FreeRadius. First cd into /usr/local/etc/raddb and take
+a look at all the configuration files, they are heavily documented so you may
+wish to read through them all before making and changes.
+
+
+edit huntgroups to specify a NAS to a huntgroup::
+
+ ----Begin huntgroups----
+ # dialup and isdn
+ isdncombo NAS-IP-Address == 10.10.10.1
+
+ # just dialup
+ dialup NAS-IP-Address == 10.10.10.2
+ dialup NAS-IP-Address == 10.10.10.3
+ ----End huntgroups----
+
+* edit proxy.conf to setup the different realms::
+
+ ----Begin proxy.conf----
+ proxy server {
+ synchronous = no
+ retry_delay = 5
+ retry_count = 3
+ dead_time = 120
+ servers_per_realm = 15
+ default_fallback = yes
+ }
+
+ realm NULL {
+ type = radius
+ authhost = LOCAL
+ accthost = LOCAL
+ #secret = testing123
+ }
+
+ realm DEFAULT {
+ type = radius
+ authhost = LOCAL
+ accthost = LOCAL
+ #secret = testing123
+ }
+ ----End proxy.conf----
+
+ -edit clients.conf to setup the NAS's that can talk to it
+
+
+ ----Begin clients.conf----
+ client 127.0.0.1 {
+ secret = example
+ shortname = localhost
+ nas_type = other
+ }
+
+
+ # isdn and dialup nas
+ client 10.10.10.1 {
+ secret = example
+ shortname = isdn
+ nas_type = cisco
+ }
+
+ #dialup only
+ client 10.10.10.2 {
+ secret = example
+ shortname = dialup1
+ nas_type = cisco
+ }
+
+ client 10.10.10.3 {
+ secret = example
+ shortname = dialup2
+ nas_type = cisco
+ }
+ ----End clients.conf----
+
+
+You may wish to look at the other files, but they should all be OK by default.
+
+create startup files in /usr/local/etc/rc.d
+
+radiusd.sh - the radiusd startup file::
+
+ ----Begin radiusd.sh----
+ #!/bin/sh
+ case "$1" in
+ start)
+ /usr/local/sbin/radiusd
+ echo -n ' radiusd'
+ ;;
+ stop)
+ if [ -f /usr/local/var/run/radiusd/radiusd.pid ]; then
+ kill -TERM `cat /usr/local/var/run/radiusd/radiusd.pid`
+ rm -f /usr/local/var/run/radiusd/radiusd.pid
+ echo -n ' radiusd'
+ fi
+ ;;
+ restart)
+ if [ -f /usr/local/var/run/radiusd/radiusd.pid ]; then
+ kill -HUP `cat /usr/local/var/run/radiusd/radiusd.pid`
+ echo 'radiusd restarted'
+ fi
+ ;;
+ *)
+ echo "Usage: ${0##*/}: { start | stop | restart }" 2>&1
+ exit 65
+ ;;
+ esac
+ ----End radiusd.sh----
+
+radrelay.sh - the radrelay startup file::
+
+
+ ----Begin radrelay.sh----
+ #!/bin/sh
+ case "$1" in
+
+ start)
+ /usr/local/bin/radrelay -a /var/log/radacct -d /usr/local/etc/raddb \
+ -S /usr/local/etc/raddb/radrelay_secret -f -r accounting.mydomain.com:1813 \
+ detail-combined
+ echo -n ' radrelay started'
+ ;;
+
+
+ stop)
+ /usr/bin/killall radrelay
+ echo ' radrelay stopped'
+ ;;
+
+ *)
+ echo "Usage: $[0##*/}: { start | stop }" 2>&1
+ exit 65
+ ;;
+
+ esac
+ ----End radrelay.sh----
+
+create radrelay_secret in /usr/local/etc/radddb
+This file will contain the secret to connect to the Accounting radius server::
+
+ ----Begin radrelay_secret----
+ example
+ ----End radrelay_secret----
+
+Now fire them up::
+ $ /usr/local/etc/rc.d/radiusd.sh start
+ $ /usr/local/etc/rc.d/radrelay.sh start
+
+You should be all set to start testing now.
+
+OTHER RANDOM NOTES AND THOUGHTS
+-------------------------------
+
+The client programs used to connect to the ldap directory are:
+
+ldapadd:
+ to add a record
+ldapmodify:
+ to modify a record
+ldapdelete:
+ to delete a record
+ldapsearch:
+ to search for a record
+slapcat:
+ to show the entire directory
+slappaswd:
+ to generate a crypted password
+
+Read the man pages on those commands, they tell you everything you
+need to know.
+
+They all follow this basic syntax::
+
+ $ ldapwhatever -D "uid=someone,ou=admins,ou=radius,dc=mydomain,dc=com" -w thesecret -andthenotherstuff
+
+Finally, if you are having trouble with LDAP, run it in debug mode by
+changing the following in slapd.sh::
+
+ slapd_args=
+
+to::
+
+ slapd_args= '-d 3'
+
+There is a program included with freeradius to test the radius server,
+its called radclient. Typing it alone will tell you all the options.
+You will need to create a file that contains radius attributes, such
+as::
+
+ User-Name = example
+ User-Password = test
+ Service-Type = Framed-User
+ NAS-IP-Address = 10.10.10.1
+ NAS-Port-Type = Async
+
+Then you fire that radius packet at the server by issuing::
+
+ $ radclient -f testradiusfile localhost auth thesecret
+
+-f = filename
+localhost is the server you are hitting
+auth or acct depending on the type of packet
+thesecret to connect to that server
+
+Finally, if you are having trouble you can run radius in debug mode
+and it will output everything that happens to the screen. To do that,
+kill the current process and run::
+
+ $ radiusd -X
+
+
+LINKS
+-----
+
+FREERADIUS
+++++++++++
+
+* _`FreeRADIUS`: http://www.freeradius.org
+* _`FreeRADIUS Documentation`: http://freeradius.org/documentation/
+* _`FreeRADIUS Wiki`: http://wiki.freeradius.org/
+
+OPENLDAP
+++++++++
+
+* _`OpenLDAP`: http://www.openldap.org
+* _`OpenLDAP Administrator's Guide`: http://www.openldap.org/doc/admin21
+
+RFCs
+++++
+
+* _`RFC2865: RADIUS Authentication`: http://freeradius.org/rfc/rfc2865.txt
+* _`RFC2866: RADIUS Accounting`: http://freeradius.org/rfc/rfc2866.txt
+* _`RFC2869: RADIUS Extentions`: http://freeradius.org/rfc/rfc2869.txt
+* _`RFC2251: LDAP v3`: http://www.ietf.org/rfc/rfc2251.txt
+* _`RFC2252: LDAP v3 Attribute Syntax Definitions`: http://www.ietf.org/rfc/rfc2252.txt
+* _`RFC2253: LDAP UTF-8 String Representation of Distinguishe d Names (DNs)`: http://www.ietf.org/rfc/rfc2252.txt
+* _`RFC2849: LDAP Data Interchange Fromat (LDIFs)`: http://www.ietf.org/rfc/rfc2849.txt
+* _`RFC3377: LDAP v3 Technical Specs`: http://www.ietf.org/rfc/rfc3377.txt
diff --git a/doc/modules/mschap.rst b/doc/modules/mschap.rst
new file mode 100644
index 0000000..37fcb9d
--- /dev/null
+++ b/doc/modules/mschap.rst
@@ -0,0 +1,196 @@
+rlm_mschap
+==========
+
+The mschap module provides support for MS-CHAPv1 and MS-CHAPv2, which is
+a common authentication mechanisms for Microsoft clients.
+
+If you want to support mschap, there are only 3 possibilities:
+
+ 1. You have access to the users plaintext password, and you configure
+ FreeRADIUS to read this, and set the Cleartext-Password control attribute.
+
+ 2. You have access to the NT (MS-CHAPv2) or LM (MS-CHAPv1) hashes,
+ and you configure FreeRADIUS to read this and set the NT/LM-Password
+ control attribute.
+
+ 3. You have Samba installed, joined into a windows domain, and use
+ the ntlm_auth helper binary to pass authentication onwards to
+ a domain controller.
+
+These are the ONLY possibilities; MS-CHAP is IMPOSSIBLE if you e.g. only
+have the unix/md5/sha crypt of your users password.
+
+For more info, see:
+
+ http://deployingradius.com/documents/protocols/compatibility.html
+
+EAP-MSCHAPv2
+============
+
+The EAP module provides MS-CHAPv2 support as well. It simply passes the
+data through to the mschap module, so you must configure mschap properly.
+
+ntlm_auth
+=========
+
+Method 3 above involves configuring the mschap module to call the Samba
+ntlm_auth helper:
+
+::
+
+ mschap {
+ ntlm_auth = "/path/to/bin ..."
+ }
+
+You need to be careful about setting this command line. There are several
+options for the arguments, in particular username and domain:
+
+ * --username=%{User-Name} - this will fail if you're using realms or host-based auth
+ * --username=%{mschap:User-Name} - this will fail if using using suffix i.e. user@domain
+
+You'll need to fit this to your local needs.
+
+Disabling ntlm_auth for some users
+----------------------------------
+
+You might have some users in the domain, and others in files or SQL that you
+want to authenticate locally. To do this, set::
+
+ MS-CHAP-Use-NTLM-Auth := 0
+
+This will disable ntlm_auth for that user/group. This is also obeyed
+for password changes (see below).
+
+Password changes
+================
+
+From FreeRADIUS version 3.0.0 the mschap module supports password changes.
+
+There are two options, ntlm_auth and local.
+
+ntlm_auth
+---------
+
+If you are using ntlm_auth to check passwords, you must also use
+ntlm_auth to change passwords. In modules/mschap you should configure::
+
+ mschap {
+ ntlm_auth = "...."
+ passchange {
+
+ # path to the binary
+ ntlm_auth = "/path/to/ntlm_auth --helper-protocol=ntlm-change-password-1"
+
+ # initial data to send
+ # this MUST be supplied
+ ntlm_auth_username = "username: %{mschap:User-Name}"
+ ntlm_auth_domain = "nt-domain: %{%{mschap:NT-Domain}:-YOURDOMAIN}"
+
+ # Or, you could try:
+ ntlm_auth_username = "full-username: %{User-Name}"
+ # ntlm_auth_domain - disabled
+
+ }
+
+
+If you are using ntlm_auth, then domain controllers might say
+"Password expired" if the user password is valid but has expired; the
+mschap module will detect this and return error 648 to the client,
+instructing it to try a password change.
+
+Note: if you have disabled ntlm_auth for a user/group, this will apply
+for password changes too - they will fall through to using the Local
+method.
+
+Local
+-----
+
+If you are performing mschap locally with Cleartext-Password/NT-Password, you
+can decrypt and process the password change locally. To do this, you configure
+the "local_cpw" string::
+
+ mschap {
+ passchange {
+ local_cpw = "%{xlat:...}
+ }
+ }
+
+To actually force a client to change passwords, you must set the expiry bit
+in the SMB-Account-Ctrl value - for example::
+
+ update control {
+ # U == user
+ # e == expired
+ SMB-Account-Ctrl-Text := '[Ue]'
+ }
+
+This will cause the client to receive "error 648 - password
+expired". Obviously you will need to ensure your local_cpw xlat clears
+this value, or else the client password will be expired the next time
+they log in. For example, you might use an SQL stored procedure to
+change passwords::
+
+ mschap {
+ passchange {
+ local_cpw = "%{sql:select change_password('%{SQL-User-Name}','%{MS-CHAP-New-NT-Password}')}"
+ }
+ }
+
+...and an example stored procedure for Postgres might be::
+
+ CREATE FUNCTION change_password(raduser text, ntpassword text) RETURNS text
+ LANGUAGE plpgsql
+ AS $$
+ BEGIN
+ update radcheck set value=ntpassword where username=raduser and attribute='NT-Password';
+ if not FOUND then
+ -- the user does not exist; die
+ return '';
+ end if;
+ update radcheck set value=replace(value,'e','') where username=raduser and attribute='SMB-Account-Ctrl-Text' and value like '%e%';
+ return 'ok';
+ END;
+ $$;
+
+
+The local_cpw xlat has access to two variables:
+
+ * MS-CHAP-New-NT-Password - the new value of NT-Password
+ * MS-CHAP-New-Cleartext-PAssword - the new value of Cleartext-Password
+
+This allows you to do things like::
+
+ # update via SQL
+ local_cpw = "%{sql:update radcheck set value='%{MS-CHAP-New-NT-Password}' where username='%{SQL-User-Name} and attribute='NT-Password'}"
+
+Or::
+
+ # update via exec/script
+ local_cpw = "%{exec:/my/script %{User-Name} %{MS-CHAP-New-Cleartext-Password}}"
+
+WARNING - wherever possible, you should use
+MS-CHAP-New-NT-Password. The reason is that cleartext passwords have
+undergone unicode transformation from the client encoding (utf-16) to
+the server encoding (utf-8) and the current code does this in a very
+ad-hoc way. The reverse transformation is also not done - when the
+server reads Cleartext-Password out of files/database, it assumes
+US-ASCII and thus international characters will fail.
+
+N.B. this could be fixed, if we wanted to pull in something like iconv.
+
+In addition, you should beware of Cleartext-Password when using SQL;
+any password character not in safe_characters will be encoded as a hex
+number, e.g. =20.
+
+Password changes over EAP
+=========================
+
+You must set the following in eap.conf::
+
+ eap {
+ mschapv2 {
+ send_error = yes
+ }
+ }
+
+Otherwise password changes for PEAP/MSCHAPv2 will not work.
diff --git a/doc/modules/rlm_dbm b/doc/modules/rlm_dbm
new file mode 100644
index 0000000..8ace2ff
--- /dev/null
+++ b/doc/modules/rlm_dbm
@@ -0,0 +1,195 @@
+Radius DBM module
+
+0. INTRODUCTION
+
+ rlm_dbm uses a Berkeley or GDBM <** database to store use information. It
+ is a lot faster than the files and passwd modules, takes less memory than
+ the fastusers module and does not require additional server software as
+ the LDAP and SQL modules does. In addition it supports groups, and of
+ course multiple entries per user or group.
+
+1. WHAT DOES IT DO
+
+ Basically, it opens the file you specifies in radiusd.conf and authenticates
+ users out of it. The file has to be a Berkeley or GDBM <** file database,
+ and may be created by rlm_dbm_parse or by a custom program of your choice.
+
+2. HOW TO USE IT
+
+ Put the module declaration in your radiusd.conf. It should in general look
+ like this:
+
+ dbm {
+ usersfile = ${confdir}/users.db
+ }
+ Note: some dbm libraries add .db suffix by itself.
+
+ Then put "dbm" in the "authorize {}" section of your radiusd.conf:
+
+ authorize {
+ preprocess
+ realms
+ dbm
+ }
+
+3. MODULE OPTIONS
+
+ The only option is "usersfile", which is the path and filename of the
+ database file you want rlm_dbm to look for users and groups in. This
+ file needs to be generated, either by the rlm_dbm_parse program or by
+ some custom program, for instance a Perl program using the DB_File or
+ GDBM_File <** modules.
+
+4. EXTERNAL UTILITIES
+
+ rlm_dbm_cat
+
+ rlm_dbm_cat: [-f file] [-w] [-i number] [-l number] [-v] [username ...]
+
+ rlm_dbm_cat simply lists the definition(s) of the username(s) or group
+ name(s), or the entire database. It takes the following options:
+
+ -f <filename>
+
+ The file name of the database to list.
+
+ -w
+ Long lines should be wrapped
+
+ -i <number>
+Set the left margin then wrapped.
+ -l <number>
+How long line should be to be wrapped (wrap threshold)
+
+ -v
+
+ Print the version number and exit.
+
+ rlm_dbm_parse
+
+ rlm_dbm_parser [-c] [-d raddb] [-i inputfile] [-o outputfile] [-x]
+ [-v] [-q] [username ...]
+
+ rlm_dbm_parses reads a file of the syntax defined below, and writes
+ a database file usable by rlm_dbm or edits current database.
+ It takes the following options:
+
+ -i <file>
+
+ Use <file> as the input file. If not defined then use standard input.
+
+ -o
+
+ Use <file> as the output file.
+
+ -c
+
+ Create a new database (empty output file before writing)
+
+ -x
+
+ Enable debug mode.
+; Multiple x flag increase debug level
+
+ -q
+
+ Do not print statistics (quiet).
+
+ -v
+
+ Print the version and exit.
+
+ -r
+
+ Remove a username or group name from the database.
+
+5. INPUT FORMAT
+
+ rlm_dbm_parse reads a format similar to the one used by the files
+ module. In incomplete RFC2234 ABNF, it looks like this:
+
+ entries = *entry
+ entry = identifier TAB definition
+ identifier = username / group-name
+ username = +PCHAR
+ groupname = +PCHAR
+ definition = (check-item ",")* LF ( *( reply-item ",") / ";" ) LF
+ check-item = AS IN FILES
+ reply-item = AS IN FILES
+
+*** need definition of username and groupname ***
+
+ As an example, these are the standard files definitions (files module).
+
+---8<---
+ DEFAULT Service-Type == Framed-User
+ Framed-IP-Address = 255.255.255.254,
+ Framed-MTU = 576,
+ Service-Type = Framed-User,
+ Fall-Through = Yes
+
+#except who call from number 555-666
+ DEFAULT Auth-Type := Reject,Service-Type ==Framed-User,
+ Calling-Station-ID == "555-666"
+
+#or call number 555-667
+ DEFAULT Auth-Type := Reject,Service-Type ==Framed-User,
+ Calling-Station-ID == "555-667"
+---8<---
+
+ To be a valid rlm_dbm input file, it should look like this:
+
+---8<---
+ DEFAULT Service-Type == Framed-User # (1)
+ Framed-IP-Address = 255.255.255.254, # comma, list cont'd
+ Framed-MTU = 576,
+ Service-Type = Framed-User,
+ Fall-Through = Yes # \n, end of list
+ Auth-Type := Reject,Service-Type ==Framed-User, # (2)
+ Calling-Station-ID == "555-666"
+ ; # ;, no reply items
+ Auth-Type := Reject,Service-Type ==Framed-User, # (3)
+ Calling-Station-ID == "555-667"
+ ; # ditto
+---8<---
+
+ This user (the DEFAULT user) contains three entries, 1, 2 and 3. The
+ first entry has a list of reply items, terminated by a reply item
+ without a trailing comma. Entries 2 and 3 has empty reply lists, as
+ indicated by the semicolon. This is necessary to separate an empty
+ line (which is ignored) from the empty list.
+ Definition Fall-Through = Yes used in order to say module to check next
+ record. By default Fall-Through = Yes.
+
+ Groups
+
+ This is implemented with the special User-Category attribute. Simply
+ set this as a reply item, and rlm_dbm will include the groups definition
+ when evaluating the check and reply items of the user. The group defined
+ the same way as users. Here is a short example:
+
+---8<---
+# group definitions
+gendialup
+ Service-Type = Framed-User,
+ Cisco-AVPair += "ip:addr-pool=SANDY",
+ Framed-Protocol = PPP
+
+locked Auth-Type := Reject
+ Reply-Message = "Your account has been disabled."
+
+# user definitions
+ssalex Auth-Type := Local, Password == "passs"
+ User-Category = "GenDialup"
+
+ssmike Auth-Type := Local, Password == "pass1"
+ User-Category = "Locked"
+---8<---
+
+6. ACKNOWLEDGMENTS
+
+ Author - Andrei Koulik <rlm_dbm@agk.nnov.ru>
+ Documentation - Bjørn Nordbø <bn@nextra.com>
+8. Bug reports:
+ rlm_dbm_bug@agk.nnov.ru
+
diff --git a/doc/modules/rlm_eap b/doc/modules/rlm_eap
new file mode 100644
index 0000000..4f903af
--- /dev/null
+++ b/doc/modules/rlm_eap
@@ -0,0 +1,395 @@
+
+
+ Extensible Authentication Protocol (EAP)
+
+
+INTRODUCTION
+
+ Extensible Authentication Protocol(EAP), rfc2284, is a general protocol
+ that allows network access points to support multiple authentication
+ methods. Each EAP-Type indicates a specific authentication mechanism.
+ 802.1x standard authenticates wireless LAN users trying to access
+ enterprise networks.
+
+ RADIUS attribute used for EAP is EAP-Message, 79(rfc2869). RADIUS
+ communicates all EAP messages by embedding them in this attribute.
+
+ General Terminology
+ Supplicant/EAP Client - is the software on the end-user/client machine
+ (machine with the wireless card).
+ Authenticator/NAS/Access Point(AP) - A network device providing users
+ with a point of entry into the network.
+ EAPOL - EAP over LAN as defined in 802.1x standard.
+ EAPOW - EAP over Wireless.
+
+
+ +----------+ +----------+ +----------+
+ | | EAPOL | | RADIUS | |
+ | EAP |<------>| Access |<------>| RADIUS |
+ | Client | EAPOW | Point | (EAP) | Server |
+ | | | | | |
+ +----------+ +----------+ +----------+
+
+
+ The sequence of events, for EAP-MD5, runs as follows:
+ 1. The end-user associates with the Access Point(AP).
+ 2. The supplicant specifies AP to use EAP by sending EAP-Start.
+ 3. AP requests the supplicant to Identify itself (EAP-Identity).
+ 4. Supplicant then sends its Identity (username) to the AP.
+ 5. AP forwards this EAP-response AS-IS to the RADIUS server.
+ (The supplicant and the RADIUS server mutually authenticate via AP.
+ AP just acts as a passthru till authentication is finished.)
+ 6. The server sends a challenge to the supplicant.
+ 7. The supplicant carries out a hash on the password and sends
+ this hashed password to the RADIUS server as its response.
+ 8. The RADIUS server performs a hash on the password for that supplicant
+ in its user database and compares the two hashed values and
+ authenticates the client if the two values match(EAP-Success/EAP-Failure)
+ 9. AP now opens a port to accept data from the end-user.
+
+ Currently, EAP is widely used in wireless networks than in wired networks.
+ In 802.11/wireless based networking, following sequence of events happen in
+ addition to the above EAP events.
+
+ 10. RADIUS server and the supplicant agree to a specific WEP key.
+ 11. The supplicant loads the key ready for logging on.
+ 12. The RADIUS server sends the key for this session (Session key) to the AP.
+ 13. The AP encrypts its Broadcast key with the Session key
+ 14. The AP sends the encypted key to the supplicant
+ 15. The supplicant decrypts the Broadcast key with the Session key and
+ the session continues using the Broadcast and Session keys until
+ the session ends.
+
+ References:
+ The Implementation of EAP over RADIUS is based on the following RFCs
+ rfc2869 -- RADIUS Extensions
+ rfc2284 -- PPP Extensible Authentication Protocol (EAP)
+ rfc2716 -- PPP EAP TLS Authentication Protocol
+
+ Following links help to understand HOW EAP works
+ www.ieee802.org/1/mirror/8021/docs2000/ieee_plenary.PDF
+
+
+EAP CODE ORGANIZATION
+
+ EAP is implemented as a module in freeradius
+ and the code is placed in src/modules/rlm_eap.
+ All EAP-Types are organized as subdirectories in rlm_eap/types/.
+
+ Each EAP-Type, like types/rlm_eap_md5, contains a chunk of code that
+ knows how to deal with a particular kind of authentication mechanism.
+
+ To add a new EAP-Type then a new directory should be created as
+ rlm_eap/types/rlm_eap_XXXX, where XXXX is EAP-Type name
+ ie for EAP-Type like ONE TIME PASSWORD (OTP) it would be rlm_eap_otp
+
+ src/modules/rlm_eap -- contains the basic EAP and generalized interfaces
+ to all the EAP-Types.
+ rlm_eap/types -- contains all the supported EAP-Types
+ rlm_eap/types/rlm_eap_md5 -- EAP-MD5 authentication.
+ rlm_eap/types/rlm_eap_tls -- EAP-TLS based authentication.
+ rlm_eap/types/rlm_eap_ttls -- TTLS based authentication.
+ rlm_eap/types/rlm_eap_peap -- Windows PEAP based authentication.
+ rlm_eap/types/rlm_eap_sim -- EAP-SIM (GSM) based authentication
+
+CONFIGURATION
+
+ Add the eap configuration stanza to the modules section in radiusd.conf
+ to load and control rlm_eap and all the supported EAP-Types:
+
+ For example:
+ modules {
+ ...
+ eap {
+ default_eap_type = md5
+
+ md5 {
+ }
+ ...
+ }
+ ...
+ }
+
+ NOTE: You cannot have empty eap stanza. At least one EAP-Type sub-stanza
+ should be defined as above, otherwise the server will not know what type
+ of eap authentication mechanism to be used and the server will exit
+ with error.
+
+ All the various options and their associated default values for each
+ EAP-Type are documented in the sample radiusd.conf that is provided
+ with the distribution.
+
+ Since the EAP requests may not contain a requested EAP type, the
+ 'default_eap_type' configuration options is used by the EAP module
+ to determine which EAP type to choose for authentication.
+
+ NOTE: EAP cannot authorize a user. It can only authenticate.
+ Other Freeradius modules authorize the user.
+
+
+EAP SIM server
+
+ To configure EAP-SIM authentication, the following attributes must be
+ set in the server. This can be done in the users file, but in many cases
+ will be taken from a database server, via one of the SQL interface.
+
+ If one has SIM cards that one controls (i.e. whose share secret you know),
+ one should be able to write a module to generate these attributes
+ (the triplets) in the server.
+
+ If one has access to the SS7 based settlement network, then a module to
+ fetch appropriate triplets could be written. This module would act as
+ an authorization only module.
+
+ The attributes are:
+ EAP-Sim-Rand1 16 bytes
+ EAP-Sim-SRES1 4 bytes
+ EAP-Sim-KC1 8 bytes
+ EAP-Sim-Rand2 16 bytes
+ EAP-Sim-SRES2 4 bytes
+ EAP-Sim-KC2 8 bytes
+ EAP-Sim-Rand3 16 bytes
+ EAP-Sim-SRES3 4 bytes
+ EAP-Sim-KC3 8 bytes
+
+ EAP-SIM will send WEP attributes to the resquestor.
+
+EAP CLIENTS
+
+ 1. eapol_test, from wpa_supplicant.
+
+ 2. Freeradius has an "radeapclient" that can do EAP-MD5 (passwords),
+ as well as EAP-SIM. It is in modules/rlm_eap/radeapclient.
+
+TESTING
+
+ You will find several test cases in src/tests/ for the EAP-SIM code.
+
+
+HOW DO I USE IT (FAQ/Examples)
+
+ 1. How can I enable EAP-MD5 authentication ?
+
+ In radiusd.conf
+
+ modules {
+ ...
+ eap {
+ default_eap_type = md5
+ md5 {
+ }
+ ...
+ }
+ ...
+ }
+
+ # eap sets the authenticate type as EAP
+ authorize {
+ ...
+ eap
+ }
+
+ # eap authentication takes place.
+ authenticate {
+ eap
+ }
+
+ # If you are proxying EAP-LEAP requests
+ # This is required to make LEAP work.
+ post-proxy {
+ eap
+ }
+
+ 2. My Userbase is in LDAP and I want to use EAP-MD5 authentication
+
+ In radiusd.conf
+
+ modules {
+ ...
+ eap {
+ default_eap_type = md5
+ md5 {
+ }
+ ...
+ }
+ ...
+ }
+
+ # ldap gets the Configured password.
+ # eap sets the authenticate type as EAP
+ authorize {
+ ...
+ ldap
+ eap
+ ...
+ }
+
+ # eap authentication takes place.
+ authenticate {
+ ...
+ eap
+ ...
+ }
+
+ 3. How can I Proxy EAP messages, with/without User-Name attribute
+ in the Access-Request packets
+
+ With User-Name attribute in Access-Request packet,
+ EAP-proxying is just same as RADIUS-proxying.
+
+ If User-Name attribute is not present in Access-Request packet,
+ Freeradius can proxy the request with the following configuration
+ in radiusd.conf
+
+ # eap module should be configured as the First module in
+ # the authorize stanza
+
+ authorize {
+ eap
+ ... other modules.
+ }
+
+ With this configuration, eap_authorize creates User-Name attribute
+ from EAP-Identity response, if it is not present.
+ Once User-Name attribute is created, RADIUS proxying takes care
+ of EAP proxying.
+
+ 4. How Freeradius can handle EAP-START messages ?
+
+ In most of the cases this is handled by the Authenticator.
+
+ Only if it is required then, in radiusd.conf
+
+ authorize {
+ eap
+ ... other modules.
+ }
+
+ With the above configuration, RADIUS server immediately responds with
+ EAP-Identity request.
+
+ NOTE: EAP does not check for any Identity or maintains any state in case
+ of EAP-START. It blindly responds with EAP-Identity request.
+ Proxying is handled only after EAP-Identity response is received.
+
+ 5. I want to enable multiple EAP-Types, how can I configure ?
+
+ In radiusd.conf
+
+ modules {
+ ...
+ eap {
+ default_eap_type = tls
+ md5 {
+ }
+ tls {
+ ...
+ }
+ ...
+ }
+ ...
+ }
+
+ The above configuration will let the server load all the EAP-Types,
+ but the server can have only one default EAP-Type, as above.
+
+ Once EAP-Identity response is received by the server, based on the
+ default_eap_type, the server will send a new request (MD5-Challenge
+ request incase of md5, TLS-START request incase of tls) to the supplicant.
+ If the supplicant is rfc2284 compliant and does not support the
+ EAP-Type sent by the server then it sends EAP-Acknowledge with the
+ supported EAP-Type. If this EAP-Type is supported by the server then it
+ will send the respective EAP-request.
+
+ Example: If the supplicant supports only EAP-MD5 but the server
+ default_eap_type is configured as EAP-TLS, as above, then the server
+ will send TLS-START after EAP-Identity is received. Supplicant will
+ respond with EAP-Acknowledge(EAP-MD5). Server now responds with
+ MD5-Challenge.
+
+
+INSTALLATION
+ EAP, EAP-MD5, and Cisco LEAP do not require any additional packages.
+ Freeradius contains all the required packages.
+
+ For EAP-TLS, EAP-TTLS, and PEAP, OPENSSL, <http://www.openssl.org/>,
+ is required to be installed.
+ Any version from 0.9.7, should fairly work with this module.
+
+ EAP-SIM should not require any additional packages.
+
+
+IMPLEMENTATION (For Developers)
+
+ The rlm_eap module only deals with EAP specific authentication mechanism
+ and the generic interface to interact with all the EAP-Types.
+
+ Currently, these are the existing interfaces,
+ int attach(CONF_SECTION *conf, void **type_arg);
+ int initiate(void *type_arg, EAP_HANDLER *handler);
+ int authenticate(void *type_arg, EAP_HANDLER *handler);
+ int detach(void **type_arg);
+
+ attach() and detach() functions allocate and deallocate all the
+ required resources.
+
+ initiate() function begins the conversation when EAP-Identity response
+ is received. Incase of EAP-MD5, initiate() function sends the challenge.
+
+ authenticate() function uses specific EAP-Type authentication mechanism
+ to authenticate the user. During authentication many EAP-Requests and
+ EAP-Responses takes place for each authentication. Hence authenticate()
+ function may be called many times. EAP_HANDLER contains the complete
+ state information required.
+
+
+HOW EAP WORKS
+ as posted to the list, by John Lindsay <jlindsay@internode.com.au>
+
+ To make it clear for everyone, the supplicant is the software on the
+ client (machine with the wireless card).
+
+ The EAP process doesn't start until the client has associated with
+ the Access Point using Open authentication. If this process isn't
+ crystal clear you need to go away and gain understanding.
+
+ Once the association is made the AP blocks all traffic that is not
+ 802.1x so although associated the connection only has value for EAP.
+ Any EAP traffic is passed to the radius server and any radius traffic
+ is passed back to the client.
+
+ So, after the client has associated to the Access Point, the
+ supplicant starts the process for using EAP over LAN by asking the
+ user for their logon and password.
+
+ Using 802.1x and EAP the supplicant sends the username and a one-way
+ hash of the password to the AP.
+
+ The AP encapsulates the request and sends it to the RADIUS server.
+
+ The radius server needs a plaintext password so that it can perform
+ the same one-way hash to determine that the password is correct. If
+ it is, the radius server issues an access challenge which goes back
+ via to the AP to the client. (my study guide says client but my
+ brain says 'supplicant')
+
+ The client sends the EAP response to the challenge via the AP to the
+ RADIUS server.
+
+ If the response is valid the RADIUS server sends a success message
+ and the session WEP key (EAP over wireless) to the client via the
+ AP. The same session WEP key is also sent to the AP in the success
+ packet.
+
+ The client and the AP then begin using session WEP keys. The WEP key
+ used for multicasts is then sent from the AP to the client. It is
+ encrypted using the session WEP key.
+
+ACKNOWLEDGEMENTS
+ Primary author - Raghu <raghud@mail.com>
+
+ EAP-SIM - Michael Richardson <mcr@sandelman.ottawa.on.ca>
+ The development of the EAP/SIM support was funded by
+ Internet Foundation Austria (http://www.nic.at/ipa).
+
+
diff --git a/doc/modules/rlm_expiration b/doc/modules/rlm_expiration
new file mode 100644
index 0000000..eb7918b
--- /dev/null
+++ b/doc/modules/rlm_expiration
@@ -0,0 +1,23 @@
+Module to expire user accounts.
+
+This module can be used to expire user accounts. Expired users receive
+an Access-Reject on every authentication attempt. Expiration is based
+on the Expiration attribute which should be present in the check item
+list for the user we wish to perform expiration checks.
+
+
+
+Expiration attribute format:
+
+You can use Expiration := "23 Sep 2004" and the user will
+no longer be able to connect at 00:00 (midnight) on September 23rd,
+2004. If you want a certain time (other than midnight) you can do
+use Expiration := "23 Sep 2004 12:00".
+The nas will receive a Session-Timeout attribute calculated to kick
+the user off when the Expiration time occurs.
+
+
+
+Example entry (users files):
+
+user1 Expiration := "23 Sep 2004"
diff --git a/doc/modules/rlm_krb5 b/doc/modules/rlm_krb5
new file mode 100644
index 0000000..d70017f
--- /dev/null
+++ b/doc/modules/rlm_krb5
@@ -0,0 +1,47 @@
+The `rlm_krb5` FreeRADIUS module enables the use of Kerberos 5 for
+authentication.
+
+Compilation issues
+==================
+
+MIT libraries
+-------------
+
+The `rlm_krb5` module, by default, presumes you have the MIT Kerberos 5
+distribution. Notes from that distribution:
+
+On linux, you may have to change:
+
+ deplibs_test_method="pass_all"
+
+in `../libtool`
+
+Otherwise, it complains if the krb5 libs aren't shared.
+
+Heimdal libraries
+-----------------
+
+If you are using the Heimdal Kerberos 5 distribution, pass an
+`--enable-heimdal-krb5` option to `configure`.
+
+Configuration parameters
+========================
+
+You can configure the module with the following parameters:
+
+ krb5 {
+ # Keytab containing the key used by rlm_krb5
+ keytab = /path/to/keytab
+
+ # Principal that is used by rlm_krb5
+ service_principal = radius/some.host.com
+ }
+
+Make sure the keytab is readable by the user that is used to run `radiusd` and
+that your authorization configuration really uses `rlm_krb5` to do the
+authentication. You will need to add the following to the 'authenticate'
+section of your radiusd.conf file:
+
+ Auth-Type Kerberos {
+ krb5
+ }
diff --git a/doc/modules/rlm_pam b/doc/modules/rlm_pam
new file mode 100644
index 0000000..8a6673c
--- /dev/null
+++ b/doc/modules/rlm_pam
@@ -0,0 +1,108 @@
+
+ PAM Support for FreeRadius
+
+
+0. INTRODUCTION
+
+ PAM support was done by Jeph Blaize. Miguel a.l. Paraz <map@iphil.net>
+ ported it to FreeRADIUS' parent, Cistron-Radius. Chris Dent <cdent@kiva.net>
+ added the Pam-Auth attribute.
+
+1. USAGE
+
+ Use Auth-Type = Pam in the users file. You cannot use User-Password = "PAM"
+ as in other radius servers. Sorry.
+
+ You can also use ``Pam-Auth = "somestring"'' to specify an entry in
+ /etc/pam.d. The default is "radius".
+
+ Compile and install freeradius with pam support (./configure --help
+ will tell you how)
+
+ Within your radiusd.conf file, in the 'modules' section, make sure
+ that the pam section is enabled:
+
+ pam {
+ #
+ # The name to use for PAM authentication.
+ # PAM looks in /etc/pam.d/${pam_auth_name}
+ # for it's configuration.
+ #
+ # Note that any Pam-Auth attribute set in the 'users'
+ # file over-rides this one.
+ #
+ pam_auth = radiusd
+ }
+
+ In the 'authenticate' section, do the same:
+
+ authenticate {
+ # Uncomment this if you want to use PAM (Auth-Type = PAM)
+ pam
+ ...
+
+
+ In your /etc/pam.d/ directory create a file called radiusd with the
+ following contents (or whatever you want for your pam configuration,
+ this seems to work for me):
+
+#%PAM-1.0
+auth required /lib/security/pam_unix_auth.so shadow md5 nullok
+auth required /lib/security/pam_nologin.so
+account required /lib/security/pam_unix_acct.so
+password required /lib/security/pam_cracklib.so
+password required /lib/security/pam_unix_passwd.so shadow md5 nullok use_authtok
+session required /lib/security/pam_unix_session.so
+
+
+ If you don't want to run your freeradius server in debug mode as
+ root (ie, run as an unpriviledged user) you will need to run
+ freeradius with a group membership that is able to read the
+ /etc/shadow file - otherwise pam will be unable to read the
+ /etc/shadow file and will fail. I suggest a group called 'shadow' or
+ the like.
+
+ $ chgrp /etc/shadow shadow
+ $ chmod g+w /etc/shadow
+
+ And in the radiusd.conf file:
+
+ # On systems with shadow passwords, you might have to set 'group = shadow'
+ # for the server to be able to read the shadow password file.
+ #
+ # Change below to suit your setup.
+ user = radius
+ group = shadow
+
+
+ Please understand that giving anything except root read permissions
+ to the /etc/shadow file is something that you want to think a bit
+ upon!!
+
+2. NOTES
+
+ None.
+
+3. TODO:
+
+ Real PAM support, figure out how we can write a module that will make
+ it blend in with PAM more seamlessly. With this, we can replace the
+ DENY_SHELL with something more flexible such as a database.
+
+4. EXAMPLE:
+
+DEFAULT Auth-Type = Pam, NAS-IP-Address = 206.97.64.5
+ Service-Type = Framed-User,
+ Framed-Protocol = PPP,
+ Framed-IP-Address = 255.255.255.254,
+ Filter-Id = "std.ppp",
+ Framed-MTU = 1500,
+ Framed-Compression = Van-Jacobson-TCP-IP
+DEFAULT Auth-Type = Pam, Pam-Auth = "radius2", NAS-IP-Address = 127.0.0.1
+ Service-Type = Framed-User,
+ Framed-Protocol = PPP,
+ Framed-IP-Address = 255.255.255.254,
+ Filter-Id = "std.ppp",
+ Framed-MTU = 1500,
+ Framed-Compression = Van-Jacobson-TCP-IP
+
diff --git a/doc/modules/rlm_passwd b/doc/modules/rlm_passwd
new file mode 100644
index 0000000..59f4a59
--- /dev/null
+++ b/doc/modules/rlm_passwd
@@ -0,0 +1,50 @@
+RADIUS rlm_passwd (passwd-like files authorization module)
+
+FAQ
+
+Q: Can I use rlm_passwd to authenticate user against Linux shadow password
+ file or BSD-style master.passwd?
+A: Yes, but you need RADIUS running as root. Hint: use Crypt-Password
+ attribute. You probably don't want to use this module with
+ FreeBSD to authenticate against system file, as it already takes care
+ of caching passwd file entries, but it may be helpfull to authenticate
+ against alternate file.
+
+Q: Can I use rlm_passwd to authenticate user against SAMBA smbpasswd?
+A: Yes, you can. Hint: use LM-Password/NT-Password attribute, set
+ authtype = MS-CHAP.
+
+Q: Can I use rlm_password to authenticate user against BLA-BLA-BLApasswd?
+A: Probably you can, if BLA-BLA-BLA stores password in some format supported
+ by RADIUS, for example cleartext, NT/LM hashes, crypt, Netscape MD5 format.
+ You have to set authtype to corresponding type, for example
+ authtype = NS-MTA-MD5
+ for Netscape MD5.
+
+Q: Are where are differences between rlm_passwd and rlm_unix?
+A: rlm_passwd supports passwd files in any format and may be used, for
+ example, to parse FreeBSD's master.passwd or SAMBA smbpasswd files, but
+ it can't perform system authentication (for example to authenticate
+ NIS user, like rlm_unix does). If you need system authentication you
+ need rlm_unix, if you have to authenticate against files only under
+ BSD you need rlm_passwd, if you need to authenticate against files only
+ under Linux, you can choose between rlm_unix and rlm_passwd, probably
+ you will have nearly same results in performance (I hope :) ).
+
+Q: I'm using realms with rlm_passwd. I see rlm_passwd do not strip realm
+ from user name. How to configure rlm_passwd to strip realm?
+
+A: In case you configured realm to strip username, User-Password attribute
+ is not changed. Instead, rlm_realm creates new attribute Stripped-User-Name.
+ All you need is to use Stripped-User-Name instead of User-Name as a key
+ field for passwd file.
+
+Q: How can I say passwd to add attribute even if it's value is empty?
+
+A: set ignore_empty to "no" in module configuration.
+
+
+5. Acknowlegements:
+
+ ZARAZA, <3APA3A@security.nnov.ru>
+ Michael Chernyakhovsky <mike@mgn.ru> - reply-items support
diff --git a/doc/modules/rlm_python b/doc/modules/rlm_python
new file mode 100644
index 0000000..ef35f1b
--- /dev/null
+++ b/doc/modules/rlm_python
@@ -0,0 +1,76 @@
+Python module for freeradius
+Copyright 2002 Miguel A Paraz <mparaz@mparaz.com>
+Copyright 2002 Imperium Technology, Inc.
+
+PURPOSE:
+To allow module writers to write modules in a high-level language,
+for implementation or for prototyping.
+
+REQUIRES:
+Python - tested with 2.2
+
+BUILDING:
+./configure --with-experimental-modules
+
+
+USAGE:
+Make your module available to the Python interpreter by either putting it
+in a standard location, or 'EXPORT PYTHONPATH=$location'.
+
+
+
+
+
+BUGS:
+1. Can't compile statically (./configure --enable-shared=no) - causes
+SIGSEGV on the first malloc() in main().
+
+Design:
+1. Support for all module functions.
+2. One module per function allowed, for example, from experimental.conf:
+
+ python {
+ mod_instantiate = radiusd_test
+ func_instantiate = instantiate
+
+ mod_authorize = radiusd_test
+ func_authorize = authorize
+
+ mod_accounting = radiusd_test
+ func_accounting = accounting
+
+ mod_preacct = radiusd_test
+ func_preacct = preacct
+
+ mod_detach = radiusd_test
+ func_detach = detach
+
+ }
+
+
+3. Different functions are wrappers around the same core.
+4. func_detach is passed no parameters, returns module return value.
+5. If functions returns None (plain 'return' no return), default to RLM_OK
+6. Python instantation function can return -1 to signal failure and abort
+ startup.
+
+Available to module:
+import radiusd
+radiusd.rad_log(radiusd.L_XXX, message_string)
+radiusd.RLM_XXX
+
+
+
+TODO:
+1. Do we need to support other pair operations beyond set (:=) ?
+2. Should we pass the value pair info as a dict and not a tuple? Faster?
+2. Give access to more radiusd variables like the dictionary.
+3. Give access to other C functions.
+ Let the Python module deal with the structures directly, instead of
+ letting our C code do it afterwards.
+ What's a good way to represent this?
+
+
+
+
+
diff --git a/doc/modules/rlm_soh b/doc/modules/rlm_soh
new file mode 100644
index 0000000..eda5c4c
--- /dev/null
+++ b/doc/modules/rlm_soh
@@ -0,0 +1,183 @@
+== Intro ==
+
+This release adds support for Microsoft Statement-of-Health (SoH), which is
+a form of network access protection.
+
+Client support is present in Windows XP SP3, Vista and 7.
+
+SoH data can come in from several places:
+
+ * inside EAP-PEAP packets for 802.1x wireless/wired connections
+ * inside a radius packet (Microsoft VSA #55, MS-Quarantine-SOH) - VPN and
+ terminal services gateways can act as the radius client
+ * inside a DHCP request, in vendor-specific options
+
+FreeRadius supports all three types. The SoH statement is decoded into
+radius-style attributes, and you can write a policy in "unlang" to act
+on them, and permit, restrict or deny network access.
+
+== PEAP support ==
+
+SoH support in peap is enabled in eap.conf using config like so:
+
+ eap {
+ peap {
+ soh = yes
+ soh_virtual_server = "soh-server"
+ }
+ }
+
+When SoH is enabled, an EAP-PEAP client will be challenged to provide an
+SoH statement after providing it's identity (or resuming a PEAP session via
+SSL session resumption). Clients which do not support PEAP will NAK the
+request, and clients which do will answer it.
+
+The client reply will be written into a fake radius request and sent to the
+virtual server specified above; it will either look like:
+
+ SoH-Supported = no
+
+...or (from a Vista machine):
+
+ SoH-Supported = yes
+ SoH-MS-Machine-OS-vendor = Microsoft
+ SoH-MS-Machine-OS-version = 6
+ SoH-MS-Machine-OS-release = 0
+ SoH-MS-Machine-OS-build = 6001
+ SoH-MS-Machine-SP-version = 1
+ SoH-MS-Machine-SP-release = 0
+ SoH-MS-Machine-Processor = x86_64
+ SoH-MS-Machine-Name = "netbios.example.com"
+ SoH-MS-Correlation-Id = 0x54468936cb494374b127a6a3cc3bb11c01ca78d858ee1ef0
+ SoH-MS-Machine-Role = client
+ SoH-MS-Windows-Health-Status = "firewall ok snoozed=0 microsoft=1 up2date=1 enabled=1"
+ SoH-MS-Windows-Health-Status = "antivirus error not-installed"
+ SoH-MS-Windows-Health-Status = "antispyware ok snoozed=0 microsoft=1 up2date=1 enabled=1"
+ SoH-MS-Windows-Health-Status = "auto-updates ok action=install"
+ SoH-MS-Windows-Health-Status = "security-updates warn some-missing"
+
+If you have "copy_request_to_tunnel = yes" set on the peap module, the
+request variables like NAS-IP-Address and so on will be copied to the fake
+request as well.
+
+Clients without SoH seem to just NAK the SoH request and continue with the inner
+EAP auth. This has been tested as working with Windows XP SP2 and lower, Linux
+clients using NetworkManager & wpa_supplicant, MacOS 10.5, Nokia/Symbian S60 and
+iPhone OS 3.x. It should therefore be safe to deploy.
+
+== Radius support ==
+
+If you are running a Microsoft VPN or Terminal Services Gateway, these can
+be configured to send the SoH data to an upstream radius server, in this
+case presumably FreeRadius. To take advantage of this you will need to add
+the "soh" module to the "authorize" section of your virtual server, like so:
+
+server tsgateway {
+ preprocess
+ soh
+ if () {
+ ... policy goes here
+ }
+}
+
+The SoH module simply looks for the Microsoft VSA #55 and decodes the SoH
+data, adding the SoH attributes to the request - see above for an example
+of the available attributes.
+
+The SoH module also does dynamic expansions - see below for more info.
+
+== DHCP support ==
+
+If you compile FreeRadius with DHCP support, the "soh" module can challenge
+a DHCP client for SoH data in the DHCPOFFER packet. As with normal radius,
+the SoH attributes are added to the request. You would use like so:
+
+server dhcp {
+ dhcp DHCP-Discover {
+ soh
+ # note - no SoH attributes are added here, the client hasn't sent them yet
+
+ # other DHCP config
+ }
+
+ dhcp DHCP-Request {
+ soh
+ if () {
+ # SoH policy
+ }
+ # other DHCP config
+ }
+}
+
+== soh module ==
+
+The "soh" module decodes the radius & DHCP payloads. It also makes some dynamic
+variables available, for example:
+
+authorize {
+ soh
+ update request {
+ Tmp-String-0 = "%{soh:OS}"
+ }
+}
+
+...will give you a string like "Windows Vista 6.1.100 sp 1.0" or "Windows XP 5.x.x sp 3.0"
+
+At the moment, this is the only dynamic expansion; in future, we will make
+various bits of info available, for example non-Microsoft SoH records (see below)
+
+== Non-microsoft SoH data ==
+
+The Windows SoH structure is extensible and, in principle, clients can be
+extended with .dll plugins to add vendor-specific info to the SoH, which
+can then be checked on the server.
+
+At the present time, few plugins are known and I have seen none, so can't
+add support for them.
+
+== Client configuration ==
+
+The code works fine with Win XP SP3 & Vista on both wired & wireless. However
+contrary to what some sites claim, the NAP service is disabled by default, as
+are the many NAP remediation agents. These can be enabled from the command prompt
+with (for XP; instructions may differ for other windows versions):
+
+ sc config napagent start= auto
+ sc start napagent
+
+ # optionally for wired 802.1x; the dot3svc should usually be made dependent
+ # on the napagent service, else the machine might attempt 802.1x before NAP
+ # has started...
+
+ sc config dot3svc start= auto depend= napagent
+ sc start dot3svc
+
+ # enable the EAP agent
+ netsh nap client show config
+
+ # get the "ID" value for the "EAP Quarantine Enforcement Client"
+ netsh nap client set enforce id=$ID admin=enable
+
+ # repeat for DHCP, VPN or Terminal Services agents
+
+This can be automated via Group Policy.
+
+You then need to enable EAP, PEAP, Quarantine Checks & the relevant auth method
+on the relevant adapters. This can be done with "netsh xml profiles" or Group
+Policy - google for the relevant terms, or see the MS article:
+
+ http://technet.microsoft.com/en-us/library/bb726965.aspx
+
+...and related links.
+
+== TODO ==
+
+Currently the code does not support sending the final SoH reply. This
+is because the SoH reply (see section 2.2.9 of MS-SOH version
+v20091104) needs various fields formatted in a manner which is not
+obvious to me, and I don't currently have access to a windows NAP
+server to look at a working example. The clients I have access don't
+seem to mind.
+
+ Phil Mayers <p.mayers@imperial.ac.uk>
+ December 2009
diff --git a/doc/modules/rlm_sql b/doc/modules/rlm_sql
new file mode 100644
index 0000000..0f06660
--- /dev/null
+++ b/doc/modules/rlm_sql
@@ -0,0 +1,283 @@
+ SQL Module
+
+0. Introduction
+
+ The SQL module is composed of two parts: a generic SQL front-end
+ (rlm_sql), and a series of database-dependent back-end drivers,
+ (rlm_sql_mysql, rlm_sql_postgresql, etc.)
+
+ In order to build the drivers, you MUST ALSO install the development
+ versions of the database. That is, you must have the appropriate
+ header files and client libraries for (say) MySQL. The
+ rlm_sql_mysql driver is NOT a complete MySQL client implementation.
+ Instead, it is a small 'shim' between the FreeRADIUS rlm_sql module,
+ and the MySQL client libraries.
+
+
+ In general, the SQL schemas mirror the layout of the 'users' file.
+ So for configuring check items and reply items, see 'man 5 users',
+ and the examples in the 'users' file.
+
+
+1. Schema and usage
+
+ The schemas are available in raddb/sql/<DB>/*, where <DB> is the
+ name of the database (mysql, postgresql, etc.)
+
+ The SQL module employs two sets of check and reply item tables for
+ processing in the authorization stage. One set of tables (radcheck and
+ radreply) are specific to a single user. The other set of tables
+ (radgroupcheck and radgroupreply) is used to apply check and reply items
+ to users that are members of a certain SQL group. The usergroup table
+ provides the list of groups each user is a member of along with a priority
+ field to control the order in which groups are processed.
+
+ When a request comes into the server and is processed by the SQL module,
+ the flow goes something like this:
+
+ 1. Search the radcheck table for any check attributes specific to the user
+ 2. If check attributes are found, and there's a match, pull the reply items
+ from the radreply table for this user and add them to the reply
+ 3. Group processing then begins if any of the following conditions are met:
+ a. The user IS NOT found in radcheck
+ b. The user IS found in radcheck, but the check items don't match
+ c. The user IS found in radcheck, the check items DO match AND
+ Fall-Through is set in the radreply table
+ d. The user IS found in radcheck, the check items DO match AND
+ the read_groups directive is set to 'yes'
+ 4. If groups are to be processed for this user, the first thing that is
+ done is the list of groups this user is a member of is pulled from the
+ usergroup table ordered by the priority field. The priority field of
+ the usergroup table allows us to control the order in which groups are
+ processed, so that we can emulate the ordering in the users file. This
+ can be important in many cases.
+ 5. For each group this user is a member of, the corresponding check items
+ are pulled from radgroupcheck table and compared with the request. If
+ there is a match, the reply items for this group are pulled from the
+ radgroupreply table and applied.
+ 6. Processing continues to the next group IF:
+ a. There was not a match for the last group's check items OR
+ b. Fall-Through was set in the last group's reply items
+ (The above is exactly the same as in the users file)
+ 7. Finally, if the user has a User-Profile attribute set or the Default
+ Profile option is set in the sql.conf, then steps 4-6 are repeated for
+ the groups that the profile is a member of.
+
+ For any fairly complex setup, it is likely that most of the actual
+ processing will be done in the groups. In these cases, the user entry in
+ radcheck will be of limited use except for things like setting the user's
+ password. So, one might have the following setup:
+
+ radcheck table:
+ joeuser Cleartext-Password := somepassword
+
+ radreply table:
+ joeuser Fall-Through = Yes
+
+ radgroupcheck table:
+ Check items for various connection scenarios
+
+ radgroupreply table:
+ reply items for the groups
+
+ usergroup table:
+ joeuser WLANgroup 1(this is the priority)
+ joeuser PPPgroup 2
+
+
+2. What NOT to do.
+
+ One of the fields of the SQL schema is named 'op' This is for the
+ 'operator' used by the attributes. e.g.:
+
+ Framed-IP-Address = 1.2.3.4
+ ^ ATTRIBUTE ----^ ^ OP ^ VALUE
+
+ If you want the server to be completely misconfigured, and to never
+ do what you want, leave the 'op' field blank. If you want to be
+ rudely told to RTFM, then post questions on the mailing list, asking
+
+ "why doesn't my SQL configuration work when I leave the 'op' field empty?"
+
+
+ The short answer is that with the op field empty, the server does
+ not know what you want it to do with the attribute. Should it be
+ added to the reply? Maybe you wanted to compare the operator to one
+ in the request? The server simply doesn't know.
+
+ So put a value in the field. The value is the string form of the
+ operator: "=", ">=", etc. See Section 4, below, for more details.
+
+
+3. Authentication versus Authorization
+
+ Many people ask if they can "authenticate" users to their SQL
+ database. The answer to this question is "You're asking the wrong
+ question."
+
+ An SQL database stores information. An SQL database is NOT an
+ authentication server. The ONLY users who should be able to
+ authenticate themselves to the database are the people who
+ administer it. Most administrators do NOT want every user to be
+ able to access the database, which means that most users will not be
+ able to "authenticate" themselves to the database.
+
+ Instead, the users will have their authorization information (name,
+ password, configuration) stored in the database. The configuration
+ files for FreeRADIUS contain a username and password used to
+ authenticate FreeRADIUS to the SQL server. (See raddb/sql.conf).
+ Once the FreeRADIUS authentication server is connected to the SQL
+ database server, then FreeRADIUS can pull user names and passwords
+ out of the database, and use that information to perform the
+ authentication.
+
+4. Operators
+
+ The list of operators is given below.
+
+ Op Example and documentation
+ -- -------------------------
+
+ = "Attribute = Value"
+
+ Not allowed as a check item for RADIUS protocol attributes. It is
+ allowed for server configuration attributes (Auth-Type, etc), and sets
+ the value of on attribute, only if there is no other item of the
+ same attribute.
+
+ As a reply item, it means "add the item to the reply list, but
+ only if there is no other item of the same attribute."
+
+
+ := "Attribute := Value"
+
+ Always matches as a check item, and replaces in the
+ configuration items any attribute of the same name. If no
+ attribute of that name appears in the request, then this
+ attribute is added.
+
+ As a reply item, it has an identical meaning, but for the
+ reply items, instead of the request items.
+
+ == "Attribute == Value"
+
+ As a check item, it matches if the named attribute is present
+ in the request, AND has the given value.
+
+ Not allowed as a reply item.
+
+
+ += "Attribute += Value"
+
+ Always matches as a check item, and adds the current attribute
+ with value to the list of configuration items.
+
+ As a reply item, it has an identical meaning, but the
+ attribute is added to the reply items.
+
+
+ != "Attribute != Value"
+
+ As a check item, matches if the given attribute is in the
+ request, AND does not have the given value.
+
+ Not allowed as a reply item.
+
+
+ > "Attribute > Value"
+
+ As a check item, it matches if the request contains an
+ attribute with a value greater than the one given.
+
+ Not allowed as a reply item.
+
+
+ >= "Attribute >= Value"
+
+ As a check item, it matches if the request contains an
+ attribute with a value greater than, or equal to the one
+ given.
+
+ Not allowed as a reply item.
+
+ < "Attribute < Value"
+
+ As a check item, it matches if the request contains an
+ attribute with a value less than the one given.
+
+ Not allowed as a reply item.
+
+
+ <= "Attribute <= Value"
+
+ As a check item, it matches if the request contains an
+ attribute with a value less than, or equal to the one given.
+
+ Not allowed as a reply item.
+
+
+ =~ "Attribute =~ Expression"
+
+ As a check item, it matches if the request contains an
+ attribute which matches the given regular expression. This
+ operator may only be applied to string attributes.
+
+ Not allowed as a reply item.
+
+
+ !~ "Attribute !~ Expression"
+
+ As a check item, it matches if the request contains an
+ attribute which does not match the given regular expression.
+ This operator may only be applied to string attributes.
+
+ Not allowed as a reply item.
+
+
+ =* "Attribute =* Value"
+
+ As a check item, it matches if the request contains the named
+ attribute, no matter what the value is.
+
+ Not allowed as a reply item.
+
+
+ !* "Attribute !* Value"
+
+ As a check item, it matches if the request does not contain
+ the named attribute, no matter what the value is.
+
+ Not allowed as a reply item.
+
+5. Instances
+
+ Just like any other module, multiple instances of the rlm_sql
+ module can be defined and used wherever you like.
+
+ The default .conf files for the different database types,
+ contain 1 instance without a name like so:
+ sql {
+ ...
+ }
+
+ You can create multiple named instances like so:
+ sql sql_instance1 {
+ ...
+ }
+ sql sql_instance2 {
+ ...
+ }
+
+ And then you can use a specific instance in radiusd.conf, like
+ so:
+ authorize {
+ ...
+ sql_instance1
+ ...
+ }
+ accounting {
+ ...
+ sql_instance1
+ sql_instance2
+ ...
+ }
diff --git a/doc/modules/rlm_sqlcounter b/doc/modules/rlm_sqlcounter
new file mode 100644
index 0000000..54ad170
--- /dev/null
+++ b/doc/modules/rlm_sqlcounter
@@ -0,0 +1,182 @@
+rlm_sqlcounter installation and running guide
+by Ram Narula ram@princess1.net
+Internet for Education (Thailand)
+
+*) Pre-requisites:
+Make sure to have configured radiusd with rlm_sqlcounter
+installed
+
+> make clean
+> ./configure --with-experimental-modules
+> make
+> make install
+
+Make sure to have radiusd running properly under sql
+and there must be a "sql" entry under accounting{ } section
+of radiusd.conf
+
+*) Configuration:
+
+[1] Create a text file called sqlcounter.conf in the same
+directory where radiusd.conf resides (usually /usr/local/etc/raddb)
+with the following content (for mysql):
+
+#-----#
+sqlcounter noresetcounter {
+ sql_module_instance = sqlcca3
+ counter_name = Max-All-Session-Time
+ check_name = Max-All-Session
+ reply_name = Session-Timeout
+ key = User-Name
+ reset = never
+
+ query = "SELECT SUM(AcctSessionTime) FROM radacct WHERE UserName='%{%k}'"
+
+ }
+
+
+sqlcounter dailycounter {
+ sql_module_instance = sqlcca3
+ driver = "rlm_sqlcounter"
+ counter_name = Daily-Session-Time
+ check_name = Max-Daily-Session
+ reply_name = Session-Timeout
+ key = User-Name
+ reset = daily
+
+ query = "SELECT SUM(AcctSessionTime - GREATEST((%b - UNIX_TIMESTAMP(AcctStartTime)), 0)) FROM radacct WHERE UserName='%{%k}' AND UNIX_TIMESTAMP(AcctStartTime) + AcctSessionTime > '%b'"
+
+ }
+
+sqlcounter monthlycounter {
+ sql_module_instance = sqlcca3
+ counter_name = Monthly-Session-Time
+ check_name = Max-Monthly-Session
+ reply_name = Session-Timeout
+ key = User-Name
+ reset = monthly
+
+ query = "SELECT SUM(AcctSessionTime - GREATEST((%b - UNIX_TIMESTAMP(AcctStartTime)), 0)) FROM radacct WHERE UserName='%{%k}' AND UNIX_TIMESTAMP(AcctStartTime) + AcctSessionTime > '%b'"
+
+ }
+
+#-----#
+
+The respective lines for postgresql are:
+
+query = "SELECT SUM(AcctSessionTime) FROM radacct WHERE UserName='%{%k}'"
+query = "SELECT SUM(AcctSessionTime - GREATEST((%b - EXTRACT(epoch FROM AcctStartTime)), 0)) FROM radacct WHERE UserName='%{%k}' AND EXTRACT(epoch FROM AcctStartTime) + AcctSessionTime > '%b'"
+query = "SELECT SUM(AcctSessionTime - GREATEST((%b - EXTRACT(epoch FROM AcctStartTime)), 0)) FROM radacct WHERE UserName='%{%k}' AND EXTRACT(epoch FROM AcctStartTime) + AcctSessionTime > '%b'"
+
+If you are running postgres 7.x, you may not have a GREATEST function.
+
+An example of one is:
+
+CREATE OR REPLACE FUNCTION "greater"(integer, integer) RETURNS integer AS '
+DECLARE
+ res INTEGER;
+ one INTEGER := 0;
+ two INTEGER := 0;
+BEGIN
+ one = $1;
+ two = $2;
+ IF one IS NULL THEN
+ one = 0;
+ END IF;
+ IF two IS NULL THEN
+ two = 0;
+ END IF;
+ IF one > two THEN
+ res := one;
+ ELSE
+ res := two;
+ END IF;
+ RETURN res;
+END;
+' LANGUAGE 'plpgsql';
+
+[2] Include the above file to radiusd.conf by adding a line in
+modules{ } section
+
+modules {
+
+$INCLUDE ${confdir}/sqlcounter.conf
+
+...some other entries here...
+
+[3] Make sure to have the sqlcounter names under authorize section
+like the followings:
+
+authorize {
+...some entries here...
+...some entries here...
+...some entries here...
+...some entries here...
+
+noresetcounter
+dailycounter
+monthlycounter
+}
+
+noresetcounter: the counter that never resets, can be used
+for real session-time cumulation
+
+dailycounter: the counter that resets everyday, can be used
+for limiting daily access time (eg. 3 hours a day)
+
+monthlycounter: the counter that resets monthly, can be used for
+limiting monthly access time (eg. 50 hours per month)
+
+You can make your own names and directives for resetting the counter
+by reading the sample sqlcounter configuration in
+raddb/experimental.conf
+
+
+
+*) Implementation:
+
+Add sqlcounter names to be used into radcheck or radgroupcheck
+table appropriately for sql. For users file just follow the
+example below.
+
+Note: The users in the example below must be able to login
+normally as the example will only show how to apply sqlcounter
+counters.
+
+Scenarios
+[1] username test0001 have total time limit of 15 hours
+(user can login as many times as needed but can be online for
+total time of 15 hours which is 54000 seconds)
+If using normal users file authentication the entry can look like:
+
+test0001 Max-All-Session := 54000, User-Password == "blah"
+ Service-Type = Framed-User,
+ Framed-Protocol = PPP
+
+or for sql make sure to have Max-All-Session entry under either
+radcheck or radgroup check table:
+> INSERT into radcheck VALUES ('','test0001','Max-All-Session','54000',':=');
+
+[2] username test0002 have total time limit of 3 hours a day
+
+test0002 Max-Daily-Session := 10800, User-Password == "blah"
+ Service-Type = Framed-User,
+ Framed-Protocol = PPP
+or in sql:
+> INSERT into radcheck VALUES ('','test0002','Max-Daily-Session','10800',':=');
+
+
+[3] username test0003 have total time limit of 90 hours a month
+
+test0003 Max-Monthly-Session := 324000, User-Password == "blah"
+ Service-Type = Framed-User,
+ Framed-Protocol = PPP
+in sql:
+> INSERT into radcheck VALUES ('','test0003','Max-Monthly-Session','10800',':=');
+
+
+Note that Max-All-Session, Max-Daily-Session and Max-Monthly-Session are
+definied in sqlcounter.conf
+
+VERY IMPORTANT:
+Accounting must be done via sql or this will not work.
diff --git a/doc/modules/rlm_sqlippool b/doc/modules/rlm_sqlippool
new file mode 100644
index 0000000..3d2840f
--- /dev/null
+++ b/doc/modules/rlm_sqlippool
@@ -0,0 +1,40 @@
+Welcome to the SQL Based IP Pool module.
+
+**********************************************************************
+As of September 2006 this module is still under some development and
+currently is only tested by the developers on PostgreSQL (Version 8.1)
+ Use it at your own risk!
+If plan to attempt to use a DB other than PostgreSQL please expect to
+have to do extra work which is not for SQL newbies.
+Having said that it works great for us in production and should (with
+some work) function correctly with other SQL server types.
+**********************************************************************
+
+
+To use the sqlipool module you simply need to have an IP-Pool Attribute
+(Keep in mind that its a **CHECK** item, not reply) in the required
+configuration file, which is either in files(users), sql or any other
+type of configuration schema.
+
+The initialization of the radippool table is left to the user instead of
+being handled inside the module. This allows pool management to be done
+from any sql capable programming language and pools can be created,
+resized, deleted at run time without radiusd needing to be restarted.
+
+The only required fields are, pool_name and ip_address. A pool consists
+of one or more rows in the table with the same pool_name and a different
+ip_address. There is no restriction on which ip addresses/ranges may be in
+the same pool, and addresses do not need to be concurrent.
+
+We are currently using the variable definitions of the xlat module, so
+before editing the sqlippool.conf file, please go and read the
+variables.rst in the doc/configuration directory. It will help you alot!..
+
+As you may noticed, there is a pool-key variable in the config file which
+allows you to select which attribute is unique according to your NAS setup.
+On a standard dialup NAS this is going to be "NAS-Port" but on an ethernet
+or wireless network it will probably be "Calling-Station-Id". Other more
+exotic options like "3GPP-IMSI" may also exist depending on your NAS.
+The only requirement is that the pool-key must be unique and must be
+received in both Access-Request and Accounting packages so that we know to
+clear the lease on the ip when the session disconnects.
diff --git a/doc/schemas/ldap/edir/freeradius-clients.ldif b/doc/schemas/ldap/edir/freeradius-clients.ldif
new file mode 100644
index 0000000..96a0f44
--- /dev/null
+++ b/doc/schemas/ldap/edir/freeradius-clients.ldif
@@ -0,0 +1,69 @@
+#
+# LDIF Novell eDirectory schema to load dynamic clients from LDAP
+#
+# copyright 2013 Olivier Beytrison <olivier@heliosnet.org>
+#
+# 1.3.6.1.4.1.11344.1.100 is the toplevel OID for this work
+# .1 = objectclasses
+# .2 = attributs
+dn: cn=schema
+changetype: modify
+add: attributetypes
+attributetypes: ( 1.3.6.1.4.1.11344.1.100.2.1
+ NAME 'radiusClientIdentifier'
+ DESC 'Client Identifier'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+attributetypes: ( 1.3.6.1.4.1.11344.1.100.2.2
+ NAME 'radiusClientSecret'
+ DESC 'Client Secret'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+attributetypes: ( 1.3.6.1.4.1.11344.1.100.2.3
+ NAME 'radiusClientShortname'
+ DESC 'Client Shortname'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+attributetypes: ( 1.3.6.1.4.1.11344.1.100.2.4
+ NAME 'radiusClientVirtualServer'
+ DESC 'VirtualServer'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+attributetypes: ( 1.3.6.1.4.1.11344.1.100.2.5
+ NAME 'radiusClientType'
+ DESC 'Client Type'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+attributetypes: ( 1.3.6.1.4.1.11344.1.100.2.6
+ NAME 'radiusClientRequireMa'
+ DESC 'Require Message Authenticator'
+ EQUALITY booleanMatch
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+attributetypes: ( 1.3.6.1.4.1.11344.1.100.2.7
+ NAME 'radiusClientComment'
+ DESC 'Client comment'
+ EQUALITY caseIgnoreMatch
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+
+dn: cn=schema
+changetype: modify
+add: objectclasses
+objectclasses: ( 1.3.6.1.4.1.11344.1.100.1.1
+ NAME 'radiusClient'
+ DESC 'radiusClient object class'
+ SUP top STRUCTURAL
+ MUST ( radiusClientIdentifier $ radiusClientSecret )
+ MAY ( radiusClientShortname $ radiusClientVirtualServer $ radiusClientType $ radiusClientRequireMa $radiusClientcomment ))
diff --git a/doc/schemas/ldap/iplanet/freeradius.ldif b/doc/schemas/ldap/iplanet/freeradius.ldif
new file mode 100644
index 0000000..3834cb3
--- /dev/null
+++ b/doc/schemas/ldap/iplanet/freeradius.ldif
@@ -0,0 +1,73 @@
+# This is a LDAPv3 schema for RADIUS attributes.
+# Converted for use with iPlanet/Sun Directory Servers 5.x by Arne Brutschy <abrutschy@xylon.de>
+#
+# Originally Tested on OpenLDAP 2.0.7
+# Posted by Javier Fernandez-Sanguino Pena <jfernandez@sgi.es>
+# LDAP v3 version by Jochen Friedrich <jochen@scram.de>
+# Updates by Adrian Pavlykevych <pam@polynet.lviv.ua>
+##############
+dn: cn=schema
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.1 NAME 'radiusArapFeatures' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.2 NAME 'radiusArapSecurity' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.3 NAME 'radiusArapZoneAccess' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.44 NAME 'radiusAuthType' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.4 NAME 'radiusCallbackId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.5 NAME 'radiusCallbackNumber' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.6 NAME 'radiusCalledStationId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.7 NAME 'radiusCallingStationId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.8 NAME 'radiusClass' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.45 NAME 'radiusClientIPAddress' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.9 NAME 'radiusFilterId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.10 NAME 'radiusFramedAppleTalkLink' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.11 NAME 'radiusFramedAppleTalkNetwork' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.12 NAME 'radiusFramedAppleTalkZone' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.13 NAME 'radiusFramedCompression' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.14 NAME 'radiusFramedIPAddress' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.15 NAME 'radiusFramedIPNetmask' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.16 NAME 'radiusFramedIPXNetwork' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.17 NAME 'radiusFramedMTU' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.18 NAME 'radiusFramedProtocol' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.19 NAME 'radiusFramedRoute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.20 NAME 'radiusFramedRouting' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.46 NAME 'radiusGroupName' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.47 NAME 'radiusHint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.48 NAME 'radiusHuntgroupName' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.21 NAME 'radiusIdleTimeout' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.22 NAME 'radiusLoginIPHost' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.23 NAME 'radiusLoginLATGroup' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.24 NAME 'radiusLoginLATNode' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.25 NAME 'radiusLoginLATPort' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.26 NAME 'radiusLoginLATService' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.27 NAME 'radiusLoginService' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.28 NAME 'radiusLoginTCPPort' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.29 NAME 'radiusPasswordRetry' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.30 NAME 'radiusPortLimit' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.49 NAME 'radiusProfileDN' DESC '' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.31 NAME 'radiusPrompt' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.50 NAME 'radiusProxyToRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.51 NAME 'radiusReplicateToRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.52 NAME 'radiusRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.32 NAME 'radiusServiceType' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.33 NAME 'radiusSessionTimeout' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.34 NAME 'radiusTerminationAction' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.35 NAME 'radiusTunnelAssignmentId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.36 NAME 'radiusTunnelMediumType' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.37 NAME 'radiusTunnelPassword' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.38 NAME 'radiusTunnelPreference' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.39 NAME 'radiusTunnelPrivateGroupId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.40 NAME 'radiusTunnelServerEndpoint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.41 NAME 'radiusTunnelType' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.42 NAME 'radiusVSA' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.43 NAME 'radiusTunnelClientEndpoint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+#need to change asn1.id
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.53 NAME 'radiusSimultaneousUse' DESC '' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.54 NAME 'radiusLoginTime' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.55 NAME 'radiusUserCategory' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.56 NAME 'radiusStripUserName' DESC '' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.57 NAME 'dialupAccess' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.58 NAME 'radiusExpiration' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.59 NAME 'radiusAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.60 NAME 'radiusControlAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.61 NAME 'radiusReplyAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.62 NAME 'radiusRequestAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+objectClasses: ( 1.3.6.1.4.1.11344.4.3.2.1 NAME 'radiusprofile' DESC '' SUP top AUXILIARY MUST ( cn ) MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $ radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $ radiusCalledStationId $ radiusCallingStationId $ radiusClass $ radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $ radiusFramedAppleTalkNetwork $ radiusFramedAppleTalkZone $ radiusFramedCompression $ radiusFramedIPAddress $ radiusFramedIPNetmask $ radiusFramedIPXNetwork $ radiusFramedMTU $ radiusFramedProtocol $ radiusAttribute $ radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $ radiusGroupName $ radiusHint $ radiusHuntgroupName $ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $ radiusLoginLATPort $ radiusLoginLATService $ radiusLoginService $ radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $ radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $ radiusRealm $ radiusReplicateToRealm $ radiusServiceType $ radiusSessionTimeout $ radiusStripUserName $ radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDn $ radiusSimultaneousUse $ radiusTunnelAssignmentId $ radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $ radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $ radiusTunnelType $ radiusUserCategory $ radiusVSA $ radiusExpiration $ dialupAccess $ radiusControlAttribute $ radiusReplyAttribute $ radiusRequestAttribute ) )
diff --git a/doc/schemas/ldap/iplanet/freeradius.schema b/doc/schemas/ldap/iplanet/freeradius.schema
new file mode 100644
index 0000000..6392b02
--- /dev/null
+++ b/doc/schemas/ldap/iplanet/freeradius.schema
@@ -0,0 +1,78 @@
+# This is a LDAPv3 schema for RADIUS attributes.
+# Tested on Sun One Directory server 5.2
+# Created by Daniel Wilson (danielwilson_2k@yahoo.com)
+##############
+dn: cn=schema
+objectClass: top
+objectClass: ldapSubentry
+objectClass: subschema
+cn: schema
+#######################
+# aci to ensure that the standard schema attributes are visible to
+# all LDAP clients (anonymous access).
+#
+aci: (target="ldap:///cn=schema")(targetattr !="aci")(version 3.0;acl "anonymous, no acis"; allow (read, search, compare) userdn = "ldap:///anyone";
+#######################
+objectClasses: ( 1.3.6.1.4.1.11344.4.3.2.1 NAME 'radiusprofile' SUP top AUXILIARY DESC 'Free Radius schema for Directory Server 5.2' MUST (cn) MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $ radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $ radiusCalledStationId $ radiusCallingStationId $ radiusClass $ radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $ radiusFramedAppleTalkNetwork $ radiusFramedAppleTalkZone $ radiusFramedCompression $ radiusFramedIPAddress $ radiusFramedIPNetmask $ radiusFramedIPXNetwork $ radiusFramedMTU $ radiusFramedProtocol $ radiusAttribute $ radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $ radiusGroupName $ radiusHint $ radiusHuntgroupName $ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $ radiusLoginLATPort $ radiusLoginLATService $ radiusLoginService $ radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $ radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $ radiusRealm $ radiusReplicateToRealm $ radiusServiceType $ radiusSessionTimeout $ radiusStripUserName $ radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDN $ radiusSimultaneousUse $ radiusTunnelAssignmentId $ radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $ radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $ radiusTunnelType $ radiusUserCategory $ radiusVSA $ radiusExpiration $ dialupAccess $ radiusControlAttribute $ radiusReplyAttribute $radiusRequestAttribute ) X-ORIGIN 'user defined')
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.1 NAME 'radiusArapFeatures' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined')
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.2 NAME 'radiusArapSecurity' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined')
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.3 NAME 'radiusArapZoneAccess' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined')
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.44 NAME 'radiusAuthType' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.4 NAME 'radiusCallbackId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.5 NAME 'radiusCallbackNumber' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.6 NAME 'radiusCalledStationId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.7 NAME 'radiusCallingStationId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.8 NAME 'radiusClass' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.45 NAME 'radiusClientIPAddress' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.9 NAME 'radiusFilterId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.10 NAME 'radiusFramedAppleTalkLink' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.11 NAME 'radiusFramedAppleTalkNetwork' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.12 NAME 'radiusFramedAppleTalkZone' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.13 NAME 'radiusFramedCompression' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.14 NAME 'radiusFramedIPAddress' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.15 NAME 'radiusFramedIPNetmask' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.16 NAME 'radiusFramedIPXNetwork' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.17 NAME 'radiusFramedMTU' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.18 NAME 'radiusFramedProtocol' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.19 NAME 'radiusFramedRoute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.20 NAME 'radiusFramedRouting' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.46 NAME 'radiusGroupName' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.47 NAME 'radiusHint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.48 NAME 'radiusHuntgroupName' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.21 NAME 'radiusIdleTimeout' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.22 NAME 'radiusLoginIPHost' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.23 NAME 'radiusLoginLATGroup' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.24 NAME 'radiusLoginLATNode' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.25 NAME 'radiusLoginLATPort' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.26 NAME 'radiusLoginLATService' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.27 NAME 'radiusLoginService' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.28 NAME 'radiusLoginTCPPort' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.29 NAME 'radiusPasswordRetry' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.30 NAME 'radiusPortLimit' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.49 NAME 'radiusProfileDN' DESC '' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.31 NAME 'radiusPrompt' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.50 NAME 'radiusProxyToRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.51 NAME 'radiusReplicateToRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.52 NAME 'radiusRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.32 NAME 'radiusServiceType' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.33 NAME 'radiusSessionTimeout' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.34 NAME 'radiusTerminationAction' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.35 NAME 'radiusTunnelAssignmentId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.36 NAME 'radiusTunnelMediumType' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.37 NAME 'radiusTunnelPassword' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.38 NAME 'radiusTunnelPreference' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.39 NAME 'radiusTunnelPrivateGroupId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.40 NAME 'radiusTunnelServerEndpoint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.41 NAME 'radiusTunnelType' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.42 NAME 'radiusVSA' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.43 NAME 'radiusTunnelClientEndpoint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.53 NAME 'radiusSimultaneousUse' DESC '' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.54 NAME 'radiusLoginTime' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.55 NAME 'radiusUserCategory' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.56 NAME 'radiusStripUserName' DESC '' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.57 NAME 'dialupAccess' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.58 NAME 'radiusExpiration' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'user defined' )
+attributeTypes: ( 1.3.6.1.4.1.11344.4.3.1.59 NAME 'radiusAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributetypes: ( 1.3.6.1.4.1.11344.4.3.1.60 NAME 'radiusControlAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributetypes: ( 1.3.6.1.4.1.11344.4.3.1.61 NAME 'radiusReplyAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributetypes: ( 1.3.6.1.4.1.11344.4.3.1.62 NAME 'radiusRequestAttribute' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
diff --git a/doc/schemas/ldap/openldap/freeradius-clients.ldif b/doc/schemas/ldap/openldap/freeradius-clients.ldif
new file mode 100644
index 0000000..d591799
--- /dev/null
+++ b/doc/schemas/ldap/openldap/freeradius-clients.ldif
@@ -0,0 +1,17 @@
+#
+# LDIF Openldap schema to load dynamic clients from LDAP
+#
+# copyright 2013 Olivier Beytrison <olivier@heliosnet.org>
+#
+
+dn: cn=radiusClient,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: radiusClient
+olcAttributeTypes: ( 1.3.6.1.4.1.11344.1.100.2.1 NAME 'radiusClientIdentifier' SINGLE-VALUE DESC 'Client identifier' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+olcAttributeTypes: ( 1.3.6.1.4.1.11344.1.100.2.2 NAME 'radiusClientSecret' SINGLE-VALUE DESC 'Client secret' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+olcAttributeTypes: ( 1.3.6.1.4.1.11344.1.100.2.3 NAME 'radiusClientShortname' SINGLE-VALUE DESC 'Client shortname' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+olcAttributeTypes: ( 1.3.6.1.4.1.11344.1.100.2.4 NAME 'radiusClientVirtualServer' SINGLE-VALUE DESC 'VirtualServer' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+olcAttributeTypes: ( 1.3.6.1.4.1.11344.1.100.2.5 NAME 'radiusClientType' SINGLE-VALUE DESC 'Client Type' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+olcAttributeTypes: ( 1.3.6.1.4.1.11344.1.100.2.6 NAME 'radiusClientRequireMa' SINGLE-VALUE DESC 'Require Message Authenticator' EQUALITY booleanMatch SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' )
+olcAttributeTypes: ( 1.3.6.1.4.1.11344.1.100.2.7 NAME 'radiusClientComment' SINGLE-VALUE DESC 'Client comment' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+olcObjectClasses: ( 1.3.6.1.4.1.11344.1.100.1.1 NAME 'radiusClient' DESC 'radiusClient object class' SUP top STRUCTURAL MUST ( radiusClientIdentifier $ radiusClientSecret ) MAY ( radiusClientShortname $ radiusClientVirtualServer $ radiusClientType $ radiusClientRequireMa $ radiusClientComment ))
diff --git a/doc/schemas/ldap/openldap/freeradius-clients.schema b/doc/schemas/ldap/openldap/freeradius-clients.schema
new file mode 100644
index 0000000..b1fdd66
--- /dev/null
+++ b/doc/schemas/ldap/openldap/freeradius-clients.schema
@@ -0,0 +1,70 @@
+attributetype ( 1.3.6.1.4.1.11344.1.100.2.1
+ NAME 'radiusClientIdentifier'
+ DESC 'Client Identifier'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.1.100.2.2
+ NAME 'radiusClientSecret'
+ DESC 'Client Secret'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.1.100.2.3
+ NAME 'radiusClientShortname'
+ DESC 'Client Shortname'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.1.100.2.4
+ NAME 'radiusClientVirtualServer'
+ DESC 'VirtualServer'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.1.100.2.5
+ NAME 'radiusClientType'
+ DESC 'Client Type'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.1.100.2.6
+ NAME 'radiusClientRequireMa'
+ DESC 'Require Message Authenticator'
+ EQUALITY booleanMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.1.100.2.7
+ NAME 'radiusClientComment'
+ DESC 'Client comment'
+ EQUALITY caseIgnoreMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ )
+
+objectclass ( 1.3.6.1.4.1.11344.1.100.1.1
+ NAME 'radiusClient'
+ DESC 'radiusClient object class'
+ SUP top
+ STRUCTURAL
+ MUST ( radiusClientIdentifier $ radiusClientSecret )
+ MAY ( radiusClientShortname $ radiusClientVirtualServer $ radiusClientType $ radiusClientRequireMa $ radiusClientComment )
+ )
+
diff --git a/doc/schemas/ldap/openldap/freeradius.ldif b/doc/schemas/ldap/openldap/freeradius.ldif
new file mode 100644
index 0000000..44d2cb9
--- /dev/null
+++ b/doc/schemas/ldap/openldap/freeradius.ldif
@@ -0,0 +1,76 @@
+dn: cn=radius,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: radius
+olcAttributeTypes: {0}( 1.3.6.1.4.1.11344.4.3.1.1 NAME 'radiusArapFeatures' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {1}( 1.3.6.1.4.1.11344.4.3.1.2 NAME 'radiusArapSecurity' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {2}( 1.3.6.1.4.1.11344.4.3.1.3 NAME 'radiusArapZoneAccess' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {3}( 1.3.6.1.4.1.11344.4.3.1.44 NAME 'radiusAuthType' DESC 'controlItem: Auth-Type' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {4}( 1.3.6.1.4.1.11344.4.3.1.4 NAME 'radiusCallbackId' DESC 'replyItem: Callback-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {5}( 1.3.6.1.4.1.11344.4.3.1.5 NAME 'radiusCallbackNumber' DESC 'replyItem: Callback-Number' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {6}( 1.3.6.1.4.1.11344.4.3.1.6 NAME 'radiusCalledStationId' DESC 'controlItem: Called-Station-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {7}( 1.3.6.1.4.1.11344.4.3.1.7 NAME 'radiusCallingStationId' DESC 'controlItem: Calling-Station-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {8}( 1.3.6.1.4.1.11344.4.3.1.8 NAME 'radiusClass' DESC 'replyItem: Class' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {9}( 1.3.6.1.4.1.11344.4.3.1.45 NAME 'radiusClientIPAddress' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {10}( 1.3.6.1.4.1.11344.4.3.1.9 NAME 'radiusFilterId' DESC 'replyItem: Filter-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {11}( 1.3.6.1.4.1.11344.4.3.1.10 NAME 'radiusFramedAppleTalkLink' DESC 'replyItem: Framed-AppleTalk-Link' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {12}( 1.3.6.1.4.1.11344.4.3.1.11 NAME 'radiusFramedAppleTalkNetwork' DESC 'replyItem: Framed-AppleTalk-Network' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {13}( 1.3.6.1.4.1.11344.4.3.1.12 NAME 'radiusFramedAppleTalkZone' DESC 'replyItem: Framed-AppleTalk-Zone' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {14}( 1.3.6.1.4.1.11344.4.3.1.13 NAME 'radiusFramedCompression' DESC 'replyItem: Framed-Compression' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {15}( 1.3.6.1.4.1.11344.4.3.1.14 NAME 'radiusFramedIPAddress' DESC 'replyItem: Framed-IP-Address' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {16}( 1.3.6.1.4.1.11344.4.3.1.15 NAME 'radiusFramedIPNetmask' DESC 'replyItem: Framed-IP-Netmask' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {17}( 1.3.6.1.4.1.11344.4.3.1.16 NAME 'radiusFramedIPXNetwork' DESC 'replyItem: Framed-IPX-Network' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {18}( 1.3.6.1.4.1.11344.4.3.1.17 NAME 'radiusFramedMTU' DESC' replyItem: Framed-MTU' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {19}( 1.3.6.1.4.1.11344.4.3.1.18 NAME 'radiusFramedProtocol'DESC 'replyItem: Framed-Protocol' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {20}( 1.3.6.1.4.1.11344.4.3.1.19 NAME 'radiusFramedRoute' DESC 'replyItem: Framed-Route' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {21}( 1.3.6.1.4.1.11344.4.3.1.20 NAME 'radiusFramedRouting' DESC 'replyItem: Framed-Routing' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {22}( 1.3.6.1.4.1.11344.4.3.1.46 NAME 'radiusGroupName' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {23}( 1.3.6.1.4.1.11344.4.3.1.47 NAME 'radiusHint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {24}( 1.3.6.1.4.1.11344.4.3.1.48 NAME 'radiusHuntgroupName' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {25}( 1.3.6.1.4.1.11344.4.3.1.21 NAME 'radiusIdleTimeout' DESC 'replyItem: Idle-Timeout' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {26}( 1.3.6.1.4.1.11344.4.3.1.22 NAME 'radiusLoginIPHost' DESC 'replyItem: Login-IP-Host' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {27}( 1.3.6.1.4.1.11344.4.3.1.23 NAME 'radiusLoginLATGroup' DESC 'replyItem: Login-LAT-Group' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {28}( 1.3.6.1.4.1.11344.4.3.1.24 NAME 'radiusLoginLATNode' DESC 'replyItem: Login-LAT-Node' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {29}( 1.3.6.1.4.1.11344.4.3.1.25 NAME 'radiusLoginLATPort' DESC 'replyItem: Login-LAT-Port' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {30}( 1.3.6.1.4.1.11344.4.3.1.26 NAME 'radiusLoginLATService' DESC 'replyItem: Login-LAT-Service' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {31}( 1.3.6.1.4.1.11344.4.3.1.27 NAME 'radiusLoginService' DESC 'replyItem: Login-Service' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {32}( 1.3.6.1.4.1.11344.4.3.1.28 NAME 'radiusLoginTCPPort' DESC 'replyItem: Login-TCP-Port' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {33}( 1.3.6.1.4.1.11344.4.3.1.29 NAME 'radiusPasswordRetry' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {34}( 1.3.6.1.4.1.11344.4.3.1.30 NAME 'radiusPortLimit' DESC 'replyItem: Port-Limit' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {35}( 1.3.6.1.4.1.11344.4.3.1.49 NAME 'radiusProfileDN' DESC '' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+olcAttributeTypes: {36}( 1.3.6.1.4.1.11344.4.3.1.31 NAME 'radiusPrompt' DESC ''EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {37}( 1.3.6.1.4.1.11344.4.3.1.50 NAME 'radiusProxyToRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {38}( 1.3.6.1.4.1.11344.4.3.1.51 NAME 'radiusReplicateToRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {39}( 1.3.6.1.4.1.11344.4.3.1.52 NAME 'radiusRealm' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE)
+olcAttributeTypes: {40}( 1.3.6.1.4.1.11344.4.3.1.32 NAME 'radiusServiceType' DESC 'replyItem: Service-Type' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {41}( 1.3.6.1.4.1.11344.4.3.1.33 NAME 'radiusSessionTimeout'DESC 'replyItem: Session-Timeout' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {42}( 1.3.6.1.4.1.11344.4.3.1.34 NAME 'radiusTerminationAction' DESC 'replyItem: Termination-Action' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {43}( 1.3.6.1.4.1.11344.4.3.1.35 NAME 'radiusTunnelAssignmentId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26)
+olcAttributeTypes: {44}( 1.3.6.1.4.1.11344.4.3.1.36 NAME 'radiusTunnelMediumType' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {45}( 1.3.6.1.4.1.11344.4.3.1.37 NAME 'radiusTunnelPassword' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {46}( 1.3.6.1.4.1.11344.4.3.1.38 NAME 'radiusTunnelPreference' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {47}( 1.3.6.1.4.1.11344.4.3.1.39 NAME 'radiusTunnelPrivateGroupId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {48}( 1.3.6.1.4.1.11344.4.3.1.40 NAME 'radiusTunnelServerEndpoint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {49}( 1.3.6.1.4.1.11344.4.3.1.41 NAME 'radiusTunnelType' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {50}( 1.3.6.1.4.1.11344.4.3.1.42 NAME 'radiusVSA' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {51}( 1.3.6.1.4.1.11344.4.3.1.43 NAME 'radiusTunnelClientEndpoint' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {52}( 1.3.6.1.4.1.11344.4.3.1.53 NAME 'radiusSimultaneousUse' DESC 'controlItem: Simultaneous-Use' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: {53}( 1.3.6.1.4.1.11344.4.3.1.54 NAME 'radiusLoginTime' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {54}( 1.3.6.1.4.1.11344.4.3.1.55 NAME 'radiusUserCategory' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {55}( 1.3.6.1.4.1.11344.4.3.1.56 NAME 'radiusStripUserName' DESC '' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+olcAttributeTypes: {56}( 1.3.6.1.4.1.11344.4.3.1.57 NAME 'dialupAccess' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {57}( 1.3.6.1.4.1.11344.4.3.1.58 NAME 'radiusExpiration' DESC 'controlItem: Expiration' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {58}( 1.3.6.1.4.1.11344.4.3.1.59 NAME 'radiusAttribute' DESC 'controlItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {59}( 1.3.6.1.4.1.11344.4.3.1.61 NAME 'radiusNASIpAddress' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {60}( 1.3.6.1.4.1.11344.4.3.1.62 NAME 'radiusReplyMessage' DESC 'replyItem: Reply-Message' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {61}( 1.3.6.1.4.1.11344.4.3.1.63 NAME 'radiusControlAttribute' DESC 'controlItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {62}( 1.3.6.1.4.1.11344.4.3.1.64 NAME 'radiusReplyAttribute' DESC 'replyItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {63}( 1.3.6.1.4.1.11344.4.3.1.65 NAME 'radiusRequestAttribute' DESC 'requestItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcObjectClasses: {0}( 1.3.6.1.4.1.11344.4.3.2.1 NAME 'radiusprofile' DESC '' SUP top AUXILIARY MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $ radiusAuthType $
+ radiusCallbackId $ radiusCallbackNumber $radiusCalledStationId $ radiusCallingStationId $ radiusClass $ radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $ radiusFramedAppleTalkNetwork $
+ radiusFramedAppleTalkZone $ radiusFramedCompression $ radiusFramedIPAddress $ radiusFramedIPNetmask $ radiusFramedIPXNetwork $ radiusFramedMTU $radiusFramedProtocol $ radiusAttribute $
+ radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $ radiusGroupName $ radiusHint $ radiusHuntgroupName $ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $ radiusLoginLATPort $
+ radiusLoginLATService $ radiusLoginService $ radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $ radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $ radiusRealm $ radiusReplicateToRealm $
+ radiusServiceType $ radiusSessionTimeout $ radiusStripUserName $ radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDN $ radiusSimultaneousUse $ radiusTunnelAssignmentId $
+ radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $ radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $ radiusTunnelType $ radiusUserCategory $ radiusVSA $ radiusExpiration $
+ dialupAccess $ radiusNASIpAddress $ radiusReplyMessage $ radiusControlAttribute $ radiusReplyAttribute $ radiusRequestAttribute ) )
+olcObjectClasses: {1}( 1.3.6.1.4.1.11344.4.3.2.2 NAME 'radiusObjectProfile' DESC 'A Container Objectclass to be used for creating radius profile object' SUP top STRUCTURAL MUST cn MAY ( uid $ userPassword $ description ) )
diff --git a/doc/schemas/ldap/openldap/freeradius.schema b/doc/schemas/ldap/openldap/freeradius.schema
new file mode 100644
index 0000000..ab11984
--- /dev/null
+++ b/doc/schemas/ldap/openldap/freeradius.schema
@@ -0,0 +1,477 @@
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.1
+ NAME 'radiusArapFeatures'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.2
+ NAME 'radiusArapSecurity'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.3
+ NAME 'radiusArapZoneAccess'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.44
+ NAME 'radiusAuthType'
+ DESC 'controlItem: Auth-Type'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.4
+ NAME 'radiusCallbackId'
+ DESC 'replyItem: Callback-Id'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.5
+ NAME 'radiusCallbackNumber'
+ DESC 'replyItem: Callback-Number'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.6
+ NAME 'radiusCalledStationId'
+ DESC 'controlItem: Called-Station-Id'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.7
+ NAME 'radiusCallingStationId'
+ DESC 'controlItem: Calling-Station-Id'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.8
+ NAME 'radiusClass'
+ DESC 'replyItem: Class'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.45
+ NAME 'radiusClientIPAddress'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.9
+ NAME 'radiusFilterId'
+ DESC 'replyItem: Filter-Id'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.10
+ NAME 'radiusFramedAppleTalkLink'
+ DESC 'replyItem: Framed-AppleTalk-Link'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.11
+ NAME 'radiusFramedAppleTalkNetwork'
+ DESC 'replyItem: Framed-AppleTalk-Network'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.12
+ NAME 'radiusFramedAppleTalkZone'
+ DESC 'replyItem: Framed-AppleTalk-Zone'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.13
+ NAME 'radiusFramedCompression'
+ DESC 'replyItem: Framed-Compression'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.14
+ NAME 'radiusFramedIPAddress'
+ DESC 'replyItem: Framed-IP-Address'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.15
+ NAME 'radiusFramedIPNetmask'
+ DESC 'replyItem: Framed-IP-Netmask'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.16
+ NAME 'radiusFramedIPXNetwork'
+ DESC 'replyItem: Framed-IPX-Network'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.17
+ NAME 'radiusFramedMTU'
+ DESC 'replyItem: Framed-MTU'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.18
+ NAME 'radiusFramedProtocol'
+ DESC 'replyItem: Framed-Protocol'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.19
+ NAME 'radiusFramedRoute'
+ DESC 'replyItem: Framed-Route'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.20
+ NAME 'radiusFramedRouting'
+ DESC 'replyItem: Framed-Routing'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.46
+ NAME 'radiusGroupName'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.47
+ NAME 'radiusHint'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.48
+ NAME 'radiusHuntgroupName'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.21
+ NAME 'radiusIdleTimeout'
+ DESC 'replyItem: Idle-Timeout'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.22
+ NAME 'radiusLoginIPHost'
+ DESC 'replyItem: Login-IP-Host'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.23
+ NAME 'radiusLoginLATGroup'
+ DESC 'replyItem: Login-LAT-Group'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.24
+ NAME 'radiusLoginLATNode'
+ DESC 'replyItem: Login-LAT-Node'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.25
+ NAME 'radiusLoginLATPort'
+ DESC 'replyItem: Login-LAT-Port'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.26
+ NAME 'radiusLoginLATService'
+ DESC 'replyItem: Login-LAT-Service'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.27
+ NAME 'radiusLoginService'
+ DESC 'replyItem: Login-Service'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.28
+ NAME 'radiusLoginTCPPort'
+ DESC 'replyItem: Login-TCP-Port'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.29
+ NAME 'radiusPasswordRetry'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.30
+ NAME 'radiusPortLimit'
+ DESC 'replyItem: Port-Limit'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.49
+ NAME 'radiusProfileDN'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.31
+ NAME 'radiusPrompt'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.50
+ NAME 'radiusProxyToRealm'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.51
+ NAME 'radiusReplicateToRealm'
+ DESC 'control:Replicate-To-Realm'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.52
+ NAME 'radiusRealm'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.32
+ NAME 'radiusServiceType'
+ DESC 'replyItem: Service-Type'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.33
+ NAME 'radiusSessionTimeout'
+ DESC 'replyItem: Session-Timeout'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.34
+ NAME 'radiusTerminationAction'
+ DESC 'replyItem: Termination-Action'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.35
+ NAME 'radiusTunnelAssignmentId'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.36
+ NAME 'radiusTunnelMediumType'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.37
+ NAME 'radiusTunnelPassword'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.38
+ NAME 'radiusTunnelPreference'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.39
+ NAME 'radiusTunnelPrivateGroupId'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.40
+ NAME 'radiusTunnelServerEndpoint'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.41
+ NAME 'radiusTunnelType'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.42
+ NAME 'radiusVSA'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.43
+ NAME 'radiusTunnelClientEndpoint'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.53
+ NAME 'radiusSimultaneousUse'
+ DESC 'controlItem: Simultaneous-Use'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.54
+ NAME 'radiusLoginTime'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.55
+ NAME 'radiusUserCategory'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.56
+ NAME 'radiusStripUserName'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.57
+ NAME 'dialupAccess'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.58
+ NAME 'radiusExpiration'
+ DESC 'controlItem: Expiration'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.59
+ NAME 'radiusAttribute'
+ DESC 'controlItem: $GENERIC$'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.61
+ NAME 'radiusNASIpAddress'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.62
+ NAME 'radiusReplyMessage'
+ DESC 'replyItem: Reply-Message'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.63
+ NAME 'radiusControlAttribute'
+ DESC 'controlItem: $GENERIC$'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.64
+ NAME 'radiusReplyAttribute'
+ DESC 'replyItem: $GENERIC$'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+attributetype ( 1.3.6.1.4.1.11344.4.3.1.65
+ NAME 'radiusRequestAttribute'
+ DESC 'requestItem: $GENERIC$'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ )
+
+objectclass ( 1.3.6.1.4.1.11344.4.3.2.1
+ NAME 'radiusprofile'
+ SUP top
+ AUXILIARY
+ MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $ radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $ radiusCalledStationId $ radiusCallingStationId $ radiusClass $ radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $ radiusFramedAppleTalkNetwork $ radiusFramedAppleTalkZone $ radiusFramedCompression $ radiusFramedIPAddress $ radiusFramedIPNetmask $ radiusFramedIPXNetwork $ radiusFramedMTU $ radiusFramedProtocol $ radiusAttribute $ radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $ radiusGroupName $ radiusHint $ radiusHuntgroupName $ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $ radiusLoginLATPort $ radiusLoginLATService $ radiusLoginService $ radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $ radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $ radiusRealm $ radiusReplicateToRealm $ radiusServiceType $ radiusSessionTimeout $ radiusStripUserName $ radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDN $ radiusSimultaneousUse $ radiusTunnelAssignmentId $ radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $ radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $ radiusTunnelType $ radiusUserCategory $ radiusVSA $ radiusExpiration $ dialupAccess $ radiusNASIpAddress $ radiusReplyMessage $ radiusControlAttribute $ radiusReplyAttribute $ radiusRequestAttribute )
+ )
+
+objectclass ( 1.3.6.1.4.1.11344.4.3.2.2
+ NAME 'radiusObjectProfile'
+ DESC 'A Container Objectclass to be used for creating radius profile object'
+ SUP top
+ STRUCTURAL
+ MUST cn
+ MAY ( uid $ userPassword $ description )
+ )
+
diff --git a/doc/schemas/ldap/samba/README.txt b/doc/schemas/ldap/samba/README.txt
new file mode 100644
index 0000000..6c87461
--- /dev/null
+++ b/doc/schemas/ldap/samba/README.txt
@@ -0,0 +1,11 @@
+Change "dc=samba4,dc=internal" to your LDAP base DN,
+then install with:
+
+ldbmodify -H /usr/local/samba/private/sam.ldb freeradius-attrs.ldif \
+ --option="dsdb:schema update allowed"=true
+ldbmodify -H /usr/local/samba/private/sam.ldb freeradius-classes.ldif \
+ --option="dsdb:schema update allowed"=true
+
+These files were created by scripts/ldap/schema_to_samba.py, then
+split into two because the attributes must be loaded in a separate
+operation to the classes which use them.
diff --git a/doc/schemas/ldap/samba/freeradius-attrs.ldif b/doc/schemas/ldap/samba/freeradius-attrs.ldif
new file mode 100644
index 0000000..6edc614
--- /dev/null
+++ b/doc/schemas/ldap/samba/freeradius-attrs.ldif
@@ -0,0 +1,806 @@
+dn: CN=radiusArapFeatures,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.1
+isSingleValued: TRUE
+cn: radiusArapFeatures
+name: radiusArapFeatures
+lDAPDisplayName: radiusArapFeatures
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusArapSecurity,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.2
+isSingleValued: TRUE
+cn: radiusArapSecurity
+name: radiusArapSecurity
+lDAPDisplayName: radiusArapSecurity
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusArapZoneAccess,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.3
+isSingleValued: TRUE
+cn: radiusArapZoneAccess
+name: radiusArapZoneAccess
+lDAPDisplayName: radiusArapZoneAccess
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusAuthType,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.44
+isSingleValued: TRUE
+cn: radiusAuthType
+name: radiusAuthType
+lDAPDisplayName: radiusAuthType
+description: controlItem: Auth-Type
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusCallbackId,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.4
+isSingleValued: TRUE
+cn: radiusCallbackId
+name: radiusCallbackId
+lDAPDisplayName: radiusCallbackId
+description: replyItem: Callback-Id
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusCallbackNumber,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.5
+isSingleValued: TRUE
+cn: radiusCallbackNumber
+name: radiusCallbackNumber
+lDAPDisplayName: radiusCallbackNumber
+description: replyItem: Callback-Number
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusCalledStationId,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.6
+isSingleValued: TRUE
+cn: radiusCalledStationId
+name: radiusCalledStationId
+lDAPDisplayName: radiusCalledStationId
+description: controlItem: Called-Station-Id
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusCallingStationId,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.7
+isSingleValued: TRUE
+cn: radiusCallingStationId
+name: radiusCallingStationId
+lDAPDisplayName: radiusCallingStationId
+description: controlItem: Calling-Station-Id
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusClass,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.8
+isSingleValued: FALSE
+cn: radiusClass
+name: radiusClass
+lDAPDisplayName: radiusClass
+description: replyItem: Class
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusClientIPAddress,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.45
+isSingleValued: TRUE
+cn: radiusClientIPAddress
+name: radiusClientIPAddress
+lDAPDisplayName: radiusClientIPAddress
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusFilterId,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.9
+isSingleValued: FALSE
+cn: radiusFilterId
+name: radiusFilterId
+lDAPDisplayName: radiusFilterId
+description: replyItem: Filter-Id
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusFramedAppleTalkLink,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.10
+isSingleValued: TRUE
+cn: radiusFramedAppleTalkLink
+name: radiusFramedAppleTalkLink
+lDAPDisplayName: radiusFramedAppleTalkLink
+description: replyItem: Framed-AppleTalk-Link
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusFramedAppleTalkNetwork,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.11
+isSingleValued: FALSE
+cn: radiusFramedAppleTalkNetwork
+name: radiusFramedAppleTalkNetwork
+lDAPDisplayName: radiusFramedAppleTalkNetwork
+description: replyItem: Framed-AppleTalk-Network
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusFramedAppleTalkZone,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.12
+isSingleValued: TRUE
+cn: radiusFramedAppleTalkZone
+name: radiusFramedAppleTalkZone
+lDAPDisplayName: radiusFramedAppleTalkZone
+description: replyItem: Framed-AppleTalk-Zone
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusFramedCompression,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.13
+isSingleValued: FALSE
+cn: radiusFramedCompression
+name: radiusFramedCompression
+lDAPDisplayName: radiusFramedCompression
+description: replyItem: Framed-Compression
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusFramedIPAddress,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.14
+isSingleValued: TRUE
+cn: radiusFramedIPAddress
+name: radiusFramedIPAddress
+lDAPDisplayName: radiusFramedIPAddress
+description: replyItem: Framed-IP-Address
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusFramedIPNetmask,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.15
+isSingleValued: TRUE
+cn: radiusFramedIPNetmask
+name: radiusFramedIPNetmask
+lDAPDisplayName: radiusFramedIPNetmask
+description: replyItem: Framed-IP-Netmask
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusFramedIPXNetwork,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.16
+isSingleValued: TRUE
+cn: radiusFramedIPXNetwork
+name: radiusFramedIPXNetwork
+lDAPDisplayName: radiusFramedIPXNetwork
+description: replyItem: Framed-IPX-Network
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusFramedMTU,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.17
+isSingleValued: TRUE
+cn: radiusFramedMTU
+name: radiusFramedMTU
+lDAPDisplayName: radiusFramedMTU
+description: replyItem: Framed-MTU
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusFramedProtocol,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.18
+isSingleValued: TRUE
+cn: radiusFramedProtocol
+name: radiusFramedProtocol
+lDAPDisplayName: radiusFramedProtocol
+description: replyItem: Framed-Protocol
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusFramedRoute,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.19
+isSingleValued: FALSE
+cn: radiusFramedRoute
+name: radiusFramedRoute
+lDAPDisplayName: radiusFramedRoute
+description: replyItem: Framed-Route
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusFramedRouting,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.20
+isSingleValued: TRUE
+cn: radiusFramedRouting
+name: radiusFramedRouting
+lDAPDisplayName: radiusFramedRouting
+description: replyItem: Framed-Routing
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusGroupName,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.46
+isSingleValued: FALSE
+cn: radiusGroupName
+name: radiusGroupName
+lDAPDisplayName: radiusGroupName
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusHint,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.47
+isSingleValued: TRUE
+cn: radiusHint
+name: radiusHint
+lDAPDisplayName: radiusHint
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusHuntgroupName,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.48
+isSingleValued: FALSE
+cn: radiusHuntgroupName
+name: radiusHuntgroupName
+lDAPDisplayName: radiusHuntgroupName
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusIdleTimeout,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.21
+isSingleValued: TRUE
+cn: radiusIdleTimeout
+name: radiusIdleTimeout
+lDAPDisplayName: radiusIdleTimeout
+description: replyItem: Idle-Timeout
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusLoginIPHost,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.22
+isSingleValued: FALSE
+cn: radiusLoginIPHost
+name: radiusLoginIPHost
+lDAPDisplayName: radiusLoginIPHost
+description: replyItem: Login-IP-Host
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusLoginLATGroup,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.23
+isSingleValued: TRUE
+cn: radiusLoginLATGroup
+name: radiusLoginLATGroup
+lDAPDisplayName: radiusLoginLATGroup
+description: replyItem: Login-LAT-Group
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusLoginLATNode,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.24
+isSingleValued: TRUE
+cn: radiusLoginLATNode
+name: radiusLoginLATNode
+lDAPDisplayName: radiusLoginLATNode
+description: replyItem: Login-LAT-Node
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusLoginLATPort,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.25
+isSingleValued: TRUE
+cn: radiusLoginLATPort
+name: radiusLoginLATPort
+lDAPDisplayName: radiusLoginLATPort
+description: replyItem: Login-LAT-Port
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusLoginLATService,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.26
+isSingleValued: TRUE
+cn: radiusLoginLATService
+name: radiusLoginLATService
+lDAPDisplayName: radiusLoginLATService
+description: replyItem: Login-LAT-Service
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusLoginService,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.27
+isSingleValued: TRUE
+cn: radiusLoginService
+name: radiusLoginService
+lDAPDisplayName: radiusLoginService
+description: replyItem: Login-Service
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusLoginTCPPort,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.28
+isSingleValued: TRUE
+cn: radiusLoginTCPPort
+name: radiusLoginTCPPort
+lDAPDisplayName: radiusLoginTCPPort
+description: replyItem: Login-TCP-Port
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusPasswordRetry,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.29
+isSingleValued: TRUE
+cn: radiusPasswordRetry
+name: radiusPasswordRetry
+lDAPDisplayName: radiusPasswordRetry
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusPortLimit,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.30
+isSingleValued: TRUE
+cn: radiusPortLimit
+name: radiusPortLimit
+lDAPDisplayName: radiusPortLimit
+description: replyItem: Port-Limit
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusProfileDN,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.49
+isSingleValued: FALSE
+cn: radiusProfileDN
+name: radiusProfileDN
+lDAPDisplayName: radiusProfileDN
+attributeSyntax: 2.5.5.1
+oMSyntax: 127
+
+dn: CN=radiusPrompt,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.31
+isSingleValued: TRUE
+cn: radiusPrompt
+name: radiusPrompt
+lDAPDisplayName: radiusPrompt
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusProxyToRealm,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.50
+isSingleValued: TRUE
+cn: radiusProxyToRealm
+name: radiusProxyToRealm
+lDAPDisplayName: radiusProxyToRealm
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusReplicateToRealm,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.51
+isSingleValued: TRUE
+cn: radiusReplicateToRealm
+name: radiusReplicateToRealm
+lDAPDisplayName: radiusReplicateToRealm
+description: control:Replicate-To-Realm
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusRealm,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.52
+isSingleValued: TRUE
+cn: radiusRealm
+name: radiusRealm
+lDAPDisplayName: radiusRealm
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusServiceType,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.32
+isSingleValued: TRUE
+cn: radiusServiceType
+name: radiusServiceType
+lDAPDisplayName: radiusServiceType
+description: replyItem: Service-Type
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusSessionTimeout,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.33
+isSingleValued: TRUE
+cn: radiusSessionTimeout
+name: radiusSessionTimeout
+lDAPDisplayName: radiusSessionTimeout
+description: replyItem: Session-Timeout
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusTerminationAction,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.34
+isSingleValued: TRUE
+cn: radiusTerminationAction
+name: radiusTerminationAction
+lDAPDisplayName: radiusTerminationAction
+description: replyItem: Termination-Action
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusTunnelAssignmentId,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.35
+isSingleValued: FALSE
+cn: radiusTunnelAssignmentId
+name: radiusTunnelAssignmentId
+lDAPDisplayName: radiusTunnelAssignmentId
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusTunnelMediumType,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.36
+isSingleValued: FALSE
+cn: radiusTunnelMediumType
+name: radiusTunnelMediumType
+lDAPDisplayName: radiusTunnelMediumType
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusTunnelPassword,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.37
+isSingleValued: TRUE
+cn: radiusTunnelPassword
+name: radiusTunnelPassword
+lDAPDisplayName: radiusTunnelPassword
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusTunnelPreference,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.38
+isSingleValued: FALSE
+cn: radiusTunnelPreference
+name: radiusTunnelPreference
+lDAPDisplayName: radiusTunnelPreference
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusTunnelPrivateGroupId,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.39
+isSingleValued: FALSE
+cn: radiusTunnelPrivateGroupId
+name: radiusTunnelPrivateGroupId
+lDAPDisplayName: radiusTunnelPrivateGroupId
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusTunnelServerEndpoint,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.40
+isSingleValued: FALSE
+cn: radiusTunnelServerEndpoint
+name: radiusTunnelServerEndpoint
+lDAPDisplayName: radiusTunnelServerEndpoint
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusTunnelType,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.41
+isSingleValued: FALSE
+cn: radiusTunnelType
+name: radiusTunnelType
+lDAPDisplayName: radiusTunnelType
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusVSA,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.42
+isSingleValued: FALSE
+cn: radiusVSA
+name: radiusVSA
+lDAPDisplayName: radiusVSA
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusTunnelClientEndpoint,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.43
+isSingleValued: FALSE
+cn: radiusTunnelClientEndpoint
+name: radiusTunnelClientEndpoint
+lDAPDisplayName: radiusTunnelClientEndpoint
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusSimultaneousUse,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.53
+isSingleValued: TRUE
+cn: radiusSimultaneousUse
+name: radiusSimultaneousUse
+lDAPDisplayName: radiusSimultaneousUse
+description: controlItem: Simultaneous-Use
+attributeSyntax: 2.5.5.9
+oMSyntax: 10
+
+dn: CN=radiusLoginTime,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.54
+isSingleValued: TRUE
+cn: radiusLoginTime
+name: radiusLoginTime
+lDAPDisplayName: radiusLoginTime
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusUserCategory,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.55
+isSingleValued: TRUE
+cn: radiusUserCategory
+name: radiusUserCategory
+lDAPDisplayName: radiusUserCategory
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusStripUserName,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.56
+isSingleValued: TRUE
+cn: radiusStripUserName
+name: radiusStripUserName
+lDAPDisplayName: radiusStripUserName
+attributeSyntax: 2.5.5.8
+oMSyntax: 1
+
+dn: CN=dialupAccess,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.57
+isSingleValued: TRUE
+cn: dialupAccess
+name: dialupAccess
+lDAPDisplayName: dialupAccess
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusExpiration,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.58
+isSingleValued: TRUE
+cn: radiusExpiration
+name: radiusExpiration
+lDAPDisplayName: radiusExpiration
+description: controlItem: Expiration
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusAttribute,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.59
+isSingleValued: FALSE
+cn: radiusAttribute
+name: radiusAttribute
+lDAPDisplayName: radiusAttribute
+description: controlItem: $GENERIC$
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusNASIpAddress,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.61
+isSingleValued: TRUE
+cn: radiusNASIpAddress
+name: radiusNASIpAddress
+lDAPDisplayName: radiusNASIpAddress
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusReplyMessage,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.62
+isSingleValued: FALSE
+cn: radiusReplyMessage
+name: radiusReplyMessage
+lDAPDisplayName: radiusReplyMessage
+description: replyItem: Reply-Message
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusControlAttribute,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.63
+isSingleValued: FALSE
+cn: radiusControlAttribute
+name: radiusControlAttribute
+lDAPDisplayName: radiusControlAttribute
+description: controlItem: $GENERIC$
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusReplyAttribute,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.64
+isSingleValued: FALSE
+cn: radiusReplyAttribute
+name: radiusReplyAttribute
+lDAPDisplayName: radiusReplyAttribute
+description: replyItem: $GENERIC$
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
+dn: CN=radiusRequestAttribute,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.4.3.1.65
+isSingleValued: FALSE
+cn: radiusRequestAttribute
+name: radiusRequestAttribute
+lDAPDisplayName: radiusRequestAttribute
+description: requestItem: $GENERIC$
+attributeSyntax: 2.5.5.5
+oMSyntax: 22
+
diff --git a/doc/schemas/ldap/samba/freeradius-classes.ldif b/doc/schemas/ldap/samba/freeradius-classes.ldif
new file mode 100644
index 0000000..455069e
--- /dev/null
+++ b/doc/schemas/ldap/samba/freeradius-classes.ldif
@@ -0,0 +1,91 @@
+dn: CN=radiusprofile,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: classSchema
+governsID: 1.3.6.1.4.1.11344.4.3.2.1
+cn: radiusprofile
+name: radiusprofile
+lDAPDisplayName: radiusprofile
+subClassOf: top
+objectClassCategory: 3
+mayContain: radiusArapFeatures
+mayContain: radiusArapSecurity
+mayContain: radiusArapZoneAccess
+mayContain: radiusAuthType
+mayContain: radiusCallbackId
+mayContain: radiusCallbackNumber
+mayContain: radiusCalledStationId
+mayContain: radiusCallingStationId
+mayContain: radiusClass
+mayContain: radiusClientIPAddress
+mayContain: radiusFilterId
+mayContain: radiusFramedAppleTalkLink
+mayContain: radiusFramedAppleTalkNetwork
+mayContain: radiusFramedAppleTalkZone
+mayContain: radiusFramedCompression
+mayContain: radiusFramedIPAddress
+mayContain: radiusFramedIPNetmask
+mayContain: radiusFramedIPXNetwork
+mayContain: radiusFramedMTU
+mayContain: radiusFramedProtocol
+mayContain: radiusAttribute
+mayContain: radiusFramedRoute
+mayContain: radiusFramedRouting
+mayContain: radiusIdleTimeout
+mayContain: radiusGroupName
+mayContain: radiusHint
+mayContain: radiusHuntgroupName
+mayContain: radiusLoginIPHost
+mayContain: radiusLoginLATGroup
+mayContain: radiusLoginLATNode
+mayContain: radiusLoginLATPort
+mayContain: radiusLoginLATService
+mayContain: radiusLoginService
+mayContain: radiusLoginTCPPort
+mayContain: radiusLoginTime
+mayContain: radiusPasswordRetry
+mayContain: radiusPortLimit
+mayContain: radiusPrompt
+mayContain: radiusProxyToRealm
+mayContain: radiusRealm
+mayContain: radiusReplicateToRealm
+mayContain: radiusServiceType
+mayContain: radiusSessionTimeout
+mayContain: radiusStripUserName
+mayContain: radiusTerminationAction
+mayContain: radiusTunnelClientEndpoint
+mayContain: radiusProfileDN
+mayContain: radiusSimultaneousUse
+mayContain: radiusTunnelAssignmentId
+mayContain: radiusTunnelMediumType
+mayContain: radiusTunnelPassword
+mayContain: radiusTunnelPreference
+mayContain: radiusTunnelPrivateGroupId
+mayContain: radiusTunnelServerEndpoint
+mayContain: radiusTunnelType
+mayContain: radiusUserCategory
+mayContain: radiusVSA
+mayContain: radiusExpiration
+mayContain: dialupAccess
+mayContain: radiusNASIpAddress
+mayContain: radiusReplyMessage
+mayContain: radiusControlAttribute
+mayContain: radiusReplyAttribute
+mayContain: radiusRequestAttribute
+
+dn: CN=radiusObjectProfile,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: classSchema
+governsID: 1.3.6.1.4.1.11344.4.3.2.2
+cn: radiusObjectProfile
+name: radiusObjectProfile
+lDAPDisplayName: radiusObjectProfile
+description: A Container Objectclass to be used for creating radius profile object
+subClassOf: top
+objectClassCategory: 1
+mustContain: cn
+mayContain: uid
+mayContain: userPassword
+mayContain: description
+
diff --git a/doc/schemas/ldap/samba/freeradius-clients-attrs.ldif b/doc/schemas/ldap/samba/freeradius-clients-attrs.ldif
new file mode 100644
index 0000000..c523da4
--- /dev/null
+++ b/doc/schemas/ldap/samba/freeradius-clients-attrs.ldif
@@ -0,0 +1,91 @@
+dn: CN=radiusClientIdentifier,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.1.100.2.1
+isSingleValued: TRUE
+cn: radiusClientIdentifier
+name: radiusClientIdentifier
+lDAPDisplayName: radiusClientIdentifier
+description: Client Identifier
+attributeSyntax: 2.5.5.3
+oMSyntax: 27
+
+dn: CN=radiusClientSecret,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.1.100.2.2
+isSingleValued: TRUE
+cn: radiusClientSecret
+name: radiusClientSecret
+lDAPDisplayName: radiusClientSecret
+description: Client Secret
+attributeSyntax: 2.5.5.3
+oMSyntax: 27
+
+dn: CN=radiusClientShortname,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.1.100.2.3
+isSingleValued: TRUE
+cn: radiusClientShortname
+name: radiusClientShortname
+lDAPDisplayName: radiusClientShortname
+description: Client Shortname
+attributeSyntax: 2.5.5.3
+oMSyntax: 27
+
+dn: CN=radiusClientVirtualServer,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.1.100.2.4
+isSingleValued: TRUE
+cn: radiusClientVirtualServer
+name: radiusClientVirtualServer
+lDAPDisplayName: radiusClientVirtualServer
+description: VirtualServer
+attributeSyntax: 2.5.5.3
+oMSyntax: 27
+
+dn: CN=radiusClientType,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.1.100.2.5
+isSingleValued: TRUE
+cn: radiusClientType
+name: radiusClientType
+lDAPDisplayName: radiusClientType
+description: Client Type
+attributeSyntax: 2.5.5.3
+oMSyntax: 27
+
+dn: CN=radiusClientRequireMa,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.1.100.2.6
+isSingleValued: TRUE
+cn: radiusClientRequireMa
+name: radiusClientRequireMa
+lDAPDisplayName: radiusClientRequireMa
+description: Require Message Authenticator
+attributeSyntax: 2.5.5.8
+oMSyntax: 1
+
+dn: CN=radiusClientComment,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.11344.1.100.2.7
+isSingleValued: TRUE
+cn: radiusClientComment
+name: radiusClientComment
+lDAPDisplayName: radiusClientComment
+description: Client comment
+attributeSyntax: 2.5.5.3
+oMSyntax: 27
+
diff --git a/doc/schemas/ldap/samba/freeradius-clients-classes.ldif b/doc/schemas/ldap/samba/freeradius-clients-classes.ldif
new file mode 100644
index 0000000..24eade2
--- /dev/null
+++ b/doc/schemas/ldap/samba/freeradius-clients-classes.ldif
@@ -0,0 +1,19 @@
+dn: CN=radiusClient,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: add
+objectClass: top
+objectClass: classSchema
+governsID: 1.3.6.1.4.1.11344.1.100.1.1
+cn: radiusClient
+name: radiusClient
+lDAPDisplayName: radiusClient
+description: radiusClient object class
+subClassOf: top
+objectClassCategory: 1
+mustContain: radiusClientIdentifier
+mustContain: radiusClientSecret
+mayContain: radiusClientShortname
+mayContain: radiusClientVirtualServer
+mayContain: radiusClientType
+mayContain: radiusClientRequireMa
+mayContain: radiusClientComment
+
diff --git a/doc/schemas/ldap/samba/freeradius-user.ldif b/doc/schemas/ldap/samba/freeradius-user.ldif
new file mode 100644
index 0000000..d252a7c
--- /dev/null
+++ b/doc/schemas/ldap/samba/freeradius-user.ldif
@@ -0,0 +1,10 @@
+# Use this if you want to be able to apply radiusprofile
+# Auxiliary class directly to Users in the directory.
+# Install with:
+# ldbmodify -H /usr/local/samba/private/sam.ldb \
+# user.ldif \
+# --option="dsdb:schema update allowed"=true
+dn: CN=User,CN=Schema,CN=Configuration,dc=samba4,dc=internal
+changetype: modify
+add: auxiliaryClass
+auxiliaryClass: radiusprofile
diff --git a/doc/schemas/logstash/README b/doc/schemas/logstash/README
new file mode 100644
index 0000000..2f36eb6
--- /dev/null
+++ b/doc/schemas/logstash/README
@@ -0,0 +1,95 @@
+Example configuration for logstash/elasticsearch
+================================================
+
+So you've got all these RADIUS logs, but how do you analyse them? What is the
+easiest way to query the logs, find out when a client connected or disconnected,
+or view the top ten clients logging into the system over the last six hours?
+
+The elastic stack is designed and built to do just that. elasticsearch is a
+search engine; logstash is commonly used to feed data in, and kibana the web
+interface to query the logs in near real time.
+
+Installing the elastic stack is beyond the scope of this document, but can be done
+in a short amount of time by any competent sysadmin. Then comes getting the
+logs in.
+
+This directory contains the following files as a starting point for feeding
+RADIUS logs into elasticsearch via logstash, then sample dashboards for Kibana
+to explore the data.
+
+Files
+-----
+
+Please note that all files should be reviewed before use to determine if they
+are suitable for your configuration/system, especially if you are integrating
+this into an existing logstash/elasticsearch setup.
+
+radius-mapping.sh
+
+ Each elasticsearch index needs a mapping to describe how fields are stored.
+ If one is not provided then all is not lost as elasticsearch will build one
+ on the fly. However, this may not be optimal, especially for RADIUS data, as
+ all fields will be analyzed making some visualisations hard or impossible
+ (such as showing top N clients).
+
+ This shell script (which just runs curl) pushes a template mapping into the
+ elasticsearch cluster.
+
+logstash-radius.conf
+
+ A sample configuration file for logstash that parses RADIUS 'detail' files.
+ It processes these by joining each record onto one line, then splitting the
+ tab-delimited key-value pairs out. Some additional data is then extracted
+ from certain key attributes.
+
+ The logstash config will need to be edited at least to set the input method:
+ for experimentation the given input (file) may be used. If logstash is running
+ on the RADIUS server itself then this example input may be appropriate,
+ otherwise a different input such as log-courier or filebeat may be better to
+ get the data over the network to logstash.
+
+ It would be best to use an input method that can join the multiple lines of
+ the detail file together and feed them to logstash as a single entry, rather
+ than using the logstash multiline codec.
+
+log-courier.conf
+
+ An example configuration for the log-courier feeder.
+
+kibana4-dashboard.json
+
+ Basic RADIUS dashboard (for Kibana 4 to Kibana 6).
+
+ To import the dashboard first create a new index called "radius-*" in
+ Settings/Indices. Then go to Kibana's Settings page, "Objects" and "Import".
+ Once imported open the "RADIUS detail" dashboard.
+
+
+Example usage
+-------------
+
+Install mapping (only needs to be done once):
+
+ $ ./radius-mapping.sh
+
+Edit logstash-radius.conf to point to the correct file, then feed a detail file
+in:
+
+ # /usr/share/logstash/bin/logstash --path.settings=/etc/logstash -f logstash-radius.conf
+
+To view debug output, append `--log.level=debug`.
+
+
+See also
+--------
+
+elasticsearch web site: http://www.elastic.co/
+
+The configuration examples presented here have been tested with the
+following software versions:
+
+ elasticsearch 6.7.0
+ logstash 6.7.0
+ kibana 6.7.0
+ kibana 5.1.2
+ kibana 4.1.11
diff --git a/doc/schemas/logstash/kibana4-dashboard.json b/doc/schemas/logstash/kibana4-dashboard.json
new file mode 100644
index 0000000..6c379df
--- /dev/null
+++ b/doc/schemas/logstash/kibana4-dashboard.json
@@ -0,0 +1,123 @@
+[
+ {
+ "_id": "RADIUS-data",
+ "_type": "search",
+ "_source": {
+ "title": "RADIUS data",
+ "description": "",
+ "hits": 0,
+ "columns": [
+ "User-Name",
+ "Calling-Station-Id",
+ "Called-Station-Id",
+ "Framed-IP-Address",
+ "NAS-Identifier"
+ ],
+ "sort": [
+ "@timestamp",
+ "desc"
+ ],
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"index\":\"radius-*\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fields\":{\"*\":{}},\"fragment_size\":2147483647},\"filter\":[]}"
+ }
+ }
+ },
+ {
+ "_id": "RADIUS-detail",
+ "_type": "dashboard",
+ "_source": {
+ "title": "RADIUS detail",
+ "hits": 0,
+ "description": "",
+ "panelsJSON": "[{\"col\":5,\"id\":\"RADIUS-unique-User-Name-by-day\",\"row\":1,\"size_x\":4,\"size_y\":4,\"type\":\"visualization\"},{\"col\":1,\"columns\":[\"User-Name\",\"Calling-Station-Id\",\"Called-Station-Id\",\"Framed-IP-Address\",\"NAS-Identifier\"],\"id\":\"RADIUS-data\",\"row\":5,\"size_x\":8,\"size_y\":4,\"sort\":[\"@timestamp\",\"desc\"],\"type\":\"search\"},{\"col\":1,\"id\":\"RADIUS-accounting-packets-histogram\",\"row\":1,\"size_x\":4,\"size_y\":4,\"type\":\"visualization\"},{\"col\":9,\"id\":\"RADIUS-table-topN-data-transferred-by-User-Name\",\"row\":1,\"size_x\":4,\"size_y\":4,\"type\":\"visualization\"},{\"id\":\"RADIUS-Sessions-per-NAS\",\"type\":\"visualization\",\"size_x\":4,\"size_y\":4,\"col\":9,\"row\":5}]",
+ "version": 1,
+ "timeRestore": true,
+ "timeTo": "now",
+ "timeFrom": "now-7d",
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"filter\":[{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}}}]}"
+ }
+ }
+ },
+ {
+ "_id": "RADIUS-Accounting-Start-data",
+ "_type": "search",
+ "_source": {
+ "title": "RADIUS Accounting-Start data",
+ "description": "",
+ "hits": 0,
+ "columns": [
+ "User-Name",
+ "Calling-Station-Id",
+ "Called-Station-Id",
+ "Framed-IP-Address",
+ "NAS-Identifier"
+ ],
+ "sort": [
+ "@timestamp",
+ "desc"
+ ],
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"index\":\"radius-*\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"highlight\":{\"pre_tags\":[\"@kibana-highlighted-field@\"],\"post_tags\":[\"@/kibana-highlighted-field@\"],\"fields\":{\"*\":{}},\"fragment_size\":2147483647},\"filter\":[{\"meta\":{\"negate\":false,\"index\":\"radius-*\",\"key\":\"Acct-Status-Type\",\"value\":\"Start\",\"disabled\":false},\"query\":{\"match\":{\"Acct-Status-Type\":{\"query\":\"Start\",\"type\":\"phrase\"}}}}]}"
+ }
+ }
+ },
+ {
+ "_id": "RADIUS-unique-User-Name-by-day",
+ "_type": "visualization",
+ "_source": {
+ "title": "RADIUS unique User-Name by day",
+ "visState": "{\n \"type\": \"histogram\",\n \"params\": {\n \"shareYAxis\": true,\n \"addTooltip\": true,\n \"addLegend\": true,\n \"scale\": \"linear\",\n \"mode\": \"stacked\",\n \"times\": [],\n \"addTimeMarker\": false,\n \"defaultYExtents\": false,\n \"setYExtents\": false,\n \"yAxis\": {}\n },\n \"aggs\": [\n {\n \"id\": \"1\",\n \"type\": \"cardinality\",\n \"schema\": \"metric\",\n \"params\": {\n \"field\": \"User-Name\"\n }\n },\n {\n \"id\": \"2\",\n \"type\": \"date_histogram\",\n \"schema\": \"segment\",\n \"params\": {\n \"field\": \"@timestamp\",\n \"interval\": \"d\",\n \"customInterval\": \"2h\",\n \"min_doc_count\": 1,\n \"extended_bounds\": {}\n }\n },\n {\n \"id\": \"3\",\n \"type\": \"terms\",\n \"schema\": \"group\",\n \"params\": {\n \"field\": \"User-Name\",\n \"size\": 50,\n \"order\": \"desc\",\n \"orderBy\": \"1\"\n }\n }\n ],\n \"listeners\": {}\n}",
+ "description": "",
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\n \"filter\": []\n}"
+ },
+ "savedSearchId": "RADIUS-data"
+ }
+ },
+ {
+ "_id": "RADIUS-accounting-packets-histogram",
+ "_type": "visualization",
+ "_source": {
+ "title": "RADIUS accounting packets histogram",
+ "visState": "{\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"Acct-Status-Type\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}],\"listeners\":{}}",
+ "description": "",
+ "savedSearchId": "RADIUS-data",
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"filter\":[]}"
+ }
+ }
+ },
+ {
+ "_id": "RADIUS-table-topN-data-transferred-by-User-Name",
+ "_type": "visualization",
+ "_source": {
+ "title": "RADIUS table topN data transferred by User-Name",
+ "visState": "{\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false},\"aggs\":[{\"id\":\"1\",\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"Acct-Output-Octets_long\"}},{\"id\":\"2\",\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"User-Name\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\"}},{\"id\":\"3\",\"type\":\"max\",\"schema\":\"metric\",\"params\":{\"field\":\"Acct-Input-Octets_long\"}}],\"listeners\":{}}",
+ "description": "",
+ "savedSearchId": "RADIUS-data",
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"filter\":[]}"
+ }
+ }
+ },
+ {
+ "_id": "RADIUS-Sessions-per-NAS",
+ "_type": "visualization",
+ "_source": {
+ "title": "RADIUS Sessions per NAS",
+ "visState": "{\"type\":\"pie\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"isDonut\":false},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"NAS-Identifier\",\"size\":20,\"order\":\"desc\",\"orderBy\":\"1\"}}],\"listeners\":{}}",
+ "description": "",
+ "savedSearchId": "RADIUS-Accounting-Start-data",
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"filter\":[]}"
+ }
+ }
+ }
+]
diff --git a/doc/schemas/logstash/log-courier.conf b/doc/schemas/logstash/log-courier.conf
new file mode 100644
index 0000000..20d106f
--- /dev/null
+++ b/doc/schemas/logstash/log-courier.conf
@@ -0,0 +1,56 @@
+# Example log-courier configuration file for RADIUS detail files.
+#
+# This has been tested with log-courier version 2.0.4
+#
+{
+ "general": {
+ "persist directory": "/var/lib/log-courier",
+ "log syslog": true,
+ "log stdout": false
+ },
+
+ "network": {
+ "transport": "tcp",
+
+ # Servers to connect to.
+ #
+ "servers": [
+ "logstash1.example:5140",
+ "logstash2.example:5140"
+ ]
+ },
+
+ "files": [
+ {
+ # Match RADIUS detail files, but not anything that has
+ # been gzipped.
+ #
+ "paths": [ "/var/log/radius/radacct/*/detail-????????" ],
+
+ # Add a type:"radiusdetail" field to the data so that
+ # logstash can tell what type of data this is (in case
+ # log-courier is being used for other data as well).
+ #
+ "fields": {
+ "type": "radiusdetail"
+ },
+
+ # Stop watching a file if nothing has been written in 12h.
+ #
+ "dead time": "12h",
+
+ # Process multilines. If this is being used then the
+ # "multiline" section should be commented out from the
+ # logstash configuration. Logstash can then also be run
+ # with multiple workers (using -w).
+ #
+ "codecs": [
+ {
+ "name": "multiline",
+ "patterns": [ "^[A-Z\t]" ],
+ "what": "next"
+ }
+ ]
+ }
+ ]
+}
diff --git a/doc/schemas/logstash/logstash-radius.conf b/doc/schemas/logstash/logstash-radius.conf
new file mode 100644
index 0000000..f473179
--- /dev/null
+++ b/doc/schemas/logstash/logstash-radius.conf
@@ -0,0 +1,256 @@
+# logstash configuration to process RADIUS detail files
+#
+# Matthew Newton
+# April 2019
+#
+# This config has been tested with logstash version 6.7.0
+#
+# RADIUS "detail" files are textual representations of the RADIUS
+# packets, and are written to disk by e.g. FreeRADIUS. They look
+# something like the following, with the timestamp on the first
+# line then all attributes/values tab-indented.
+#
+# Tue Mar 10 15:32:24 2015
+# Packet-Type = Access-Request
+# User-Name = "test@example.com"
+# Calling-Station-Id = "01-02-03-04-05-06"
+# Called-Station-Id = "aa-bb-cc-dd-ee-ff:myssid"
+# NAS-Port = 10
+# NAS-IP-Address = 10.9.0.4
+# NAS-Identifier = "Wireless-Controller-1"
+# Service-Type = Framed-User
+# NAS-Port-Type = Wireless-802.11
+#
+
+
+
+# Example input - read data from a file. For example, to read in a
+# detail file with this input you could use:
+#
+# # /usr/share/logstash/bin/logstash --path.settings=/etc/logstash -f logstash-radius.conf --log.level=debug
+#
+
+input {
+ file {
+ path => "/var/log/radius/radacct/*/detail-*"
+ exclude => "*.gz"
+
+ # Note when testing that logstash will remember where
+ # it got to and continue from there.
+ start_position => "beginning"
+
+ # Set the type, for below.
+ type => radiusdetail
+
+ # It is preferable to use a log feeder that can join
+ # multiple lines together, rather than using multiline
+ # here. For an example, see the log-courier
+ # configuration in this directory.
+
+ # If you didn't read the above, go back and read it again.
+
+ # If that is not possible you may be able to use the
+ # following section. Note that if you are using the
+ # "stdin" input, the file is chunked into 16k blobs,
+ # so every 16k a detail record is likely to be chopped
+ # in half. If you are using the "file" input (as in this
+ # example), the blank links between records are not
+ # passed through so the regex here has to be aware of
+ # that. Basically, do multiline as early as possible
+ # in your log feeder client not here and you'll avoid
+ # most issues that are likely to come up.
+
+ codec => multiline {
+ pattern => "^\t"
+ negate => false
+ what => "previous"
+ }
+
+ # If you really want to use the "stdin" input, this
+ # will work better, but be aware of the comments
+ # above.
+
+ #codec => multiline {
+ # pattern => "^[A-Z\t]"
+ # negate => false
+ # what => "next"
+ #}
+ }
+}
+
+# Moving into production will likely need something more reliable.
+# There are many input methods, an example here using log-courier
+# (which supports client-site multiline processing and does not
+# lose log events if logstash is restarted). You could also
+# investigate e.g. filebeat from Elastic.
+
+# input {
+# courier {
+# port => 5140
+# transport => "tcp"
+#
+# # Don't set the type here, as it's set in the
+# # log-courier config instead.
+# #type => radiusdetail
+# }
+# }
+
+
+
+# Filter stage. Here we take the raw logs and process them into
+# something structured ready to index. Each attribute is stored as
+# a separate field in the output document.
+
+filter {
+
+ if [type] == "radiusdetail" {
+
+ # Pull off the timestamp at the start of the
+ # detail record. Note there may be additional data
+ # after it that has been added by the local admin,
+ # so stop at a newline OR a tab.
+
+ grok {
+ match => [ "message", "^(?<timestamp>[^\n\t]+)[\n\t]" ]
+ }
+
+ # Create the @timestamp field.
+
+ date {
+ match => [ "timestamp", "EEE MMM dd HH:mm:ss yyyy",
+ "EEE MMM d HH:mm:ss yyyy" ]
+ }
+
+ # Split the attributes and values into fields.
+ # This is the bulk of processing that adds all of
+ # the RADIUS attributes as elasticsearch fields.
+
+ # Note issue https://github.com/logstash-plugins/logstash-filter-kv/issues/10
+ # currently means that all spaces will be stripped
+ # from all fields. If this is a problem, adjust the
+ # trim setting.
+
+ kv {
+ field_split => "\n"
+ source => "message"
+ trim_value => "\" "
+ trim_key => "\t "
+ }
+
+ # Now we try and add some useful additional
+ # information. If certain fields can be broken
+ # down into components then do that here and add
+ # the data as sub-fields. For example,
+ # Called-Station-Id might be able to be broken
+ # down to Called-Station-Id_mac and Called-Station-Id_ssid
+ # on some wireless systems, or to _ip and _port
+ # with a VPN.
+
+ # Multiple calls to grok otherwise it can stop
+ # processing once it has matched one field, but
+ # e.g. you want to pull both IP and port out of
+ # the same field in two different regex's.
+
+ # Pull out some IP addresses as field_ip:
+
+ grok {
+ break_on_match => false
+ tag_on_failure => []
+ match => [
+ "Framed-IP-Address", "^(?<Framed-IP-Address_ip>\d+\.\d+\.\d+\.\d+$)",
+ "NAS-IP-Address", "^(?<NAS-IP-Address_ip>\d+\.\d+\.\d+\.\d+$)",
+ "Calling-Station-Id", "^(?<Calling-Station-Id_ip>\d+\.\d+\.\d+\.\d+)",
+ "Called-Station-Id", "^(?<Called-Station-Id_ip>\d+\.\d+\.\d+\.\d+)"
+ ]
+ }
+
+ # Split User-Name, Operator-Name, and pull out
+ # some IP ports if they are there:
+
+ grok {
+ break_on_match => false
+ tag_on_failure => []
+ match => [
+ "User-Name", "^(?<User-Name_username>[^@]+)?(?:@(?<User-Name_realm>[^@]+))$",
+ "Operator-Name", "^(?<Operator-Name_id>.)(?<Operator-Name_value>.+)$",
+
+ "Calling-Station-Id", "\[(?<Calling-Station-Id_port>\d+)\]$",
+ "Called-Station-Id", "\[(?<Called-Station-Id_port>\d+)\]$"
+ ]
+ }
+
+ # Extract MAC addresses (and SSIDs if there).
+ # MAC address matching here is lazy, but should be
+ # good enough.
+
+ grok {
+ break_on_match => false
+ tag_on_failure => []
+ match => [
+ "Calling-Station-Id", "^(?<Calling-Station-Id_mac>[a-fA-F0-9:-]{17})$",
+ "Calling-Station-Id", "^(?<Calling-Station-Id_mac>[a-fA-F0-9\.]{14})$",
+ "Calling-Station-Id", "^(?<Calling-Station-Id_mac>[a-fA-F0-9]{12})$",
+
+ "Called-Station-Id", "^(?<Called-Station-Id_mac>[a-fA-F0-9:-]{17})(?::(?<Called-Station-Id_ssid>.*))?$",
+ "Called-Station-Id", "^(?<Called-Station-Id_mac>[a-fA-F0-9\.]{14})(?::(?<Called-Station-Id_ssid>.*))?$",
+ "Called-Station-Id", "^(?<Called-Station-Id_mac>[a-fA-F0-9]{12})(?::(?<Called-Station-Id_ssid>.*))?$"
+ ]
+ }
+
+ # With the optional sanitize_mac plugin, it's
+ # possible to make sure all MAC addresses look the
+ # same, which has obvious benefits.
+ #
+ # https://github.com/mcnewton/logstash-filter-sanitize_mac
+
+ # sanitize_mac {
+ # match => {
+ # "Called-Station-Id_mac" => "Called-Station-Id_mac"
+ # "Calling-Station-Id_mac" => "Calling-Station-Id_mac"
+ # }
+ # separator => "-"
+ # fixcase => "lower"
+ # }
+
+
+ # Gigawords presents an issue because the 64-bit
+ # value is split across two attributes. Combine
+ # them both back into a single attribute so that
+ # the full value is available to use.
+
+ if ([Acct-Input-Octets]) {
+ ruby {
+ code => "event.set('Acct-Input-Octets_long', event.get('Acct-Input-Octets').to_i +
+ (event.get('Acct-Input-Gigawords') ? (event.get('Acct-Input-Gigawords').to_i * (2**32)) : 0))"
+ }
+ }
+
+ if ([Acct-Output-Octets]) {
+ ruby {
+ code => "event.set('Acct-Output-Octets_long', event.get('Acct-Output-Octets').to_i +
+ (event.get('Acct-Output-Gigawords') ? (event.get('Acct-Output-Gigawords').to_i * (2**32)) : 0))"
+ }
+ }
+
+
+ # Remove the original "message" field.
+
+ mutate {
+ remove_field => ["message"]
+ }
+
+ }
+}
+
+
+
+# Output data to the local elasticsearch cluster
+# using type "detail" in index "radius-DATE".
+
+output {
+ if [type] == "radiusdetail" {
+ elasticsearch {
+ index => "radius-%{+YYYY.MM.dd}"
+ }
+ }
+}
diff --git a/doc/schemas/logstash/radius-mapping.sh b/doc/schemas/logstash/radius-mapping.sh
new file mode 100755
index 0000000..0ee9a3f
--- /dev/null
+++ b/doc/schemas/logstash/radius-mapping.sh
@@ -0,0 +1,100 @@
+#! /bin/sh
+
+# Create an elasticsearch template mapping for RADIUS data
+# Matthew Newton
+# April 2019
+
+# This should be run on an elasticsearch node. Alternatively,
+# adjust the curl URI below.
+
+# This version has been tested on elasticsearch 6.7.0
+
+# The template will be called "radius", and will apply to all
+# indices prefixed with "radius-".
+#
+# As not all RADIUS attributes are known to begin with it has the
+# following starting point that can be modified to suit the local
+# configuration:
+#
+# Acct-Input- or Acct-Output- attributes are numbers;
+# Acct-Session-Time is a number;
+# Everything else is a keyword, which is a non-analysed string.
+
+# Additionally, the supplied logstash config will try and extract
+# MAC addresses, IP addresses and ports from the data. These are
+# stored with suffixes on the respective attribute. For example,
+# an attribute
+#
+# Called-Station-Id := "10.0.4.6[4500]"
+#
+# will be broken down into the following fields in elasticsearch:
+#
+# Called-Station-Id = "10.0.4.6[4500]"
+# Called-Station-Id_ip = "10.0.4.6"
+# Called-Station-Id_port = "4500"
+#
+# This mapping ensures that these have an appropriate data type.
+
+
+curl -s -XPUT -H 'Content-Type: application/json' '127.0.0.1:9200/_template/radius' -d '
+{
+ "template":"radius-*",
+ "order":0,
+ "mappings":{
+ "doc":{
+
+ "properties": {
+ "@timestamp": { "format" : "date_optional_time", "type" : "date" },
+ "@version": { "type" : "keyword" },
+ "message": { "type" : "text" },
+ "Acct-Session-Time": { "type" : "long" },
+ "offset": { "type" : "long" }
+ },
+
+ "dynamic_templates": [
+
+ { "acct_io_numbers": {
+ "match_pattern": "regex",
+ "match": "^Acct-(Input|Output)-.*$",
+ "mapping": {
+ "type": "long"
+ }
+ }
+ },
+
+ { "ipv4_address": {
+ "path_match": "*_ip",
+ "mapping": {
+ "type": "ip"
+ }
+ }
+ },
+
+ { "network_port": {
+ "path_match": "*_port",
+ "mapping": {
+ "type": "integer"
+ }
+ }
+ },
+
+ { "long_number": {
+ "path_match": "*_long",
+ "mapping": {
+ "type": "long"
+ }
+ }
+ },
+
+ { "no_analyze_strings": {
+ "match": "*",
+ "mapping": {
+ "type": "keyword"
+ }
+ }
+ }
+
+ ]
+ }
+ }
+}'
diff --git a/doc/schemas/sql b/doc/schemas/sql
new file mode 100644
index 0000000..b3ba35f
--- /dev/null
+++ b/doc/schemas/sql
@@ -0,0 +1,8 @@
+SQL Schemas
+===========
+
+SQL Schemas are included in raddb/<sql_module>/<sql_dialect>/schema.sql.
+
+It seemed like too much of a pain to split them out and put them here,
+especially in the case of SQLite where they're sometimes used for
+bootstrap.
diff --git a/doc/source/.gitignore b/doc/source/.gitignore
new file mode 100644
index 0000000..8683fe4
--- /dev/null
+++ b/doc/source/.gitignore
@@ -0,0 +1,4 @@
+html
+latex
+man
+*.tmp
diff --git a/doc/source/Doxyfile b/doc/source/Doxyfile
new file mode 100644
index 0000000..0d3c301
--- /dev/null
+++ b/doc/source/Doxyfile
@@ -0,0 +1,2313 @@
+# Doxyfile 1.8.9
+
+# 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 (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config 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 http://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 = FreeRADIUS
+
+# 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 = $Id$
+
+# 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 = ./extra/freeradius.png
+
+# 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 =
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 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.
+# The default value is: NO.
+
+CREATE_SUBDIRS = 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, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, 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 = YES
+
+# 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 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
+
+# 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 = 8
+
+# 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:\n"
+# 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:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# 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 = YES
+
+# 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
+
+# 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,
+# C#, C, C++, D, PHP, Objective-C, Python, 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), VHDL. 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.
+
+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 http://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 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 = NO
+
+# 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:
+# http://www.riverbankcomputing.co.uk/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
+
+# 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 = YES
+
+# 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 = 2
+
+#---------------------------------------------------------------------------
+# 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_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 = YES
+
+# 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 = 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
+# 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
+# (class|struct|union) 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
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+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 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 = NO
+
+# 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.
+#
+# 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 http://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 not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = 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 or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = 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)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# 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).
+
+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.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = ../../src \
+ extra
+
+# 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: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# 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. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS = *.c \
+ *.h
+
+# 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 = YES
+
+# 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 =
+
+# 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 =
+
+# 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,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */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 =
+
+# 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.
+
+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.
+
+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 =
+
+#---------------------------------------------------------------------------
+# 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,
+# classes and enums 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 = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# 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 = NO
+
+# 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 http://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 config 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 COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. 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.
+# 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). 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_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 colorwheel, see
+# http://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 grayscales 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_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = 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
+
+# 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: http://developer.apple.com/tools/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 http://developer.apple.com/tools/creatingdocsetswithdoxygen.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 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
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# 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 master .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
+
+# 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: http://qt-project.org/doc/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 = org.doxygen.Project
+
+# 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: http://qt-project.org/doc/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: http://qt-project.org/doc/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: http://qt-project.org/doc/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:
+# http://qt-project.org/doc/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 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. 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 = 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 = 1
+
+# 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 = 250
+
+# 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
+
+# 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
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://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
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) 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 http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# 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/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 = YES
+
+# 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: http://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: http://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 enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# 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.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = 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. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# 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 personal 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. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal 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.
+#
+# 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_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 = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# 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
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://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
+
+#---------------------------------------------------------------------------
+# 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 config
+# 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 config 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
+
+# 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 = YES
+
+#---------------------------------------------------------------------------
+# 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 http://autogen.sf.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 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 = NO
+
+# 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 = NO
+
+# 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.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH = ./../../src/include
+
+# 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 = *.h
+
+# 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 = WITH_PROXY \
+ WITH_UNLANG \
+ WITH_ACCOUNTING \
+ WITH_DETAIL \
+ WITH_SESSION_MGMT \
+ WITH_DYNAMIC_CLIENTS \
+ WITH_STATS \
+ WITH_COMMAND_SOCKET \
+ WITH_COA \
+ WITH_COA_TUNNEL \
+ WITH_TCP \
+ WITH_DHCP \
+ WITH_VMPS \
+ WITH_THREADS \
+ HAVE_JSON
+
+# 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 class will be listed in
+# the class 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 modules 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
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# 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 =
+
+# 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:
+# http://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
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. 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.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to 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.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# 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 HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# 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.
+# 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.
+# 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.
+# 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.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = YES
+
+# 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.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# 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 and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = svg
+
+# 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 = YES
+
+# 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 =
+
+# 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 =
+
+# 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 =
+
+# 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 = 150
+
+# 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 = 4
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = YES
+
+# 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 = YES
+
+# 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.
+# 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 dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/doc/source/extra/client.c b/doc/source/extra/client.c
new file mode 100644
index 0000000..b7775c4
--- /dev/null
+++ b/doc/source/extra/client.c
@@ -0,0 +1,12 @@
+/**
+ * @cond skip
+ * vim:syntax=doxygen
+ * @endcond
+ *
+ *
+@page client_doc
+
+@section client_intro Introduction
+
+*/
+~
diff --git a/doc/source/extra/core.c b/doc/source/extra/core.c
new file mode 100644
index 0000000..c3af04c
--- /dev/null
+++ b/doc/source/extra/core.c
@@ -0,0 +1,31 @@
+/**
+ * @cond skip
+ * vim:syntax=doxygen
+ * @endcond
+ *
+ *
+@page server_doc
+
+@section server_intro Introduction
+
+FreeRADIUS uses a thread pool to serve requests. Each request is processed
+synchronously, and processing passes through a series of stages, and a list
+of modules in each stage.
+
+The request is processed as follows
+
+- The radius packet is received by a listener - see listen.c
+- The radius packet is parsed and validated into a request - see ?
+- The request is processed - see process.c
+- The server passes through each authentication stage
+ - authorize
+ - if Proxy-To-Realm is set:
+ - pre-proxy
+ - send proxy request
+ - post-proxy
+ - else
+ - authenticate
+ - post-auth
+- Authentication stages are lists of modules - see modcall.c
+
+*/
diff --git a/doc/source/extra/freeradius.png b/doc/source/extra/freeradius.png
new file mode 100644
index 0000000..45e96a4
--- /dev/null
+++ b/doc/source/extra/freeradius.png
Binary files differ
diff --git a/doc/source/extra/module.c b/doc/source/extra/module.c
new file mode 100644
index 0000000..74071bd
--- /dev/null
+++ b/doc/source/extra/module.c
@@ -0,0 +1,17 @@
+/**
+ * @cond skip
+ * vim:syntax=doxygen
+ * @endcond
+ *
+ *
+@page module_doc
+
+@section module_intro Introduction
+
+FreeRADIUS uses a pluggable module infrastructure to extend the
+basic functionality of the server.
+
+Modules in 3.0 are dynamically linked at runtime using dlopen.
+
+@defgroup module_safe module_safe - Internal API functions that are safe for use by modules
+*/
diff --git a/doc/source/extra/toc.c b/doc/source/extra/toc.c
new file mode 100644
index 0000000..3a96fa7
--- /dev/null
+++ b/doc/source/extra/toc.c
@@ -0,0 +1,102 @@
+/**
+ * @cond skip
+ * vim:syntax=doxygen
+ * @endcond
+ *
+ *
+@mainpage
+
+@section main_intro Introduction
+
+FreeRADIUS is a high-performance modular RADIUS server, supporting PAP, CHAP,
+EAP (including EAP-TLS, EAP-TTLS, EAP-PEAP with EAP-MSCHAP) and a very flexible
+configuration model, including conditional request processing, querying of
+LDAP and SQL databases, exection of external scripts and more.
+
+FreeRADIUS uses a thread pool to serve requests. Each request is processed
+synchronously, and processing passes through a series of stages, and a list
+of modules in each stage.
+
+@section main_toc Table of Contents
+
+- @subpage server_doc "1. Core server APIs"
+- @subpage module_doc "2. Server modules"
+- @subpage client_doc "3. Client APIs"
+
+@section main_branches GIT Branch
+
+@subsection branch_master Master (v4) feature branch
+
+@code
+git clone git@github.com:FreeRADIUS/freeradius-server.git
+@endcode
+- Web: http://github.com/FreeRADIUS/freeradius-server/tree/master
+
+@subsection branch_32x 3.2.x stable branch
+
+Minor bug fixes, documentation updatews, etc. go into this branch.
+
+@code
+git clone git@github.com:FreeRADIUS/freeradius-server.git
+cd freeradius-server
+git fetch origin v3.2.x:v3.2.x
+git checkout v3.2.x
+@endcode
+- Web: http://github.com/FreeRADIUS/freeradius-server/tree/v3.2.x
+
+@subsection branch_2xx 2.x.x EOL branch
+
+@note This branch is now permanently feature frozen. New features or modules
+ should be submitted against the master branch.
+
+@code
+git clone git@github.com:FreeRADIUS/freeradius-server.git
+cd freeradius-server
+git fetch origin v2.x.x:v2.x.x
+git checkout v2.x.x
+@endcode
+- Web: http://github.com/FreeRADIUS/freeradius-server/tree/v2.x.x
+
+@subsection branch_1xx 1.1.x EOL branch
+
+@note This branch is now permanently feature frozen. New features or modules
+ should be submitted against the master branch.
+
+@code
+git clone git@github.com:FreeRADIUS/freeradius-server.git
+cd freeradius-server
+git fetch origin v2.x.x:v2.x.x
+git checkout v2.x.x
+@endcode
+- Web: http://github.com/FreeRADIUS/freeradius-server/tree/v1.1.x
+
+@section main_website Website
+
+- http://www.freeradius.org
+
+@section mailinglist Mailing lists
+
+@subsection main_list FreeRADIUS-users
+
+This list is for users of the server
+
+@code
+freeradius-users@lists.freeradius.org
+@endcode
+- Archives: http://lists.freeradius.org/pipermail/freeradius-users/
+- List info: http://freeradius.org/list/users.html
+
+@subsection dev_list FreeRADIUS-devel
+
+This list is for development of the server, including patches, and
+new features. PLEASE DO NOT post questions related to the operation
+of the server here - use the "users" list. Most of the developers
+read both, and will answer your questions there if they have the time.
+
+@code
+freeradius-devel@lists.freeradius.org
+@endcode
+- Archives: http://lists.freeradius.org/pipermail/freeradius-devel/
+- List info: http://freeradius.org/list/devel.html
+
+*/
diff --git a/doc/vendor/ascend b/doc/vendor/ascend
new file mode 100644
index 0000000..57a44be
--- /dev/null
+++ b/doc/vendor/ascend
@@ -0,0 +1,57 @@
+ Ascend Radius Options
+ or
+ What happens when a big vendor ignores an RFC
+
+
+ FreeRADIUS uses Vendor-Specific attributes to send the Ascend attributes.
+By default, Ascend NASes send the Ascend specific attributes as NON VSA's,
+which conflict with new RADIUS attributes assigned by the IETF. This was
+a very bad screw-up by Ascend that still causes many headaches, but sometimes
+we have to live with it, so we try to cope the best we can.
+
+ If you see a large number of messages about invalid Message-Authenticator
+attribute, you most likely are affected by this problem, and should implement
+the first option.
+
+You have two options:
+
+o Enable VSA's on the Ascend/Lucent MAX:
+
+ This is by far the preferred method ( as it solves many other problems ).
+
+ Max6000/4000 Series TAOS with Menued Interface:
+
+ Go to Ethernet->Mod Config->Auth.
+ At the bottom of the menu, change Auth-Compat from "OLD" to "VSA".
+ Save your changes, no reboot is needed.
+
+ Go to Ethernet->Mod Config->Acct.
+ At the bottom of the menu, change Acct-Compat from "OLD" to "VSA".
+ Save your changes, no reboot is needed.
+
+ Max TNT/Apex 8000 Series TAOS with CLI:
+
+ nas> read external-auth
+ nas> set rad-auth-client auth-radius-compat = vendor-specific
+ nas> set rad-acct-client acct-radius-compat = vendor-specific
+ nas> write
+
+o Enable OLD attributes in FreeRADIUS
+
+ One note on this, Ciscos have an Ascend compatibility mode that
+ accepts only the OLD style Ascend attributes, just to make life more
+ interesting. :)
+
+ You can make FreeRADIUS send the OLD style attributes by prefixing the
+ Ascend attributes with 'X-' in the 'users' file, sql table, ldap directory,
+ attr_filter module, etc...
+
+ Thus the VSA Ascend attribute:
+
+ Ascend-Data-Filter
+
+ becomes the OLD Ascend attribute:
+
+ X-Ascend-Data-Filter
+
+$Id$
diff --git a/doc/vendor/bay b/doc/vendor/bay
new file mode 100644
index 0000000..a23cacd
--- /dev/null
+++ b/doc/vendor/bay
@@ -0,0 +1,11 @@
+ All versions of the BAY software prior to 18.0.2 are broken in
+regards to the Message-Authenticator. They send a strictly MD5
+encoded secret instead of the encoding required by the RFC. This has
+been fixed in 18.0.2 and only 18.0.2.
+
+ If you see messages in the radius log like:
+
+Received packet from xxx.xxx.xxx.xxx with invalid Message-Authenticator!
+
+ and you are using a Bay Annex, then you MUST upgrade the software on
+your Annex. There is NO other solution to the problem.
diff --git a/doc/vendor/cisco.rst b/doc/vendor/cisco.rst
new file mode 100644
index 0000000..12270cb
--- /dev/null
+++ b/doc/vendor/cisco.rst
@@ -0,0 +1,168 @@
+Cisco IOS and Radius
+====================
+
+Introduction
+------------
+
+Cisco NAS equipment has become quite popular of late, but being Cisco
+equipment running IOS, the configuration can be a bit non-obvious to the
+unfamiliar. This document aims to describe the most common configuration
+options to make your Ciscos interoperate with radius as you would expect a
+well-behaved NAS to do.
+
+IOS 12.x
+--------
+
+For Cisco 12.x ( 12.0 and 12.1 ), the following AAA configuration directives
+are suggested:
+
+::
+
+ aaa new-model
+ aaa authentication login default group radius local
+ aaa authentication login localauth local
+ aaa authentication ppp default if-needed group radius local
+ aaa authorization exec default group radius local
+ aaa authorization network default group radius local
+ aaa accounting delay-start
+ aaa accounting exec default start-stop group radius
+ aaa accounting network default start-stop group radius
+ aaa processes 6
+
+this configuration works very well with most radius servers. One of the more
+important configurations is:
+
+::
+
+ aaa accounting delay-start
+
+This directive will delay the sending of the Accounting Start packet until
+after an IP address has been assigned during the PPP negotiation process.
+This will supersede the need to enable the sending of "Alive" packets as
+described below for IOS versions 11.x
+
+*Note* with the above it will use the radius server to authenticate
+your inbound 'telnet' connections. You will need to create an entry
+in your users file similar to the following to allow access:
+
+::
+
+ !root Cleartext-Password := "somepass" Service-Type = NAS-Prompt-User
+
+This will let a user in for the first level of access to your Cisco. You
+will still need to 'enable' ( using the locally configured enable secret )
+to perform any configuration changes or anything requiring a higher level
+of access. The username '!root' was used as an example here, you can make
+this any username you want, of course.
+
+Unique Acct-Session-Id's
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+From: http://isp-lists.isp-planet.com/isp-australia/0201/msg05143.html
+
+Just a note to all cisco ISPs out there who want RFC2866 compliance need to
+enable the hidden command ``radius-server unique-ident <n>``
+
+Minimum IOS: 12.1(4.1)T.
+
+Acct-Session-Id should be unique and wrap after every 256 reboots.
+
+You must reboot after entering this command to take effect. If not, you
+will observe after 10 minutes
+of entering this command, the following message.
+
+::
+
+ %RADIUS-3-IDENTFAIL: Save of unique accounting ident aborted.
+
+IOS 11.x
+--------
+
+For Cisco 11.1, you normally use
+
+::
+
+ aaa new-model
+ aaa authentication ppp radppp if-needed radius
+ aaa authorization network radius none
+ aaa accounting network wait-start radius
+
+to get the Cisco to talk to a radius server.
+
+With IOS 11.3
+^^^^^^^^^^^^^
+
+::
+
+ aaa accounting update newinfo
+
+If you want the IP address of the user to show up in the radutmp file
+(and thus, the output of "radwho").
+
+This is because with IOS 11.3, the Cisco first sends a "Start" accounting
+packet without the IP address included. By setting "update newinfo" it
+will send an account "Alive" packet which updates the information.
+
+Also you might see a lot of "duplicates" in the logfile. That can be
+fixed by:
+
+::
+
+ aaa accounting network wait radius
+ radius-server timeout 3
+
+To disable the Ascend style attributes (which is a VERY good idea!):
+
+::
+
+ radius-server host X.Y.Z.A auth-port 1645 acct-port 1646
+
+To enable the Ascend style attributes (which we do NOT recommend!):
+
+::
+
+ radius-server host X.Y.Z.A auth-port 1645 acct-port 1646 non-standard
+
+To see Cisco-AVPair attributes in the Cisco debugging log:
+
+ radius-server vsa accounting
+
+Cisco 36xx & 26xx, keeping the NAS IP static
+--------------------------------------------
+
+The Cisco 36/26 by default selects (it seems at random) any IP address
+assigned to it (serial, ethernet etc.) as it's RADIUS client source
+address, thus the access request may be dropped by the RADIUS server,
+because it can not verify the client. To make the cisco box always use
+one fixed address, add the following to your configuration:
+
+::
+
+ ip radius source-interface Loopback0
+
+and configure the loopback interface on your router as follows:
+
+::
+
+ interface Loopback0
+ ip address 192.0.2.250 255.255.255.255
+
+Use a real world IP address and check the Cisco documentation for why
+it is a good idea to have working loopback interface configured on
+your router.
+
+If you don't want to use the loopback interface of course you can set
+the source-interface to any interface on your Cisco box which has an
+IP address.
+
+Credits
+-------
+
+Original - Alan DeKok <aland@ox.org>
+12.x Info - Chris Parker <cparker@starnetusa.net> 2000-10-12
+
+More Information
+----------------
+For more information, the following page on Cisco's web site may help:
+
+http://www.cisco.com/univercd/cc/td/doc/product/access/acs_serv/vapp_dev/vsaig3.htm
diff --git a/doc/vendor/proxim b/doc/vendor/proxim
new file mode 100644
index 0000000..3a8f2b9
--- /dev/null
+++ b/doc/vendor/proxim
@@ -0,0 +1,12 @@
+Proxim
+^^^^^^
+
+Proxim AP-2000 NASes up to and including firmware version 2.4.5 ignore
+the Session-Timeout attribute, despite the fact that Proxim's firmware
+release notes specifically state that it is supported. On top of
+this, firmware version 2.4.5 (the latest as of this writing) has a bug
+in which the reauthentication interval on the AP cannot be set to any
+value less than 2 hours. As such, fine-grained control of client
+session times is not currently possible with this NAS. Note that this
+NAS is OEMed to several vendors, including Avaya, and may be listed
+under different names with different vendors.