kea-2.0.2/0000755000175000017500000000000014206773511007262 500000000000000kea-2.0.2/platforms.rst0000644000175000017500000001172414206773363011755 00000000000000 .. _platforms: Supported platforms =================== In general, this version of Kea will build and run on any POSIX-compliant system with a C++ compiler (with C++11 support), the Botan cryptographic library, the log4cplus logging library and the Boost system library. The Kea build has been checked with GCC g++ 4.8.5 and some later versions, and Clang 800.0.38 and some later versions. ISC regularly tests Kea on many operating systems and architectures, but lacks the resources to test all of them. Consequently, ISC is only able to offer support on a "best effort" basis for some. Regularly tested platforms -------------------------- Kea is officially supported on Alpine, CentOS, Fedora, Ubuntu, Debian, and FreeBSD systems. Kea-|release| builds have been tested on: * Alpine - 3.12, 3.13 * CentOS — 7 * Debian — 9, 10, 11 * Fedora — 33, 34 * FreeBSD — 12.1, 13.0 * Ubuntu — 18.04, 20.04, 21.04 There are currently no plans to port Kea to Windows systems. Best effort ----------- The following are platforms on which Kea is known to build and run. ISC makes every effort to fix bugs on these platforms, but may be unable to do so quickly due to lack of hardware, less familiarity on the part of engineering staff, and other constraints. * Alpine — 3.11 * FreeBSD - 11.4 * macOS — 10.13, 10.14 Community maintained -------------------- These systems may not all have the required dependencies for building Kea easily available, although it will be possible in many cases to compile those directly from source. The community and interested parties may wish to help with maintenance, and we welcome patch contributions, although we cannot guarantee that we will accept them. All contributions will be assessed against the risk of adverse effect on officially supported platforms. Platforms past their respective EOL dates, such as: * Alpine — 3.10 (1 May 2021) * CentOS — 6 (30 November 2020) * Fedora — 31, 32 * Ubuntu — 14.04, 18.10, 19.04, 19.10 * Debian — 8 (30 June 2020) * FreeBSD — 10 (31 October 2018) Unsupported platforms --------------------- These are platforms on which Kea 1.7+ is known *not* to build or run: * Windows (all versions) * Windows Server (all versions) * Any platform with OpenSSL 1.0.1 or earlier, which does not also have Botan as an alternative * Any platform with log4cplus version 1.0.2 or earlier. .. _required-software: Required Software at Run-Time ============================= Running Kea uses various extra software packages which may not be provided in the default installation of some operating systems, nor in the standard package collections. You may need to install this required software separately. (For the build requirements, also see :ref:`build-requirements`.) - Kea supports two cryptographic libraries: Botan and OpenSSL. Only one of them is required to be installed during compilation. Kea uses the Botan library for C++ (https://botan.randombit.net/), version 2.0 or later. Note that support for Botan versions earlier than 2.0 was removed in Kea 1.7.0 and later. As an alternative to Botan, Kea can use the OpenSSL cryptographic library (https://www.openssl.org/), version 1.0.2 or later. - Kea uses the log4cplus C++ logging library (https://sourceforge.net/p/log4cplus/wiki/Home/). It requires log4cplus version 1.0.3 or later. - Kea requires the Boost system library (https://www.boost.org/). Building with the header-only version of Boost is no longer recommended. Some optional features of Kea have additional dependencies. - To store lease information in a MySQL database, Kea requires MySQL headers and libraries. This is an optional dependency; Kea can be built without MySQL support. - To store lease information in a PostgreSQL database, Kea requires PostgreSQL headers and libraries. This is an optional dependency; Kea can be built without PostgreSQL support. - To store lease information in a Cassandra database (CQL), Kea requires Cassandra headers and libraries. This is an optional dependency; Kea can be built without Cassandra support. - Integration with RADIUS is provided in Kea via the hooks library available to our paid support customers. Use of this library requires the FreeRadius-client library to be present on the system where Kea is running. This is an optional dependency; Kea can be built without RADIUS support. - Kea provides a NETCONF interface with the kea-netconf agent. This Kea module requires Sysrepo software when used. Building Kea with NETCONF support requires many dependencies to be installed, which are described in more detail in :ref:`netconf-install`. This is an optional dependency; Kea can be built without NETCONF support. - To sign and verify DNS updates, Kea DDNS server may use GSS-TSIG which requires MIT Kerberos 5 or Heimdal libraries. The dependencies required to be installed are described in more detail in :ref:`gss-tsig-install`. This is an optional dependency; Kea can be built without GSS-TSIG support. kea-2.0.2/ylwrap0000755000175000017500000001531414206773400010447 00000000000000#! /bin/sh # ylwrap - wrapper for lex/yacc invocations. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1996-2018 Free Software Foundation, Inc. # # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . get_dirname () { case $1 in */*|*\\*) printf '%s\n' "$1" | sed -e 's|\([\\/]\)[^\\/]*$|\1|';; # Otherwise, we want the empty string (not "."). esac } # guard FILE # ---------- # The CPP macro used to guard inclusion of FILE. guard () { printf '%s\n' "$1" \ | sed \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g' \ -e 's/__*/_/g' } # quote_for_sed [STRING] # ---------------------- # Return STRING (or stdin) quoted to be used as a sed pattern. quote_for_sed () { case $# in 0) cat;; 1) printf '%s\n' "$1";; esac \ | sed -e 's|[][\\.*]|\\&|g' } case "$1" in '') echo "$0: No files given. Try '$0 --help' for more information." 1>&2 exit 1 ;; --basedir) basedir=$2 shift 2 ;; -h|--h*) cat <<\EOF Usage: ylwrap [--help|--version] INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]... Wrapper for lex/yacc invocations, renaming files as desired. INPUT is the input file OUTPUT is one file PROG generates DESIRED is the file we actually want instead of OUTPUT PROGRAM is program to run ARGS are passed to PROG Any number of OUTPUT,DESIRED pairs may be used. Report bugs to . EOF exit $? ;; -v|--v*) echo "ylwrap $scriptversion" exit $? ;; esac # The input. input=$1 shift # We'll later need for a correct munging of "#line" directives. input_sub_rx=`get_dirname "$input" | quote_for_sed` case $input in [\\/]* | ?:[\\/]*) # Absolute path; do nothing. ;; *) # Relative path. Make it absolute. input=`pwd`/$input ;; esac input_rx=`get_dirname "$input" | quote_for_sed` # Since DOS filename conventions don't allow two dots, # the DOS version of Bison writes out y_tab.c instead of y.tab.c # and y_tab.h instead of y.tab.h. Test to see if this is the case. y_tab_nodot=false if test -f y_tab.c || test -f y_tab.h; then y_tab_nodot=true fi # The parser itself, the first file, is the destination of the .y.c # rule in the Makefile. parser=$1 # A sed program to s/FROM/TO/g for all the FROM/TO so that, for # instance, we rename #include "y.tab.h" into #include "parse.h" # during the conversion from y.tab.c to parse.c. sed_fix_filenames= # Also rename header guards, as Bison 2.7 for instance uses its header # guard in its implementation file. sed_fix_header_guards= while test $# -ne 0; do if test x"$1" = x"--"; then shift break fi from=$1 # Handle y_tab.c and y_tab.h output by DOS if $y_tab_nodot; then case $from in "y.tab.c") from=y_tab.c;; "y.tab.h") from=y_tab.h;; esac fi shift to=$1 shift sed_fix_filenames="${sed_fix_filenames}s|"`quote_for_sed "$from"`"|$to|g;" sed_fix_header_guards="${sed_fix_header_guards}s|"`guard "$from"`"|"`guard "$to"`"|g;" done # The program to run. prog=$1 shift # Make any relative path in $prog absolute. case $prog in [\\/]* | ?:[\\/]*) ;; *[\\/]*) prog=`pwd`/$prog ;; esac dirname=ylwrap$$ do_exit="cd '`pwd`' && rm -rf $dirname > /dev/null 2>&1;"' (exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 mkdir $dirname || exit 1 cd $dirname case $# in 0) "$prog" "$input" ;; *) "$prog" "$@" "$input" ;; esac ret=$? if test $ret -eq 0; then for from in * do to=`printf '%s\n' "$from" | sed "$sed_fix_filenames"` if test -f "$from"; then # If $2 is an absolute path name, then just use that, # otherwise prepend '../'. case $to in [\\/]* | ?:[\\/]*) target=$to;; *) target=../$to;; esac # Do not overwrite unchanged header files to avoid useless # recompilations. Always update the parser itself: it is the # destination of the .y.c rule in the Makefile. Divert the # output of all other files to a temporary file so we can # compare them to existing versions. if test $from != $parser; then realtarget=$target target=tmp-`printf '%s\n' "$target" | sed 's|.*[\\/]||g'` fi # Munge "#line" or "#" directives. Don't let the resulting # debug information point at an absolute srcdir. Use the real # output file name, not yy.lex.c for instance. Adjust the # include guards too. sed -e "/^#/!b" \ -e "s|$input_rx|$input_sub_rx|" \ -e "$sed_fix_filenames" \ -e "$sed_fix_header_guards" \ "$from" >"$target" || ret=$? # Check whether files must be updated. if test "$from" != "$parser"; then if test -f "$realtarget" && cmp -s "$realtarget" "$target"; then echo "$to is unchanged" rm -f "$target" else echo "updating $to" mv -f "$target" "$realtarget" fi fi else # A missing file is only an error for the parser. This is a # blatant hack to let us support using "yacc -d". If -d is not # specified, don't fail when the header file is "missing". if test "$from" = "$parser"; then ret=1 fi fi done fi # Remove the directory. cd .. rm -rf $dirname exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: kea-2.0.2/README0000644000175000017500000000140614206773363010070 00000000000000Kea provides DHCPv4 and DHCPv6 servers, a dynamic DNS update module, a portable DHCP library, libdhcp++, a control agent that provides a management REST interface, a NETCONF agent that provides a YANG/NETCONF interface for Kea, and a DHCP benchmarking tool, perfdhcp. Kea is developed by Internet Systems Consortium, Inc. The developers' website is at https://gitlab.isc.org/isc-projects/kea/. Limitations, known issues, and feature requests can be found at https://gitlab.isc.org/isc-projects/kea/-/wikis/known-issues-list. If you are thinking about contributing a patch, please read the Contributor's Guide first. Participants in this project implicitly agree to abide by the project's Code of Conduct. The Kea mailing lists are available via https://lists.isc.org/. kea-2.0.2/AUTHORS0000644000175000017500000001750214206773363010264 00000000000000 Kea authors and contributors ------------------------------ Primary developers: - Tomek Mrugalski (DHCPv4, DHCPv6 components, prefix delegation, memfile, database interface, core libdhcp++, host reservation, MAC extraction in DHCPv6, statistics manager, kea-shell, netconf, flex/bison parsers, flex-id, documentation) - Marcin Siodelski (DHCPv4, DHCPv6 components, options handling, perfdhcp, host reservation, lease file cleanup, lease expiration, control agent, shared networks, high availability, config backend) - Thomas Markwalder (DDNS, user_chk, global host reservations, stat commands, congestion handling, config backend, multi-threading mode of high availability, forensic logging, leasequery) - Wlodek Wencel (testing, release engineering) - Francis Dupont (crypto, flex/bison parsers, perfdhcp, control agent, radius, netconf, config backend, multi-threading) - Razvan Becheriu (cassandra, netconf, multi-threading, forensic logging, run script hook, multi-threading mode of high availability) - Vicky Risk (documentation) - Suzanne Goldlust (documentation) - Andrei Pavel (build system, documentation, hammer, netconf, perfdhcp, release engineering, shell scripts, testing) - Peter Davies (documentation) - Slawek Figiel (documentation) Former developers who are no longer active: - Stephen Morris (Hooks, MySQL) - Jeremy C. Reed (documentation, build system, testing, release engineering) - Brian Reid (logo design) - Shawn Routhier (lease file cleanup) - Michal Nowikowski (testing, hammer, release engineering) Main area of work mentioned in parentheses. The lists are in a roughly chronological order. Kea uses parts of the code of the now-defunct BIND 10 project. The following people contributed to BIND 10 code: Chen Zhengzhang Dmitriy Volodin Evan Hunt Francis Dupont Haidong Wang Haikuo Zhang Han Feng Jelte Jansen Jeremy C. Reed Xie Jiagui Jin Jian JINMEI Tatuya John DuBois Kazunori Fujiwara Marcin Siodelski Michael Graff Michal Vaner Mukund Sivaraman Naoki Kambe Paul Selkirk Shane Kerr Shen Tingting Stephen Morris Thomas Markwalder Tomek Mrugalski Yoshitaka Aharen Zhang Likun We have received the following contributions: - David Carlier 2013-11: memfile fixes 2013-12: better error handling when port is in use 2013-12: interface detection for BSD systems 2014-04: PostgreSQL support - Jiri Popelka, Red Hat 2014-08: config files examples permission fix 2014-08: compilation fix for armv7 2014-08: configure.ac update: AC_PROG_LIBTOOL => LT_INIT 2014-08: PostgreSQL compilation fix on i686 2015-12: compilation fix in MySQL host data source 2016-02: Fixed missing slashes in path_replacer.sh - Adam Osuchowski, Silesian University of Technology 2014-09: Examples corrected in Kea ARM 2019-02: Hooks installation directory fixed. 2019-02: Possible syntax error in keactrl fixed. - Nicolas Chaigneau, Capgemini 2014-09: Fix for interfaces with multiple addresses in perfdhcp 2015-11: query4 parameter added to pkt4_send hook point - Marcin Wyszynki, Facebook 2014-11: Export CalloutManager headers for testing statically linked libraries. - David Gutierrez Rueda, CERN 2014-12: Support for client link-address option in DHCPv6 (RFC6939) - Adam Kalmus, Gdansk University of Technology 2014-12: Extract MAC address from DUID-LL and DUID-LLT types 2015-01: Extract MAC address from remote-id 2015-05: MySQL schema extended to cover host reservation 2015-10: Common MySQL Connector Pool 2015-12: MySQL host data source implemented 2016-02: IPv6 reservations implemented - Jinmei Tatuya 2015-10: Pkt4o6 class improvements 2015-11: split Dhcpv4Srv::run() into run() and processPacket() - Sebastien Couture, Ubity Inc 2015-12: Fixes to MySQL schema creation - Angelo Failla, Facebook 2016-04: Fixes for transaction id generation in perfdhcp 2016-08: Using a file as a source of MAC addresses to be used in new transactions. 2016-08: Support for generating relayed DHCPv6 traffic. - Razvan Becheriu, Qualitance 2016-05: Added support for Cassandra 2017-12: Significant update for Cassandra backend 2018-01: Host reservations for Cassandra 2018-01: Various changes (github 54) 2018-02: Support for Google benchmark added (github 36) 2018-02: exit-wait-time param added to perfdhcp (github 55) 2018-03: Cassandra: host delete, fixed DHCPv4 fields, user contexts, Postgres: hwaddress source, type storage (github 70) 2018-07: Sysrepo detection improvements - Patrik Lundin 2016-07: Replace test by expr for < in configure.ac 2016-11: Fixes in Lease File Cleanup unit test - Michal Humpula (mihu) 2016-07: Response to DHCPINFORM is sent to port 68 - Andreas Rammhold (andir) 2016-09: Compilation fixes for GCC 6, using C++14. - Yusef Shaban (xxwolfsrainxx) 2016-09: MySQL database creation scripts use single quotes for strings to avoid issues with creation of the database when MySQL server operates in ANSI_QUOTES mode. - Cristian Secareanu, Qualitance 2016-10: Support for IPv6 prefix and PDEXCLUDE option - Andrei Pavel, Qualitance 2016-10: Support for DHCPv6 options defined in RFC6603 and RFC7598 2017-02: Doxygen support updated to 1.8.11 2017-02: Improved PgSQL backend version handling 2017-02: Numerous spelling mistakes 2017-12: Significant update for Cassandra backend 2018-01: Host reservations for Cassandra 2018-01: Uniform compilation 2018-01: Various changes (github 54,43) 2018-02: Documentation upgraded to DocBook 5.0 2018-02: --with-dhcp-XXX renamed to --with-XXX 2018-02: Support for Google benchmark added (github 36) 2018-02: exit-wait-time param added to perfdhcp (github 55) 2018-07: Sysrepo detection improvements 2020-05: Fix exit-wait-time in perfdhcp - Vincent Legout 2016-11: Fixed serveral spelling mistakes - Sebasian Schrader 2017-01: Fix build dir in doc/guide/Makefile.am - Marvin Frick (MrMarvin) 2017-04: -h and --host parameters added to kea-admin - Olivier Clavel (zeitounator) 2017-04: Improvements in valgrind test script - Josh Soref (jsoref) 2017-07: Many spelling corrections. - Walt Steverson (waltsteverson) 2017-07: Compilation fixed for Alpine Linux 2017-07: option6_pdexclude.h now installed properly - Ebben Aries 2017-10: Option length checks improvements for the V-I Vendor Class option - Ryan Goodfellow (rcgoodfellow) 2018-01: Fix kea-admin typo breaking lease-dump - Sunil Mayya 2018-07: support for Authentication option in DHCPv6 2018-07: support storage of Authentication keys in host structure 2018-08: Optimized query for host reservation from the backends - Piotr Strzyżewski 2018-07: YANG model for DHCPv4 Kea - Vicky Risk 2018-08: Documentation clean up 2018-10: API documentation clean ups - Franciszek Gorski 2018-10: Makefile bug fixed 2019-07: Statistics enhancements 2019-09: Statistics initialization enhancements - Suzanne Goldlust 2018-10: API documentation - lpaserati, Thorsten Krohn 2018-11: Two bugfixes in kea-admin - Kristoffer Larsen 2019-10: Changes in alloc_engine_messages.mes are now picked up correctly. - Niclas Rosenvik 2020-01: Fix in the gtest detection scripts. - Carsten Strotman 2020-11: Several Kea ARM corrections. - Khem Raj 2021-05: gcc11 compilation fixes. - Sriram Rajagopalan 2021-08: fix for a type mismatch in libdhcp which could have lead to an an interface with index of MAX_UINT32_T to be set in a Pkt when the index was meant to be reset instead - Brad Smith 2021-08: compilation fix for upcoming boost 1.77 kea-2.0.2/compile0000755000175000017500000001632714206773377010603 00000000000000#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2018 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: kea-2.0.2/kea_version.h0000644000175000017500000000015614206773477011675 00000000000000#define EXTENDED_VERSION "git e107418c680e9eea42194ded568c703366692d83" #define PACKAGE_VERSION_TYPE "stable" kea-2.0.2/config.h.in0000644000175000017500000001566514206773376011253 00000000000000/* config.h.in. Generated from configure.ac by autoheader. */ /* Define to 1 if system and steady clocks use the same duration type */ #undef CHRONO_SAME_DURATION /* config.h inclusion marker */ #undef CONFIG_H_WAS_INCLUDED /* AFL fuzzing was enabled. */ #undef ENABLE_AFL /* Enable low-performing debugging facilities? */ #undef ENABLE_DEBUG /* Check logger messages? */ #undef ENABLE_LOGGER_CHECKS /* Does this platform have some undefined pthreads behavior? */ #undef HAS_UNDEFINED_PTHREAD_BEHAVIOR /* Define to 1 if you have the header file. */ #undef HAVE_BOOST_ASIO_COROUTINE_HPP /* Define to 1 if you have the header file. */ #undef HAVE_BOOST_ASIO_HPP /* Define to 1 if you have the header file. */ #undef HAVE_BOOST_ASIO_IP_ADDRESS_HPP /* Define to 1 if you have the header file. */ #undef HAVE_BOOST_ASIO_SIGNAL_SET_HPP /* Define to 1 if you have the header file. */ #undef HAVE_BOOST_ASIO_SSL_HPP /* Define to 1 if you have the header file. */ #undef HAVE_BOOST_ATOMIC_HPP /* Define to 1 if you have the header file. */ #undef HAVE_BOOST_CIRCULAR_BUFFER_HPP /* Define to 1 if you have the header file. */ #undef HAVE_BOOST_DATE_TIME_POSIX_TIME_POSIX_TIME_TYPES_HPP /* Define to 1 if you have the header file. */ #undef HAVE_BOOST_FOREACH_HPP /* Define to 1 if you have the header file. */ #undef HAVE_BOOST_FUNCTIONAL_HASH_HPP /* Define to 1 if you have the header file. */ #undef HAVE_BOOST_INTEGER_COMMON_FACTOR_HPP /* Define to 1 if you have the header file. */ #undef HAVE_BOOST_INTERPROCESS_SYNC_INTERPROCESS_UPGRADABLE_MUTEX_HPP /* Define to 1 if you have the header file. */ #undef HAVE_BOOST_SHARED_PTR_HPP /* Define to 1 if you have the header file. */ #undef HAVE_BOOST_SYSTEM_ERROR_CODE_HPP /* Define to 1 if you have the header file. */ #undef HAVE_BOTAN_ASIO_STREAM_H /* Define to 1 if you have the header file. */ #undef HAVE_BOTAN_BOTAN_H /* Define to 1 if getsockopt(IPV6_USE_MIN_MTU) does not work */ #undef HAVE_BROKEN_GET_IPV6_USE_MIN_MTU /* CQL is present */ #undef HAVE_CQL /* Define to 1 if gtest defines edit_distance::CreateUnifiedDiff */ #undef HAVE_CREATE_UNIFIED_DIFF /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define to 1 if boost::asio::ssl::context::tls is available */ #undef HAVE_GENERIC_TLS_METHOD /* Define to 1 if you have the header file. */ #undef HAVE_GSSAPI_GSSAPI_H /* Define to 1 if you have the header file. */ #undef HAVE_GSSAPI_GSSAPI_KRB5_H /* gss_str_to_oid is available */ #undef HAVE_GSS_STR_TO_OID /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if std::is_base_of is available */ #undef HAVE_IS_BASE_OF /* Define to 1 if you have the header file. */ #undef HAVE_KRB5_H /* Define to 1 if you have the header file. */ #undef HAVE_KRB5_KRB5_H /* Define to 1 if you have the header file. */ #undef HAVE_LOG4CPLUS_LOGGER_H /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* MySQL is present */ #undef HAVE_MYSQL /* MySQL uses my_bool */ #undef HAVE_MYSQL_MY_BOOL /* Check for optreset? */ #undef HAVE_OPTRESET /* PostgreSQL is present */ #undef HAVE_PGSQL /* Define to 1 if you have the `pselect' function. */ #undef HAVE_PSELECT /* Define to 1 if sockaddr has a sa_len member, and corresponding sin_len and sun_len */ #undef HAVE_SA_LEN /* Define to 1 if stdbool.h conforms to C99. */ #undef HAVE_STDBOOL_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if boost::asio::ssl::error::stream_truncated is available */ #undef HAVE_STREAM_TRUNCATED_ERROR /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Using sysrepo 1.x */ #undef HAVE_SYSREPO_V1 /* Define to 1 if you have the header file. */ #undef HAVE_SYS_DEVPOLL_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_FILIO_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if boost::asio::ssl::context::tlsv12 is available */ #undef HAVE_TLS_1_2_METHOD /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the header file. */ #undef HAVE_UTILS_ERRCODES_H /* Check valgrind headers */ #undef HAVE_VALGRIND_HEADERS /* Define to 1 if you have the header file. */ #undef HAVE_VALGRIND_VALGRIND_H /* Define to 1 if the system has the type `_Bool'. */ #undef HAVE__BOOL /* Define to 1 if libc is musl */ #undef LIBC_MUSL /* Explicit initialization of log4cplus possible */ #undef LOG4CPLUS_INITIALIZER_H /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Running on BSD? */ #undef OS_BSD /* Running on FreeBSD? */ #undef OS_FREEBSD /* Running on Linux? */ #undef OS_LINUX /* Running on NetBSD? */ #undef OS_NETBSD /* Running on OpenBSD? */ #undef OS_OPENBSD /* Running on OSX? */ #undef OS_OSX /* Running on Solaris? */ #undef OS_SOLARIS /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define to 1 if C++11 regex is usable */ #undef USE_REGEX /* Was Kea statically linked? */ #undef USE_STATIC_LINK /* Version number of package */ #undef VERSION /* Compile with Botan crypto */ #undef WITH_BOTAN /* Define to 1 if Botan boost TLS is available */ #undef WITH_BOTAN_BOOST /* Heimdal GSS-API implementation */ #undef WITH_HEIMDAL /* Compile with OpenSSL crypto */ #undef WITH_OPENSSL /* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a `char[]'. */ #undef YYTEXT_POINTER /* Define to `unsigned int' if does not define. */ #undef size_t /* Define to `int' if does not define. */ #undef ssize_t kea-2.0.2/py-compile0000755000175000017500000001110014206773400011174 00000000000000#!/bin/sh # py-compile - Compile a Python program scriptversion=2018-03-07.03; # UTC # Copyright (C) 2000-2018 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . if [ -z "$PYTHON" ]; then PYTHON=python fi me=py-compile usage_error () { echo "$me: $*" >&2 echo "Try '$me --help' for more information." >&2 exit 1 } basedir= destdir= while test $# -ne 0; do case "$1" in --basedir) if test $# -lt 2; then usage_error "option '--basedir' requires an argument" else basedir=$2 fi shift ;; --destdir) if test $# -lt 2; then usage_error "option '--destdir' requires an argument" else destdir=$2 fi shift ;; -h|--help) cat <<\EOF Usage: py-compile [--help] [--version] [--basedir DIR] [--destdir DIR] FILES..." Byte compile some python scripts FILES. Use --destdir to specify any leading directory path to the FILES that you don't want to include in the byte compiled file. Specify --basedir for any additional path information you do want to be shown in the byte compiled file. Example: py-compile --destdir /tmp/pkg-root --basedir /usr/share/test test.py test2.py Report bugs to . EOF exit $? ;; -v|--version) echo "$me $scriptversion" exit $? ;; --) shift break ;; -*) usage_error "unrecognized option '$1'" ;; *) break ;; esac shift done files=$* if test -z "$files"; then usage_error "no files given" fi # if basedir was given, then it should be prepended to filenames before # byte compilation. if [ -z "$basedir" ]; then pathtrans="path = file" else pathtrans="path = os.path.join('$basedir', file)" fi # if destdir was given, then it needs to be prepended to the filename to # byte compile but not go into the compiled file. if [ -z "$destdir" ]; then filetrans="filepath = path" else filetrans="filepath = os.path.normpath('$destdir' + os.sep + path)" fi $PYTHON -c " import sys, os, py_compile, imp files = '''$files''' sys.stdout.write('Byte-compiling python modules...\n') for file in files.split(): $pathtrans $filetrans if not os.path.exists(filepath) or not (len(filepath) >= 3 and filepath[-3:] == '.py'): continue sys.stdout.write(file) sys.stdout.flush() if hasattr(imp, 'get_tag'): py_compile.compile(filepath, imp.cache_from_source(filepath), path) else: py_compile.compile(filepath, filepath + 'c', path) sys.stdout.write('\n')" || exit $? # this will fail for python < 1.5, but that doesn't matter ... $PYTHON -O -c " import sys, os, py_compile, imp # pypy does not use .pyo optimization if hasattr(sys, 'pypy_translation_info'): sys.exit(0) files = '''$files''' sys.stdout.write('Byte-compiling python modules (optimized versions) ...\n') for file in files.split(): $pathtrans $filetrans if not os.path.exists(filepath) or not (len(filepath) >= 3 and filepath[-3:] == '.py'): continue sys.stdout.write(file) sys.stdout.flush() if hasattr(imp, 'get_tag'): py_compile.compile(filepath, imp.cache_from_source(filepath, False), path) else: py_compile.compile(filepath, filepath + 'o', path) sys.stdout.write('\n')" 2>/dev/null || : # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: kea-2.0.2/kea_version.h.in0000644000175000017500000000013514206773363012271 00000000000000#define EXTENDED_VERSION "@KEA_SRCID@" #define PACKAGE_VERSION_TYPE "@PACKAGE_VERSION_TYPE@" kea-2.0.2/doc/0000755000175000017500000000000014206773520010027 500000000000000kea-2.0.2/doc/sphinx/0000755000175000017500000000000014206773520011340 500000000000000kea-2.0.2/doc/sphinx/umls.rst0000644000175000017500000001471214206773363013004 00000000000000.. Copyright (C) 2020-2021 Internet Systems Consortium, Inc. ("ISC") This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. .. _umls: Kea Flow Diagrams ================= These flow diagrams describe the Kea DHCPv4 server implementation. They may be useful for system administrators to understand for several reasons. In order to design a configuration that will result in clients getting the intended addresses and options it is important to understand the sequence of request processing steps. For example, Kea will iterate looking for a suitable address, and will conditionally accept the first available address, so the order in which addresses are evaluated matters. It is also useful to understand Kea's processing logic because there are configuration choices which can make the process far more efficient. Kea is very flexible so that it can be applied to very different use cases and in different environments. In an environment where throughput and efficiency are a priority, the administrator can choose to limit some of the processing steps. For example, it is possible to limit the number of different client identifiers Kea evaluates in looking for a host reservation, or even to skip the whole step of checking for host reservations. These diagrams are focused on those aspects of Kea processing that will be most useful to operators. The diagrams illustrate DHCPv4 request processing, but most of the logic applies to DHCPv6. Following the title of each diagram is a Kea version number. Kea behavior has evolved over time, and the diagrams document the behavior as of the Kea version indicated. The diagrams are provided in the Kea source tree in UML (source), PNG and SVG formats. Main Loop ^^^^^^^^^ The main loop is common to both DHCPv4 and DHPCv6 servers. .. figure:: uml/main-loop.* DHCP server main loop .. _uml_packet4: DHCPv4 Packet Processing ^^^^^^^^^^^^^^^^^^^^^^^^ Next is the DHCPv4 packet processing, where we determine what sort of DHCP message this is, Discover, Request, Release, Decline or Inform. This diagram shows the general, high level flow for processing an inbound client DHCP packet (e.g. Discover, Request, Release, etc) from receipt to the server's response.. .. figure:: uml/packet4.* DHCPv4 packet processing .. _uml_request4: DHCP Request Processing ^^^^^^^^^^^^^^^^^^^^^^^ The following diagrams focus on DHCPREQUEST processing. This chart gives an overview of the process, beginning with subnet selection, proceeding to checking for Host Reservations and evaluating client classes. Finally, before acknowledging the lease, the options are evaluated and added to the message. .. figure:: uml/request4.* DHCPREQUEST processing . .. _uml_select4: DHCPv4 Subnet Selection ^^^^^^^^^^^^^^^^^^^^^^^ Subnet selection is the process of choosing a subnet that is topologically appropriate for the client. Note that when the selected subnet is a member of a shared network the whole shared network is selected. During subnet selection the client class may be checked more than once while iterating through subnets, to determine if it is permitted in the selected subnet. .. figure:: uml/select4.* DHCPv4 subnet selection .. _uml_assign-lease4: DHCPv4 Special Case of Double-booting ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ After the subnet selection and before the lease allocation the DHCPv4 server handles the special case of clients restarting with an image provided by PXE boot or bootp. Note that the Lease Request box is expanded below. .. figure:: uml/assign-lease4.* DHCPv4 Assign Lease .. _uml_request4-lease: DHCPv4 Allocate Lease ^^^^^^^^^^^^^^^^^^^^^ This diagram provides the details of the processing the client request, showing renewing an existing lease, assigning a reserved lease and allocating an unreserved lease. The next diagram after this one shows the algorithm in more detail. .. figure:: uml/request4-lease.* Allocate a lease for DHCPREQUEST This diagram shows the algorithm used to validate a requested lease or select a new address to offer. The far right side of the diagram shows how a new address is selected when a new lease is required and the client has neither a requested address nor a reservation. Note that when a new lease is required and Kea iterates over pools and subnets, it starts with the subnet selected above in the subnet selection process. .. figure:: uml/requestLease4.* requestLease4 algorithm .. note:: Declined addresses are included in the statistic for assigned addresses so the :math:`assigned + free = total` equation is true. .. _uml_lease-states: Lease States ^^^^^^^^^^^^ This diagram illustrates the different lease states including the free one where no lease object exists. .. figure:: uml/lease-states.* lease states .. _uml_currentHost4: Checking for Host Reservations ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The allocation engine checks for host reservations after selecting a subnet. This diagram shows the details of that operation. Subnet selection is based on network topology. Host reservations are primarily for assigning options, and options are evaluated after subnet selection. However if client classes are added in the host reservation, those will also be evaluated against the selected subnet in a further check (added in Kea 1.7.10). Kea includes several options to skip checking for host reservations, which can make this process much more efficient if you are not using reservations. .. note:: To find a free lease the allocation engine begins with evaluating the most recently used subnet. The current subnet depends on the history of prior queries. .. figure:: uml/currentHost4.* currentHost .. _uml_CfgOptionList: Building the Options List ^^^^^^^^^^^^^^^^^^^^^^^^^ Before sending a response, options are added: - evaluate required client classes - build the configured option list - append requested options - append requested vendor options - append basic options .. figure:: uml/buildCfgOptionList.* buildCfgOptionList (build configured option list) algorithm .. figure:: uml/appendRequestedOptions.* appendRequestedOptions (append requested options) algorithm .. figure:: uml/appendRequestedVendorOptions.* appendRequestedVendorOptions (append vendor requested options) algorithm kea-2.0.2/doc/sphinx/arm/0000755000175000017500000000000014206773520012117 500000000000000kea-2.0.2/doc/sphinx/arm/classify.rst0000644000175000017500000013515014206773363014420 00000000000000.. _classify: ********************* Client Classification ********************* Client Classification Overview ============================== In certain cases it is useful to differentiate among different types of clients and treat them accordingly. Common reasons include: - The clients represent different pieces of topology, e.g. a cable modem is not the same as the clients behind that modem. - The clients have different behavior, e.g. a smartphone behaves differently from a laptop. - The clients require different values for some options, e.g. a docsis3.0 cable modem requires different settings from a docsis2.0 cable modem. To make management easier, different clients can be grouped into a client class to receive common options. An incoming packet can be associated with a client class in several ways: - Implicitly, using a vendor class option or another built-in condition. - Using an expression which evaluates to true. - Using static host reservations, a shared network, a subnet, etc. - Using a hook. It is envisaged that client classification will be used to change the behavior of almost any part of the DHCP message processing. There are currently five mechanisms that take advantage of client classification: subnet selection, pool selection, definition of DHCPv4 private (codes 224-254) and code 43 options, assignment of different options, and, for DHCPv4 cable modems, the setting of specific options for use with the TFTP server address and the boot file field. The classification process is conducted in several steps: 1. The ALL class is associated with the incoming packet. 2. Vendor class options are processed. 3. Classes with matching expressions and not marked for later evaluation ("on request" or depending on the KNOWN/UNKNOWN built-in classes) are processed in the order they are defined in the configuration; the boolean expression is evaluated and, if it returns true ("match"), the incoming packet is associated with the class. 4. If a private or code 43 DHCPv4 option is received, it is decoded following its client class or global (or, for option 43, last resort) definition. 5. When the incoming packet belongs to the special class, `DROP`, it is dropped and an informational message is logged with the packet information. 6. A subnet is chosen, possibly based on the class information when some subnets are reserved. More precisely: when choosing a subnet, the server iterates over all of the subnets that are feasible given the information found in the packet (client address, relay address, etc.). It uses the first subnet it finds that either doesn't have a class associated with it, or has a class which matches one of the packet's classes. 7. The server looks for host reservations. If an identifier from the incoming packet matches a host reservation in the subnet or shared network, the packet is associated with the KNOWN class and all classes of the host reservation. If a reservation is not found, the packet is assigned to the UNKNOWN class. 8. Classes with matching expressions - directly, or indirectly using the KNOWN/UNKNOWN built-in classes and not marked for later evaluation ("on request") - are processed in the order they are defined in the configuration; the boolean expression is evaluated and, if it returns true ("match"), the incoming packet is associated with the class. After a subnet is selected, the server determines whether there is a reservation for a given client. Therefore, it is not possible to use KNOWN/UNKNOWN classes to select a shared network or a subnet. 9. When the incoming packet belongs to the special class, `DROP`, it is dropped and an informational message is logged with the packet information. Since Kea version 1.9.8 it is allowed to make DROP class dependent on KNOWN/UNKNOWN classes. 10. If needed, addresses and prefixes from pools are assigned, possibly based on the class information when some pools are reserved for class members. 11. Classes marked as "required" are evaluated in the order in which they are listed: first the shared network, then the subnet, and finally the pools that assigned resources belong to. 12. Options are assigned, again possibly based on the class information in the order that classes were associated with the incoming packet. For DHCPv4 private and code 43 options, this includes class local option definitions. .. .. note:: Client classes in Kea follow the order in which they are specified in the configuration (vs. alphabetical order). Required classes follow the order in which they are required. When determining which options to include in the response, the server examines the union of options from all of the assigned classes. If two or more classes include the same option, the value from the first class examined is used; classes are examined in the order they were associated, so ALL is always the first class and matching required classes are last. As an example, imagine that an incoming packet matches two classes. Class "foo" defines values for an NTP server (option 42 in DHCPv4) and an SMTP server (option 69 in DHCPv4), while class "bar" defines values for an NTP server and a POP3 server (option 70 in DHCPv4). The server examines the three options - NTP, SMTP, and POP3 - and returns any that the client requested. As the NTP server was defined twice, the server chooses only one of the values for the reply; the class from which the value is obtained determined as explained in the previous paragraph. .. note:: Care should be taken with client classification, as it is easy for clients that do not meet any class criteria to be denied service altogether. .. _classification-using-vendor: Built-in Client Classes ======================= Some classes are built-in, so they do not need to be defined. The main example uses Vendor Class information: the server checks whether an incoming DHCPv4 packet includes the vendor class identifier option (60) or an incoming DHCPv6 packet includes the vendor class option (16). If it does, the content of that option is prepended with "VENDOR_CLASS\_" and the result is interpreted as a class. For example, modern cable modems send this option with value "docsis3.0", so the packet belongs to class "VENDOR_CLASS_docsis3.0". The "HA\_" prefix is used by the High Availability hooks library to designate certain servers to process DHCP packets as a result of load balancing. The class name is constructed by prepending the "HA\_" prefix to the name of the server which should process the DHCP packet. This server uses an appropriate pool or subnet to allocate IP addresses (and/or prefixes), based on the assigned client classes. The details can be found in :ref:`high-availability-library`. The BOOTP class is used by the BOOTP hook library to classify and respond to inbound BOOTP queries. Other examples are the ALL class, which all incoming packets belong to, and the KNOWN class, assigned when host reservations exist for a particular client. By convention, built-in classes' names begin with all capital letters. Currently recognized built-in class names are ALL, KNOWN and UNKNOWN, and the prefixes VENDOR_CLASS\_, HA\_, AFTER\_, and EXTERNAL\_. Although the AFTER\_ prefix is a provision for an as-yet-unwritten hook, the EXTERNAL\_ prefix can be freely used; built-in classes are implicitly defined so they never raise warnings if they do not appear in the configuration. .. _classification-using-expressions: Using Expressions in Classification =================================== The expression portion of a classification definition contains operators and values. All values are currently strings; operators take a string or strings and return another string. When all the operations have completed, the result should be a value of "true" or "false". The packet belongs to the class (and the class name is added to the list of classes) if the result is "true". Expressions are written in standard format and can be nested. Expressions are pre-processed during the parsing of the configuration file and converted to an internal representation. This allows certain types of errors to be caught and logged during parsing. Examples of these errors include an incorrect number or type of argument to an operator. The evaluation code also checks for this class of error and generally throws an exception, though this should not occur in a normally functioning system. Other issues, such as the starting position of a substring being outside of the substring or an option not existing in the packet, result in the operator returning an empty string. Dependencies between classes are also checked. For instance, forward dependencies are rejected when the configuration is parsed; an expression can only depend on already-defined classes (including built-in classes) which are evaluated in a previous or the same evaluation phase. This does not apply to the KNOWN or UNKNOWN classes. .. table:: List of Classification Values +-----------------------+-------------------------------+-----------------------+ | Name | Example expression | Example value | +=======================+===============================+=======================+ | String literal | 'example' | 'example' | +-----------------------+-------------------------------+-----------------------+ | Hexadecimal string | 0x5a7d | 'Z}' | | literal | | | +-----------------------+-------------------------------+-----------------------+ | IP address literal | 10.0.0.1 | 0x0a000001 | +-----------------------+-------------------------------+-----------------------+ | Integer literal | 123 | '123' | +-----------------------+-------------------------------+-----------------------+ | Binary content of the | option[123].hex | '(content of the | | option | | option)' | +-----------------------+-------------------------------+-----------------------+ | Option existence | option[123].exists | 'true' | +-----------------------+-------------------------------+-----------------------+ | Binary content of the | option[12].option[34].hex | '(content of the | | sub-option | | sub-option)' | +-----------------------+-------------------------------+-----------------------+ | Sub-Option existence | option[12].option[34].exists | 'true' | +-----------------------+-------------------------------+-----------------------+ | Client class | member('foobar') | 'true' | | membership | | | +-----------------------+-------------------------------+-----------------------+ | Known client | known | member('KNOWN') | +-----------------------+-------------------------------+-----------------------+ | Unknown client | unknown | not member('KNOWN') | +-----------------------+-------------------------------+-----------------------+ | DHCPv4 relay agent | relay4[123].hex | '(content of the RAI | | sub-option | | sub-option)' | +-----------------------+-------------------------------+-----------------------+ | DHCPv6 Relay Options | relay6[nest].option[code].hex | (value of the option) | +-----------------------+-------------------------------+-----------------------+ | DHCPv6 Relay Peer | relay6[nest].peeraddr | 2001:DB8::1 | | Address | | | +-----------------------+-------------------------------+-----------------------+ | DHCPv6 Relay Link | relay6[nest].linkaddr | 2001:DB8::1 | | Address | | | +-----------------------+-------------------------------+-----------------------+ | Interface name of | pkt.iface | eth0 | | packet | | | +-----------------------+-------------------------------+-----------------------+ | Source address of | pkt.src | 10.1.2.3 | | packet | | | +-----------------------+-------------------------------+-----------------------+ | Destination address | pkt.dst | 10.1.2.3 | | of packet | | | +-----------------------+-------------------------------+-----------------------+ | Length of packet | pkt.len | 513 | +-----------------------+-------------------------------+-----------------------+ | Hardware address in | pkt4.mac | 0x010203040506 | | DHCPv4 packet | | | +-----------------------+-------------------------------+-----------------------+ | Hardware length in | pkt4.hlen | 6 | | DHCPv4 packet | | | +-----------------------+-------------------------------+-----------------------+ | Hardware type in | pkt4.htype | 6 | | DHCPv4 packet | | | +-----------------------+-------------------------------+-----------------------+ | ciaddr field in | pkt4.ciaddr | 192.0.2.1 | | DHCPv4 packet | | | +-----------------------+-------------------------------+-----------------------+ | giaddr field in | pkt4.giaddr | 192.0.2.1 | | DHCPv4 packet | | | +-----------------------+-------------------------------+-----------------------+ | yiaddr field in | pkt4.yiaddr | 192.0.2.1 | | DHCPv4 packet | | | +-----------------------+-------------------------------+-----------------------+ | siaddr field in | pkt4.siaddr | 192.0.2.1 | | DHCPv4 packet | | | +-----------------------+-------------------------------+-----------------------+ | Message type in | pkt4.msgtype | 1 | | DHCPv4 packet | | | +-----------------------+-------------------------------+-----------------------+ | Transaction ID (xid) | pkt4.transid | 12345 | | in DHCPv4 packet | | | +-----------------------+-------------------------------+-----------------------+ | Message type in | pkt6.msgtype | 1 | | DHCPv6 packet | | | +-----------------------+-------------------------------+-----------------------+ | Transaction ID in | pkt6.transid | 12345 | | DHCPv6 packet | | | +-----------------------+-------------------------------+-----------------------+ | Vendor option | vendor[*].exists | true | | existence (any | | | | vendor) | | | +-----------------------+-------------------------------+-----------------------+ | Vendor option | vendor[4491].exists | true | | existence (specific | | | | vendor) | | | +-----------------------+-------------------------------+-----------------------+ | Enterprise-id from | vendor.enterprise | 4491 | | vendor option | | | +-----------------------+-------------------------------+-----------------------+ | Vendor sub-option | vendor[4491].option[1].exists | true | | existence | | | +-----------------------+-------------------------------+-----------------------+ | Vendor sub-option | vendor[4491].option[1].hex | docsis3.0 | | content | | | +-----------------------+-------------------------------+-----------------------+ | Vendor class option | vendor-class[*].exist | true | | existence (any | s | | | vendor) | | | +-----------------------+-------------------------------+-----------------------+ | Vendor class option | vendor-class[4491].exists | true | | existence (specific | | | | vendor) | | | +-----------------------+-------------------------------+-----------------------+ | Enterprise-id from | vendor-class.enterprise | 4491 | | vendor class option | | | +-----------------------+-------------------------------+-----------------------+ | First data chunk from | vendor-class[4491].data | docsis3.0 | | vendor class option | | | +-----------------------+-------------------------------+-----------------------+ | Specific data chunk | vendor-class[4491].data[3] | docsis3.0 | | from vendor class | | | | option | | | +-----------------------+-------------------------------+-----------------------+ Notes: - Hexadecimal strings are converted into a string as expected. The starting "0X" or "0x" is removed, and if the string is an odd number of characters a "0" is prepended to it. - IP addresses are converted into strings of length 4 or 16. IPv4, IPv6, and IPv4-embedded IPv6 (e.g. IPv4-mapped IPv6) addresses are supported. - Integers in an expression are converted to 32-bit unsigned integers and are represented as four-byte strings; for example, 123 is represented as 0x0000007b. All expressions that return numeric values use 32-bit unsigned integers, even if the field in the packet is smaller. In general, it is easier to use decimal notation to represent integers, but it is also possible to use hexadecimal notation. When writing an integer in hexadecimal, care should be taken to make sure the value is represented as 32 bits, e.g. use 0x00000001 instead of 0x1 or 0x01. Also, make sure the value is specified in network order, e.g. 1 is represented as 0x00000001. - "option[code].hex" extracts the value of the option with the code "code" from the incoming packet. If the packet doesn't contain the option, it returns an empty string. The string is presented as a byte string of the option payload, without the type code or length fields. - "option[code].exists" checks whether an option with the code "code" is present in the incoming packet. It can be used with empty options. - "member('foobar')" checks whether the packet belongs to the client class "foobar". To avoid dependency loops, the configuration file parser verifies whether client classes were already defined or are built-in, i.e., beginning by "VENDOR_CLASS\_", "AFTER\_" (for the to-come "after" hook) and "EXTERNAL\_" or equal to "ALL", "KNOWN", "UNKNOWN", etc. "known" and "unknown" are shorthand for "member('KNOWN')" and "not member('KNOWN')". Note that the evaluation of any expression using directly or indirectly the "KNOWN" class is deferred after the host reservation lookup (i.e. when the "KNOWN" or "UNKNOWN" partition is determined). - "relay4[code].hex" attempts to extract the value of the sub-option "code" from the option inserted as the DHCPv4 Relay Agent Information (82) option. If the packet doesn't contain a RAI option, or the RAI option doesn't contain the requested sub-option, the expression returns an empty string. The string is presented as a byte string of the option payload without the type code or length fields. This expression is allowed in DHCPv4 only. - "relay4" shares the same representation types as "option"; for instance, "relay4[code].exists" is supported. - "relay6[nest]" allows access to the encapsulations used by any DHCPv6 relays that forwarded the packet. The "nest" level specifies the relay from which to extract the information, with a value of 0 indicating the relay closest to the DHCPv6 server. Negative values allow specifying relays counted from the DHCPv6 client, -1 indicating the relay closest to the client. In general, a negative "nest" level is the same as the number of relays + "nest" level. If the requested encapsulation doesn't exist, an empty string "" is returned. This expression is allowed in DHCPv6 only. - "relay6[nest].option[code]" shares the same representation types as "option"; for instance, "relay6[nest].option[code].exists" is supported. - Expressions starting with "pkt4" can be used only in DHCPv4. They allow access to DHCPv4 message fields. - "pkt6" refers to information from the client request. To access any information from an intermediate relay use "relay6". "pkt6.msgtype" and "pkt6.transid" output a 4-byte binary string for the message type or transaction id. For example the message type SOLICIT will be "0x00000001" or simply 1 as in "pkt6.msgtype == 1". - Vendor option means the Vendor-Identifying Vendor-Specific Information option in DHCPv4 (code 125; see `Section 4 of RFC 3925 `__) and Vendor-Specific Information Option in DHCPv6 (code 17, defined in `Section 21.17 of RFC 8415 `__). Vendor class option means Vendor-Identifying Vendor Class Option in DHCPv4 (code 124; see `Section 3 of RFC 3925 `__) in DHCPv4 and Class Option in DHCPv6 (code 16; see `Section 21.16 of RFC 8415 `__). Vendor options may have sub-options that are referenced by their codes. Vendor class options do not have sub-options, but rather data chunks, which are referenced by index value. Index 0 means the first data chunk, index 1 is for the second data chunk (if present), etc. - In the vendor and vendor-class constructs an asterisk (*) or 0 can be used to specify a wildcard enterprise-id value, i.e. it will match any enterprise-id value. - Vendor Class Identifier (option 60 in DHCPv4) can be accessed using the option[60] expression. - `RFC 3925 `__ and `RFC 8415 `__ allow for multiple instances of vendor options to appear in a single message. The client classification code currently examines the first instance if more than one appear. For the vendor.enterprise and vendor-class.enterprise expressions, the value from the first instance is returned. Please submit a feature request on the `Kea GitLab site `__ to request support for multiple instances. .. table:: List of Classification Expressions +-----------------------+-------------------------+-----------------------+ | Name | Example | Description | +=======================+=========================+=======================+ | Equal | 'foo' == 'bar' | Compare the two | | | | values and return | | | | "true" or "false" | +-----------------------+-------------------------+-----------------------+ | Not | not ('foo' == 'bar') | Logical negation | +-----------------------+-------------------------+-----------------------+ | And | ('foo' == 'bar') and | Logical and | | | ('bar' == 'foo') | | +-----------------------+-------------------------+-----------------------+ | Or | ('foo' == 'bar') or | Logical or | | | ('bar' == 'foo') | | +-----------------------+-------------------------+-----------------------+ | Substring | substring('foobar',0,3) | Return the requested | | | | substring | +-----------------------+-------------------------+-----------------------+ | Concat | concat('foo','bar') | Return the | | | | concatenation of the | | | | strings | +-----------------------+-------------------------+-----------------------+ | Concat (operator +) | 'foo' + 'bar' | Return the | | | | concatenation of the | | | | strings | +-----------------------+-------------------------+-----------------------+ | Ifelse | ifelse('foo' == | Return the branch | | | 'bar','us','them') | value according to | | | | the condition | +-----------------------+-------------------------+-----------------------+ | Hexstring | hexstring('foo', '-') | Converts the value to | | | | a hexadecimal string, | | | | e.g. 0a:1b:2c:3e | +-----------------------+-------------------------+-----------------------+ .. table:: List of Conversion to Text Expressions +-----------------------+---------------------------+------------------------+ | Name | Example | Description | +=======================+===========================+========================+ | AddressToText | addrtotext (192.10.0.1) | Represent the 4 bytes | | | addrtotext (2003:db8::) | of an IPv4 address or | | | | the 16 bytes of an | | | | IPv6 address in human | | | | readable format | +-----------------------+---------------------------+------------------------+ | Int8ToText | int8totext (-1) | Represents the 8 bit | | | | signed integer in text | | | | format | +-----------------------+---------------------------+------------------------+ | Int16ToText | int16totext (-1) | Represents the 16 bit | | | | signed integer in text | | | | format | +-----------------------+---------------------------+------------------------+ | Int32ToText | int32totext (-1) | Represents the 32 bit | | | | signed integer in text | | | | format | +-----------------------+---------------------------+------------------------+ | UInt8ToText | uint8totext (255) | Represents the 8 bit | | | | unsigned integer in | | | | text format | +-----------------------+---------------------------+------------------------+ | UInt16ToText | uint16totext (65535) | Represents the 16 bit | | | | unsigned integer in | | | | text format | +-----------------------+---------------------------+------------------------+ | UInt32ToText | uint32totext (4294967295) | Represents the 32 bit | | | | unsigned integer in | | | | text format | +-----------------------+---------------------------+------------------------+ Notes: The conversion operators can be used to transform data from binary to the text representation. The only requirement is that the input data type length matches an expected value. The AddressToText token expects 4 bytes for IPv4 addresses or 16 bytes for IPv6 addresses. The Int8ToText and UInt8ToText expect 1 byte, the Int16ToText and UInt16ToText expect 2 bytes and Int32ToText and UInt32ToText expect 4 bytes. For all conversion tokens, if the data length is 0, the result string is empty. Logical operators ----------------- The Not, And, and Or logical operators are the common operators. Not has the highest precedence and Or the lowest. And and Or are (left) associative. Parentheses around a logical expression can be used to enforce a specific grouping; for instance, in "A and (B or C)" (without parentheses "A and B or C" means "(A and B) or C"). Substring --------- The substring operator "substring(value, start, length)" accepts both positive and negative values for the starting position and the length. For "start", a value of 0 is the first byte in the string while -1 is the last byte. If the starting point is outside of the original string an empty string is returned. "length" is the number of bytes to extract. A negative number means to count towards the beginning of the string but does not include the byte pointed to by "start". The special value "all" means to return all bytes from start to the end of the string. If the length is longer than the remaining portion of the string, then the entire remaining portion is returned. Some examples may be helpful: :: substring('foobar', 0, 6) == 'foobar' substring('foobar', 3, 3) == 'bar' substring('foobar', 3, all) == 'bar' substring('foobar', 1, 4) == 'ooba' substring('foobar', -5, 4) == 'ooba' substring('foobar', -1, -3) == 'oba' substring('foobar', 4, -2) == 'ob' substring('foobar', 10, 2) == '' Concat ------ The concat function "concat(string1, string2)" returns the concatenation of its two arguments. For instance: :: concat('foo', 'bar') == 'foobar' For user convenience Kea version 1.9.8 added an associative operator version of the concat function. For instance: :: 'abc' + 'def' + 'ghi' + 'jkl' + '...' is the same as: :: concat(concat(concat(concat('abc', 'def'), 'ghi'), 'jkl'), '...') or: :: concat('abc', concat('def', concat('ghi', concat('jkl', '...')))) or: :: 'abcdefghijkl...' Ifelse ------ The ifelse function "ifelse(cond, iftrue, ifelse)" returns the "iftrue" or "ifelse" branch value following the boolean condition "cond". For instance: :: ifelse(option[230].exists, option[230].hex, 'none') Hexstring --------- The hexstring function "hexstring(binary, separator)" returns the binary value as its hexadecimal string representation: pairs of hexadecimal digits separated by the separator, e.g ':', '-', '' (empty separator). :: hexstring(pkt4.mac, ':') .. .. note:: The expression for each class is executed on each packet received. If the expressions are overly complex, the time taken to execute them may impact the performance of the server. Administrators who need complex or time-consuming expressions should consider writing a :ref:`hook ` to perform the necessary work. .. _classification-configuring: Configuring Classes =================== A class contains five items: a name, a test expression, option data, an option definition, and an only-if-required flag. The name must exist and must be unique among all classes. The test expression, option data and definition, and only-if-required flag are optional. The test expression is a string containing the logical expression used to determine membership in the class. The entire expression is in double quotes. The option data is a list which defines any options that should be assigned to members of this class. The option definition is for DHCPv4 option 43 (:ref:`dhcp4-vendor-opts`) and DHCPv4 private options (:ref:`dhcp4-private-opts`). Usually the test expression is evaluated before subnet selection, but in some cases it is useful to evaluate it later when the subnet, shared network, or pools are known but output option processing has not yet been done. The only-if-required flag, false by default, allows the evaluation of the test expression only when it is required, i.e. in a require-client-classes list of the selected subnet, shared network, or pool. The require-client-classes list which is valid for shared-network, subnet, and pool scope specifies the classes which are evaluated in the second pass before output option processing. The list is built in the reversed precedence order of option data, i.e. an option data item in a subnet takes precedence over one in a shared network, but required class in a subnet is added after one in a shared network. The mechanism is related to the only-if-required flag but it is not mandatory that the flag be set to true. In the following example, the class named "Client_foo" is defined. It is comprised of all clients whose client ids (option 61) start with the string "foo". Members of this class will be given 192.0.2.1 and 192.0.2.2 as their domain name servers. :: "Dhcp4": { "client-classes": [ { "name": "Client_foo", "test": "substring(option[61].hex,0,3) == 'foo'", "option-data": [ { "name": "domain-name-servers", "code": 6, "space": "dhcp4", "csv-format": true, "data": "192.0.2.1, 192.0.2.2" } ] }, ... ], ... } This example shows a client class being defined for use by the DHCPv6 server. In it the class named "Client_enterprise" is defined. It is comprised of all clients whose client identifiers start with the given hex string (which would indicate a DUID based on an enterprise id of 0xAABBCCDD). Members of this class will be given an 2001:db8:0::1 and 2001:db8:2::1 as their domain name servers. :: "Dhcp6": { "client-classes": [ { "name": "Client_enterprise", "test": "substring(option[1].hex,0,6) == 0x0002AABBCCDD", "option-data": [ { "name": "dns-servers", "code": 23, "space": "dhcp6", "csv-format": true, "data": "2001:db8:0::1, 2001:db8:2::1" } ] }, ... ], ... } .. _classification-using-host-reservations: Using Static Host Reservations In Classification ================================================ Classes can be statically assigned to the clients using techniques described in :ref:`reservation4-client-classes` and :ref:`reservation6-client-classes`. .. _classification-subnets: Configuring Subnets With Class Information ========================================== In certain cases it is beneficial to restrict access to certain subnets only to clients that belong to a given class, using the "client-class" keyword when defining the subnet. Let's assume that the server is connected to a network segment that uses the 192.0.2.0/24 prefix. The administrator of that network has decided that addresses from the range 192.0.2.10 to 192.0.2.20 are going to be managed by the DHCP4 server. Only clients belonging to client class Client_foo are allowed to use this subnet. Such a configuration can be achieved in the following way: :: "Dhcp4": { "client-classes": [ { "name": "Client_foo", "test": "substring(option[61].hex,0,3) == 'foo'", "option-data": [ { "name": "domain-name-servers", "code": 6, "space": "dhcp4", "csv-format": true, "data": "192.0.2.1, 192.0.2.2" } ] }, ... ], "subnet4": [ { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ], "client-class": "Client_foo" }, ... ],, ... } The following example shows how to restrict access to a DHCPv6 subnet. This configuration will restrict use of the addresses 2001:db8:1::1 to 2001:db8:1::FFFF to members of the "Client_enterprise" class. :: "Dhcp6": { "client-classes": [ { "name": "Client_enterprise", "test": "substring(option[1].hex,0,6) == 0x0002AABBCCDD", "option-data": [ { "name": "dns-servers", "code": 23, "space": "dhcp6", "csv-format": true, "data": "2001:db8:0::1, 2001:db8:2::1" } ] }, ... ], "subnet6": [ { "subnet": "2001:db8:1::/64", "pools": [ { "pool": "2001:db8:1::-2001:db8:1::ffff" } ], "client-class": "Client_enterprise" } ], ... } .. _classification-pools: Configuring Pools With Class Information ======================================== Similar to subnets, in certain cases access to certain address or prefix pools must be restricted to only clients that belong to a given class, using the "client-class" when defining the pool. Let's assume that the server is connected to a network segment that uses the 192.0.2.0/24 prefix. The administrator of that network has decided that addresses from the range 192.0.2.10 to 192.0.2.20 are going to be managed by the DHCP4 server. Only clients belonging to client class Client_foo are allowed to use this pool. Such a configuration can be achieved in the following way: :: "Dhcp4": { "client-classes": [ { "name": "Client_foo", "test": "substring(option[61].hex,0,3) == 'foo'", "option-data": [ { "name": "domain-name-servers", "code": 6, "space": "dhcp4", "csv-format": true, "data": "192.0.2.1, 192.0.2.2" } ] }, ... ], "subnet4": [ { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.10 - 192.0.2.20", "client-class": "Client_foo" } ] }, ... ],, } The following example shows how to restrict access to an address pool. This configuration will restrict use of the addresses 2001:db8:1::1 to 2001:db8:1::FFFF to members of the "Client_enterprise" class. :: "Dhcp6": { "client-classes": [ { "name": "Client_enterprise_", "test": "substring(option[1].hex,0,6) == 0x0002AABBCCDD", "option-data": [ { "name": "dns-servers", "code": 23, "space": "dhcp6", "csv-format": true, "data": "2001:db8:0::1, 2001:db8:2::1" } ] }, ... ], "subnet6": [ { "subnet": "2001:db8:1::/64", "pools": [ { "pool": "2001:db8:1::-2001:db8:1::ffff", "client-class": "Client_foo" } ] }, ... ], ... } Using Classes ============= Currently classes can be used for two functions: they can supply options to members of the class, and they can be used to choose a subnet from which an address will be assigned to a class member. When supplying options, options defined as part of the class definition are considered "class globals." They will override any global options that may be defined and in turn will be overridden by any options defined for an individual subnet. Classes and Hooks ================= Hooks may be used to classify packets. This may be useful if the expression would be complex or time-consuming to write, and could be better or more easily written as code. Once the hook has added the proper class name to the packet, the rest of the classification system will work as expected in choosing a subnet and selecting options. For a description of hooks, see :ref:`hooks-libraries`; for information on configuring classes, see :ref:`classification-configuring` and :ref:`classification-subnets`. Debugging Expressions ===================== While constructing classification expressions, administrators may find it useful to enable logging; see :ref:`logging` for a more complete description of the logging facility. To enable the debug statements in the classification system, the severity must be set to "DEBUG" and the debug level to at least 55. The specific loggers are "kea-dhcp4.eval" and "kea-dhcp6.eval". To understand the logging statements, it is essential to understand a bit about how expressions are evaluated; for a more complete description, refer to the design document at https://gitlab.isc.org/isc-projects/kea/wikis/designs/Design-documents. In brief, there are two structures used during the evaluation of an expression: a list of tokens which represent the expressions, and a value stack which represents the values being manipulated. The list of tokens is created when the configuration file is processed, with most expressions and values being converted to a token. The list is organized in reverse Polish notation. During execution, the list will be traversed in order; as each token is executed it will be able to pop values from the top of the stack and eventually push its result on the top of the stack. Imagine the following expression: :: "test": "substring(option[61].hex,0,3) == 'foo'", This will result in the following tokens: :: option, number (0), number (3), substring, text ('foo'), equals In this example the first three tokens will simply push values onto the stack. The substring token will then remove those three values and compute a result that it places on the stack. The text option also places a value on the stack and finally the equals token removes the two tokens on the stack and places its result on the stack. When debug logging is enabled, each time a token is evaluated it will emit a log message indicating the values of any objects that were popped off of the value stack and any objects that were pushed onto the value stack. The values will be displayed as either text, if the command is known to use text values, or hexadecimal, if the command either uses binary values or can manipulate either text or binary values. For expressions that pop multiple values off the stack, the values will be displayed in the order they were popped. For most expressions this will not matter, but for the concat expression the values are displayed in reverse order from their written order in the expression. Let us assume that the following test has been entered into the configuration. This example skips most of the configuration to concentrate on the test. :: "test": "substring(option[61].hex,0,3) == 'foo'", The logging might then resemble this: :: 2016-05-19 13:35:04.163 DEBUG [kea.eval/44478] EVAL_DEBUG_OPTION Pushing option 61 with value 0x666F6F626172 2016-05-19 13:35:04.164 DEBUG [kea.eval/44478] EVAL_DEBUG_STRING Pushing text string '0' 2016-05-19 13:35:04.165 DEBUG [kea.eval/44478] EVAL_DEBUG_STRING Pushing text string '3' 2016-05-19 13:35:04.166 DEBUG [kea.eval/44478] EVAL_DEBUG_SUBSTRING Popping length 3, start 0, string 0x666F6F626172 pushing result 0x666F6F 2016-05-19 13:35:04.167 DEBUG [kea.eval/44478] EVAL_DEBUG_STRING Pushing text string 'foo' 2016-05-19 13:35:04.168 DEBUG [kea.eval/44478] EVAL_DEBUG_EQUAL Popping 0x666F6F and 0x666F6F pushing result 'true' .. .. note:: The debug logging may be quite verbose if there are a number of expressions to evaluate; that is intended as an aid in helping create and debug expressions. Administrators should plan to disable debug logging when the expressions are working correctly. Users may also wish to include only one set of expressions at a time in the configuration file while debugging them, to limit the log statements. For example, when adding a new set of expressions, an administrator might find it more convenient to create a configuration file that only includes the new expressions until they are working correctly, and then add the new set to the main configuration file. kea-2.0.2/doc/sphinx/arm/ddns.rst0000644000175000017500000010534314206773363013534 00000000000000.. _dhcp-ddns-server: ******************** The DHCP-DDNS Server ******************** .. _dhcp-ddns-overview: Overview ======== The DHCP-DDNS Server (kea-dhcp-ddns, known informally as D2) conducts the client side of the Dynamic DNS protocol (DDNS, defined in `RFC 2136 `__) on behalf of the DHCPv4 and DHCPv6 servers (kea-dhcp4 and kea-dhcp6 respectively). The DHCP servers construct DDNS update requests, known as Name Change Requests (NCRs), based on DHCP lease change events and then post them to D2. D2 attempts to match each request to the appropriate DNS server(s) and carries out the necessary conversation with those servers to update the DNS data. .. _dhcp-ddns-dns-server-selection: DNS Server Selection -------------------- In order to match a request to the appropriate DNS servers, D2 must have a catalog of servers from which to select. In fact, D2 has two such catalogs, one for forward DNS and one for reverse DNS; these catalogs are referred to as DDNS Domain Lists. Each list consists of one or more named DDNS Domains. Further, each DDNS Domain has a list of one or more DNS servers that publish the DNS data for that domain. When conducting forward domain matching, D2 compares the fully qualified domain name (FQDN) in the request against the name of each Forward DDNS Domain in its catalog. The domain whose name matches the longest portion of the FQDN is considered the best match. For example, if the FQDN is "myhost.sample.example.com.", and there are two forward domains in the catalog, "sample.example.com." and "example.com.", the former is regarded as the best match. In some cases, it may not be possible to find a suitable match. Given the same two forward domains there would be no match for the FQDN, "bogus.net", so the request would be rejected. Finally, if there are no Forward DDNS Domains defined, D2 simply disregards the forward update portion of requests. When conducting reverse domain matching, D2 constructs a reverse FQDN from the lease address in the request and compares that against the name of each Reverse DDNS Domain. Again, the domain whose name matches the longest portion of the FQDN is considered the best match. For instance, if the lease address is "172.16.1.40" and there are two reverse domains in the catalog, "1.16.172.in-addr.arpa." and "16.172.in-addr.arpa", the former is the best match. As with forward matching, it may not find a suitable match. Given the same two domains, there would be no match for the lease address, "192.168.1.50", and the request would be rejected. Finally, if there are no Reverse DDNS Domains defined, D2 simply disregards the reverse update portion of requests. .. _dhcp-ddns-conflict-resolution: Conflict Resolution ------------------- D2 implements the conflict resolution strategy prescribed by `RFC 4703 `__. Conflict resolution is intended to prevent different clients from mapping to the same FQDN at the same time. To make this possible, the RFC requires that forward DNS entries for a given FQDN must be accompanied by a DHCID resource record (RR). This record contains a client identifier that uniquely identifies the client to whom the name belongs. Furthermore, any DNS updater that wishes to update or remove existing forward entries for an FQDN may only do so if their client matches that of the DHCID RR. In other words, the DHCID RR maps an FQDN to the client to whom it belongs, and thereafter changes to that mapping should only be done by or at the behest of that client. Currently, conflict detection is always performed. .. _dhcp-ddns-dual-stack: Dual-Stack Environments ----------------------- `RFC 4703, section 5.2, `__ describes issues that may arise with dual-stack clients. These are clients that wish to have both IPv4 and IPv6 mappings for the same FQDN. For this to work properly, the clients are required to embed their IPv6 DUID within their IPv4 client identifier option, as described in `RFC 4703 `__. In this way, DNS updates for both IPv4 and IPv6 can be managed under the same DHCID RR. Kea does not currently support this feature. .. _dhcp-ddns-server-start-stop: Starting and Stopping the DHCP-DDNS Server ========================================== ``kea-dhcp-ddns`` is the Kea DHCP-DDNS server and, due to the nature of DDNS, it runs alongside either the DHCPv4 or DHCPv6 component (or both). Like other parts of Kea, it is a separate binary that can be run on its own or through ``keactrl`` (see :ref:`keactrl`). In normal operation, controlling ``kea-dhcp-ddns`` with ``keactrl`` is recommended; however, it is also possible to run the DHCP-DDNS server directly. It accepts the following command-line switches: - ``-c file`` - specifies the configuration file. This is the only mandatory switch. - ``-d`` - specifies whether the server logging should be switched to debug/verbose mode. In verbose mode, the logging severity and debuglevel specified in the configuration file are ignored and "debug" severity and the maximum debuglevel (99) are assumed. The flag is convenient for temporarily switching the server into maximum verbosity, e.g. when debugging. - ``-v`` - displays the Kea version and exits. - ``-W`` - displays the Kea configuration report and exits. The report is a copy of the ``config.report`` file produced by ``./configure``; it is embedded in the executable binary. - ``-t file`` - specifies the configuration file to be tested. Kea-dhcp-ddns will attempt to load it and will conduct sanity checks. Note that certain checks are possible only while running the actual server. The actual status is reported with an exit code (0 = configuration looks ok, 1 = error encountered). Kea prints out log messages to standard output and errors to standard error when testing the configuration. The ``config.report`` may also be accessed more directly, via the following command. The binary ``path`` may be found in the install directory or in the ``.libs`` subdirectory in the source tree. For example: ``kea/src/bin/d2/.libs/kea-dhcp-ddns``. :: strings path/kea-dhcp-ddns | sed -n 's/;;;; //p' Upon startup, the module will load its configuration and begin listening for NCRs based on that configuration. During startup, the server will attempt to create a PID file of the form: [**runstatedir**]/[**conf name**].kea-dhcp-ddns.pid where: - ``runstatedir`` - is the value as passed into the build configure script; it defaults to "/usr/local/var/run". Note that this value may be overridden at runtime by setting the environment variable KEA_PIDFILE_DIR. This is intended primarily for testing purposes. - ``conf name`` - is the configuration file name used to start the server, minus all preceding paths and the file extension. For example, given a pathname of "/usr/local/etc/kea/myconf.txt", the portion used would be "myconf". If the file already exists and contains the PID of a live process, the server will issue a DHCP_DDNS_ALREADY_RUNNING log message and exit. It is possible, though unlikely, that the file is a remnant of a system crash and the process to which the PID belongs is unrelated to Kea. In such a case it is necessary to manually delete the PID file. .. _d2-configuration: Configuring the DHCP-DDNS Server ================================ Before starting the ``kea-dhcp-ddns`` module for the first time, a configuration file must be created. The following default configuration is a template that can be customized to individual requirements. :: "DhcpDdns": { "ip-address": "127.0.0.1", "port": 53001, "dns-server-timeout": 100, "ncr-protocol": "UDP", "ncr-format": "JSON", "tsig-keys": [ ], "forward-ddns": { "ddns-domains": [ ] }, "reverse-ddns": { "ddns-domains": [ ] } } The configuration can be divided into the following sections, each of which is described below: - *Global Server Parameters* - define values which control connectivity and global server behavior. - *Control Socket* - defines the Control Socket type and name. - *TSIG Key Info* - defines the TSIG keys used for secure traffic with DNS servers. - *Forward DDNS* - defines the catalog of Forward DDNS Domains. - *Reverse DDNS* - defines the catalog of Forward DDNS Domains. .. _d2-server-parameter-config: Global Server Parameters ------------------------ - ``ip-address`` - the IP address on which D2 listens for requests. The default is the local loopback interface at address 127.0.0.1. Either an IPv4 or IPv6 address may be specified. - ``port`` - the port on which D2 listens for requests. The default value is 53001. - ``dns-server-timeout`` - the maximum amount of time, in milliseconds, that D2 will wait for a response from a DNS server to a single DNS update message. - ``ncr-protocol`` - the socket protocol to use when sending requests to D2. Currently only UDP is supported. - ``ncr-format`` - the packet format to use when sending requests to D2. Currently only JSON format is supported. D2 must listen for change requests on a known address and port. By default it listens at 127.0.0.1 on port 53001. The following example illustrates how to change D2's global parameters so it will listen at 192.168.1.10 port 900: :: "DhcpDdns": { "ip-address": "192.168.1.10", "port": 900, ... } } .. .. warning:: It is possible for a malicious attacker to send bogus NameChangeRequests to the DHCP-DDNS server. Addresses other than the IPv4 or IPv6 loopback addresses (127.0.0.1 or ::1) should only be used for testing purposes; note that local users may still communicate with the DHCP-DDNS server. .. note:: If the ip-address and port are changed, the corresponding values in the DHCP servers' "dhcp-ddns" configuration section must be changed. .. _d2-ctrl-channel: Management API for the D2 Server -------------------------------- The management API allows the issuing of specific management commands, such as configuration retrieval or shutdown. For more details, see :ref:`ctrl-channel`. Currently, the only supported communication channel type is UNIX stream socket. By default there are no sockets open; to instruct Kea to open a socket, the following entry in the configuration file can be used: :: "DhcpDdns": { "control-socket": { "socket-type": "unix", "socket-name": "/path/to/the/unix/socket" }, ... } The length of the path specified by the ``socket-name`` parameter is restricted by the maximum length for the UNIX socket name on the operating system, i.e. the size of the ``sun_path`` field in the ``sockaddr_un`` structure, decreased by 1. This value varies on different operating systems between 91 and 107 characters. Typical values are 107 on Linux and 103 on FreeBSD. Communication over the control channel is conducted using JSON structures. See the `Control Channel section in the Kea Developer's Guide `__ for more details. The D2 server supports the following operational commands: - build-report - config-get - config-reload - config-set - config-test - config-write - list-commands - shutdown - status-get - version-get Starting with Kea version 2.0.0 the D2 server supports too the following operational commands for statistics: - statistic-get - statistic-get-all - statistic-reset - statistic-reset-all The ``shutdown`` command supports the extra ``type`` argument which controls the way the D2 server cleans up on exit. The supported shutdown types are: - ``normal`` - Stops the queue manager and finishes all current transactions before exiting. This is the default. - ``drain_first`` - Stops the queue manager but continues processing requests from the queue until it is empty. - ``now`` - Exits immediately. An example command may look like this: :: { "command": "shutdown" "arguments": { "exit-value": 3, "type": "drain_first" } } .. _d2-tsig-key-list-config: TSIG Key List ------------- A DDNS protocol exchange can be conducted with or without TSIG (defined in `RFC 2845 `__). This configuration section allows the administrator to define the set of TSIG keys that may be used in such exchanges. To use TSIG when updating entries in a DNS domain, a key must be defined in the TSIG Key list and referenced by name in that domain's configuration entry. When D2 matches a change request to a domain, it checks whether the domain has a TSIG key associated with it. If so, D2 uses that key to sign DNS update messages sent to and verify responses received from the domain's DNS server(s). For each TSIG key required by the DNS servers that D2 will be working with, there must be a corresponding TSIG key in the TSIG Key list. As one might gather from the name, the tsig-key section of the D2 configuration lists the TSIG keys. Each entry describes a TSIG key used by one or more DNS servers to authenticate requests and sign responses. Every entry in the list has three parameters: - ``name`` - is a unique text label used to identify this key within the list. This value is used to specify which key (if any) should be used when updating a specific domain. As long as the name is unique its content is arbitrary, although for clarity and ease of maintenance it is recommended that it match the name used on the DNS server(s). This field cannot be blank. - ``algorithm`` - specifies which hashing algorithm should be used with this key. This value must specify the same algorithm used for the key on the DNS server(s). The supported algorithms are listed below: - HMAC-MD5 - HMAC-SHA1 - HMAC-SHA224 - HMAC-SHA256 - HMAC-SHA384 - HMAC-SHA512 This value is not case-sensitive. - ``digest-bits`` - is used to specify the minimum truncated length in bits. The default value 0 means truncation is forbidden; non-zero values must be an integral number of octets, and be greater than both 80 and half of the full length. (Note that in BIND 9 this parameter is appended after a dash to the algorithm name.) - ``secret`` - is used to specify the shared secret key code for this key. This value is case-sensitive and must exactly match the value specified on the DNS server(s). It is a base64-encoded text value. As an example, suppose that a domain D2 will be updating is maintained by a BIND 9 DNS server, which requires dynamic updates to be secured with TSIG. Suppose further that the entry for the TSIG key in BIND 9's named.conf file looks like this: :: : key "key.four.example.com." { algorithm hmac-sha224; secret "bZEG7Ow8OgAUPfLWV3aAUQ=="; }; : By default, the TSIG Key list is empty: :: "DhcpDdns": { "tsig-keys": [ ], ... } We must extend the list with a new key: :: "DhcpDdns": { "tsig-keys": [ { "name": "key.four.example.com.", "algorithm": "HMAC-SHA224", "secret": "bZEG7Ow8OgAUPfLWV3aAUQ==" } ], ... } These steps would be repeated for each TSIG key needed. Note that the same TSIG key can be used with more than one domain. .. _d2-forward-ddns-config: Forward DDNS ------------ The Forward DDNS section is used to configure D2's forward update behavior. Currently it contains a single parameter, the catalog of Forward DDNS Domains, which is a list of structures. :: "DhcpDdns": { "forward-ddns": { "ddns-domains": [ ] }, ... } By default, this list is empty, which will cause the server to ignore the forward update portions of requests. .. _add-forward-ddns-domain: Adding Forward DDNS Domains ~~~~~~~~~~~~~~~~~~~~~~~~~~~ A Forward DDNS Domain maps a forward DNS zone to a set of DNS servers which maintain the forward DNS data (i.e. name-to-address mapping) for that zone. Each zone served needs one Forward DDNS Domain. It may very well be that some or all of the zones are maintained by the same servers, but one DDNS Domain is still needed for each zone. Remember that matching a request to the appropriate server(s) is done by zone and a DDNS Domain only defines a single zone. This section describes how to add Forward DDNS Domains; repeat these steps for each Forward DDNS Domain desired. Each Forward DDNS Domain has the following parameters: - ``name`` - the fully qualified domain name (or zone) that this DDNS Domain can update. This value is compared against the request FQDN during forward matching. It must be unique within the catalog. - ``key-name`` - if TSIG is used with this domain's servers, this value should be the name of the key from the TSIG Key list. If the value is blank (the default), TSIG will not be used in DDNS conversations with this domain's servers. - ``dns-servers`` - a list of one or more DNS servers which can conduct the server side of the DDNS protocol for this domain. The servers are used in a first-to-last preference; in other words, when D2 begins to process a request for this domain, it will pick the first server in this list and attempt to communicate with it. If that attempt fails, D2 will move to the next one in the list and so on until either it is successful or the list is exhausted. To create a new Forward DDNS Domain, add a new domain element and set its parameters: :: "DhcpDdns": { "forward-ddns": { "ddns-domains": [ { "name": "other.example.com.", "key-name": "", "dns-servers": [ ] } ] } } It is possible to add a domain without any servers; however, if that domain matches a request, the request will fail. To make the domain useful, at least one DNS server must be added to it. .. _add-forward-dns-servers: Adding Forward DNS Servers ^^^^^^^^^^^^^^^^^^^^^^^^^^ This section describes how to add DNS servers to a Forward DDNS Domain. Repeat these instructions as needed for all the servers in each domain. Forward DNS Server entries represent actual DNS servers which support the server side of the DDNS protocol. Each Forward DNS Server has the following parameters: - ``hostname`` - the resolvable host name of the DNS server; this parameter is not yet implemented. - ``ip-address`` - the IP address at which the server listens for DDNS requests. This may be either an IPv4 or an IPv6 address. - ``port`` - the port on which the server listens for DDNS requests. It defaults to the standard DNS service port of 53. To create a new Forward DNS Server, a new server element must be added to the domain and its parameters filled in. If, for example, the service is running at "172.88.99.10", set the Forward DNS Server as follows: :: "DhcpDdns": { "forward-ddns": { "ddns-domains": [ { "name": "other.example.com.", "key-name": "", "dns-servers": [ { "ip-address": "172.88.99.10", "port": 53 } ] } ] } } .. .. note:: Since "hostname" is not yet supported, the parameter "ip-address" must be set to the address of the DNS server. .. _d2-reverse-ddns-config: Reverse DDNS ------------ The Reverse DDNS section is used to configure D2's reverse update behavior, and the concepts are the same as for the forward DDNS section. Currently it contains a single parameter, the catalog of Reverse DDNS Domains, which is a list of structures. :: "DhcpDdns": { "reverse-ddns": { "ddns-domains": [ ] } ... } By default, this list is empty, which will cause the server to ignore the reverse update portions of requests. .. _add-reverse-ddns-domain: Adding Reverse DDNS Domains ~~~~~~~~~~~~~~~~~~~~~~~~~~~ A Reverse DDNS Domain maps a reverse DNS zone to a set of DNS servers which maintain the reverse DNS data (address-to-name mapping) for that zone. Each zone served needs one Reverse DDNS Domain. It may very well be that some or all of the zones are maintained by the same servers, but one DDNS Domain entry is still needed for each zone. Remember that matching a request to the appropriate server(s) is done by zone and a DDNS Domain only defines a single zone. This section describes how to add Reverse DDNS Domains; repeat these steps for each Reverse DDNS Domain desired. Each Reverse DDNS Domain has the following parameters: - ``name`` - the fully qualified reverse zone that this DDNS domain can update. This is the value used during reverse matching, which will compare it with a reversed version of the request's lease address. The zone name should follow the appropriate standards; for example, to support the IPv4 subnet 172.16.1, the name should be "1.16.172.in-addr.arpa.". Similarly, to support an IPv6 subnet of 2001:db8:1, the name should be "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa." Whatever the name, it must be unique within the catalog. - ``key-name`` - if TSIG is used with this domain's servers, this value should be the name of the key from the TSIG Key List. If the value is blank (the default), TSIG will not be used in DDNS conversations with this domain's servers. - ``dns-servers`` - a list of one or more DNS servers which can conduct the server side of the DDNS protocol for this domain. Currently, the servers are used in a first-to-last preference; in other words, when D2 begins to process a request for this domain, it will pick the first server in this list and attempt to communicate with it. If that attempt fails, D2 will move to the next one in the list and so on until either it is successful or the list is exhausted. To create a new Reverse DDNS Domain, a new domain element must be added and its parameters set. For example, to support subnet 2001:db8:1::, the following configuration could be used: :: "DhcpDdns": { "reverse-ddns": { "ddns-domains": [ { "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.", "key-name": "", "dns-servers": [ ] } ] } } It is possible to add a domain without any servers; however, if that domain matches a request, the request will fail. To make the domain useful, at least one DNS server must be added to it. .. _add-reverse-dns-servers: Adding Reverse DNS Servers ^^^^^^^^^^^^^^^^^^^^^^^^^^ This section describes how to add DNS servers to a Reverse DDNS Domain. Repeat these instructions as needed for all the servers in each domain. Reverse DNS Server entries represent actual DNS servers which support the server side of the DDNS protocol. Each Reverse DNS Server has the following parameters: - ``hostname`` - the resolvable host name of the DNS server; this value is currently ignored. - ``ip-address`` - the IP address at which the server listens for DDNS requests. - ``port`` - the port on which the server listens for DDNS requests. It defaults to the standard DNS service port of 53. To create a new reverse DNS Server, a new server element must be added to the domain and its parameters filled in. If, for example, the service is running at "172.88.99.10", then set it as follows: :: "DhcpDdns": { "reverse-ddns": { "ddns-domains": [ { "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.", "key-name": "", "dns-servers": [ { "ip-address": "172.88.99.10", "port": 53 } ] } ] } } .. .. note:: Since "hostname" is not yet supported, the parameter "ip-address" must be set to the address of the DNS server. .. _per-server-keys: Per DNS server TSIG keys ~~~~~~~~~~~~~~~~~~~~~~~~ Since Kea version 2.0.0 a TSIG key can be specified in a DNS server configuration. The priority rule is: - if a not empty key name is specified in a DNS server entry this TSIG key will protect DNS updates sent to this server. - if empty or no key name is specified in a DNS server entry but a not empty key name is specified in the parent domain entry, the domain TSIG key will protect DNS updates sent to this server. - if empty or no key name is specified in a DNS server entry and its parent domain entry, no TSIG will protect DNS updates sent to this server. For instance in this configuration: :: "DhcpDdns": { "forward-ddns": { "ddns-domains": [ { "name": "other.example.com.", "key-name": "foo", "dns-servers": [ { "ip-address": "172.88.99.10", "port": 53 }, { "ip-address": "172.88.99.11", "port": 53, "key-name": "bar" } ] } ] }, "reverse-ddns": { "ddns-domains": [ { "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.", "dns-servers": [ { "ip-address": "172.88.99.12", "port": 53 }, { "ip-address": "172.88.99.13", "port": 53, "key-name": "bar" } ] } ] }, "tsig-keys": [ { "name": "foo", "algorithm": "HMAC-MD5", "secret": "LSWXnfkKZjdPJI5QxlpnfQ==" }, { "name": "bar", "algorithm": "HMAC-SHA224", "secret": "bZEG7Ow8OgAUPfLWV3aAUQ==" } ] } The 172.88.99.10 server will use the foo TSIG key, 172.88.99.11 and 172.88.99.13 servers the bar one and 172.88.99.12 will not use TSIG. .. _d2-user-contexts: User Contexts in DDNS --------------------- See :ref:`user-context` for additional background regarding the user context idea. User contexts can be specified on global scope, DDNS domain, DNS server, TSIG key, and loggers. One other useful usage is the ability to store comments or descriptions; the parser translates a "comment" entry into a user context with the entry, which allows a comment to be attached inside the configuration itself. .. _d2-example-config: Example DHCP-DDNS Server Configuration -------------------------------------- This section provides a sample DHCP-DDNS server configuration, based on a small example network. Let's suppose our example network has three domains, each with their own subnet. .. table:: Our Example Network +------------------+-----------------+-----------------+-----------------+ | Domain | Subnet | Forward DNS | Reverse DNS | | | | Servers | Servers | +==================+=================+=================+=================+ | four.example.com | 192.0.2.0/24 | 172.16.1.5, | 172.16.1.5, | | | | 172.16.2.5 | 172.16.2.5 | +------------------+-----------------+-----------------+-----------------+ | six.example.com | 2001:db8:1::/64 | 3001:1::50 | 3001:1::51 | +------------------+-----------------+-----------------+-----------------+ | example.com | 192.0.0.0/16 | 172.16.2.5 | 172.16.2.5 | +------------------+-----------------+-----------------+-----------------+ We need to construct three Forward DDNS Domains: .. table:: Forward DDNS Domains Needed +----+-------------------+------------------------+ | # | DDNS Domain Name | DNS Servers | +====+===================+========================+ | 1. | four.example.com. | 172.16.1.5, 172.16.2.5 | +----+-------------------+------------------------+ | 2. | six.example.com. | 3001:1::50 | +----+-------------------+------------------------+ | 3. | example.com. | 172.16.2.5 | +----+-------------------+------------------------+ As discussed earlier, FQDN-to-domain matching is based on the longest match. The FQDN, "myhost.four.example.com.", will match the first domain ("four.example.com") while "admin.example.com." will match the third domain ("example.com"). The FQDN, "other.example.net.", will fail to match any domain and is rejected. The following example configuration specifies the Forward DDNS Domains. :: "DhcpDdns": { "comment": "example configuration: forward part", "forward-ddns": { "ddns-domains": [ { "name": "four.example.com.", "key-name": "", "dns-servers": [ { "ip-address": "172.16.1.5" }, { "ip-address": "172.16.2.5" } ] }, { "name": "six.example.com.", "key-name": "", "dns-servers": [ { "ip-address": "2001:db8::1" } ] }, { "name": "example.com.", "key-name": "", "dns-servers": [ { "ip-address": "172.16.2.5" } ], "user-context": { "backup": false } }, ] } } Similarly, we need to construct the three Reverse DDNS Domains: .. table:: Reverse DDNS Domains Needed +----+-----------------------------------+------------------------+ | # | DDNS Domain Name | DNS Servers | +====+===================================+========================+ | 1. | 2.0.192.in-addr.arpa. | 172.16.1.5, 172.16.2.5 | +----+-----------------------------------+------------------------+ | 2. | 1.0.0.0.8.d.b.0.1.0.0.2.ip6.arpa. | 3001:1::50 | +----+-----------------------------------+------------------------+ | 3. | 0.182.in-addr.arpa. | 172.16.2.5 | +----+-----------------------------------+------------------------+ An address of "192.0.2.150" will match the first domain, "2001:db8:1::10" will match the second domain, and "192.0.50.77" the third domain. These Reverse DDNS Domains are specified as follows: :: "DhcpDdns": { "comment": "example configuration: reverse part", "reverse-ddns": { "ddns-domains": [ { "name": "2.0.192.in-addr.arpa.", "key-name": "", "dns-servers": [ { "ip-address": "172.16.1.5" }, { "ip-address": "172.16.2.5" } ] } { "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.", "key-name": "", "dns-servers": [ { "ip-address": "2001:db8::1" } ] } { "name": "0.192.in-addr.arpa.", "key-name": "", "dns-servers": [ { "ip-address": "172.16.2.5" } ] } ] } } DHCP-DDNS Server Statistics =========================== Kea version 2.0.0 introduced statistics support for the DHCP-DDNS. Statistics are divided in three groups: Name Change Request, DNS update and per TSIG key DNS updates. If the statistics of the first two groups are cumulative, i.e. not affected by configuration change or reload, per key statistics are reset to 0 when the underlying object is (re)created. Currently the statistics management is limited: - only integer samples (i.e. a counter and a timestamp) are used - the maximum sample count is 1 - there is no API to remove one or all statistics - there is no API to set the maximum sample count or age .. note:: Hook libraries like the GSS-TSIG add new statistics. A reference about Kea statistics can be found at :ref:`stats`. NCR Statistics -------------- The Name Change Request statistics are: - ``ncr-received`` - received valid NCRs - ``ncr-invalid`` - received invalid NCRs - ``ncr-error`` - errors in NCR receptions other than I/O cancel on shutdown DNS Update Statistics --------------------- The global DNS update statistics are: - ``update-sent`` - sent DNS updates - ``update-signed`` - sent DNS updates protected by TSIG - ``update-unsigned`` - sent DNS updates not protected by TSIG - ``update-success`` - DNS updates which completed with a success - ``update-timeout`` - DNS updates which completed on timeout - ``update-error`` - DNS updates which completed with an error other than timeout Per TSIG key DNS Update Statistics ---------------------------------- The per TSIG key DNS update statistics are: - ``update-sent`` - sent DNS updates - ``update-success`` - DNS updates which completed with a success - ``update-timeout`` - DNS updates which completed on timeout - ``update-error`` - DNS updates which completed with an error other than timeout The name of a per key statistics is ``key[].``, for instance the name of the ``update-sent`` statistics for the ``key.example.com.`` TSIG key is ``key[key.example.com.].update-sent``. DHCP-DDNS Server Limitations ============================ The following are the current limitations of the DHCP-DDNS Server. - Requests received from the DHCP servers are placed in a queue until they are processed. Currently, all queued requests are lost if the server shuts down. Supported Standards =================== The following RFCs are supported by the DHCP-DDNS server: - *Secret Key Transaction Authentication for DNS (TSIG)*, `RFC 2845 `__: All DNS Update packets sent and received by DHCP-DDNS server can be protected by TSIG signatures. - *Dynamic Updates in the Domain Name System (DNS UPDATE)*, `RFC 2136 `__: The whole DNS Update mechanism is supported. - *Resolution of Fully Qualified Domain Name (FQDN) Conflicts among Dynamic Host Configuration Protocol (DHCP) Clients*, `RFC 4703 `__: The DHCP-DDNS takes care of the conflict resolution. This capability is used by DHCPv4 and DHCPv6 servers. - *A DNS Resource Record (RR) for Encoding Dynamic Host Configuration Protocol (DHCP) Information (DHCID RR)*, `RFC 4701 `__: The DHCP-DDNS server uses the DHCID records. kea-2.0.2/doc/sphinx/arm/stork.rst0000644000175000017500000000540714206773363013746 00000000000000.. _stork: ************************* Monitoring Kea With Stork ************************* Most administrators want to be able to monitor any Kea services that are running. Kea offers so many pieces of information - configuration files, API, statistics, logs, open database content, and more - that it may sometimes be overwhelming to keep up. ISC's Stork project is intended to address this problem for both Kea and BIND 9. Stork is useful in a variety of scenarios: - Stork can be used as a dashboard. It provides insight into what exactly is happening on the servers. In particular, it allows users to: see up-to-date details regarding pool utilization in subnets and shared networks; monitor the state of the HA pair (and provide extra insight in case of failover and recovery events); list, filter, and search for specific host reservations; and more. Only a single Stork server needs to be deployed, and one Stork agent on each machine to be monitored. - The Stork agent can integrate Kea with Prometheus and Grafana. Once the Stork agent is active on the server, it serves as a Prometheus exporter. Users who have deployed Prometheus in their networks can visualize statistics as time series using Grafana. - Stork can act as both a dashboard and an integrator for Prometheus/Grafana. Once Stork is linked to where Grafana is deployed on the network, users can inspect the current status and visit a customized link to Grafana to see how a given property behaves over time. Stork is available as source code, but also as native deb and RPM packages, which makes it easy to install on most popular systems. For more details, please see the `Stork ARM `_ or the `Stork project page `_. The former has a nice collection of screenshots that is frequently updated, to give users an idea of what is currently available. Stork is in the midst of full development with monthly releases, so please check back frequently. .. _grafana: .. _prometheus: Kea Statistics in Grafana ========================= The ISC Stork project provides an agent that can be deployed alongside Kea and BIND 9. It exposes Kea statistics in a format that is accepted by Prometheus. One of the major benefits of Prometheus is that it turns repeated one-time observations into time series, which lets users monitor how certain behaviors change over time. It is easy to use other tools to visualize data available in Prometheus; the most common approach is to use Grafana to provide visual dashboards. The Stork project provides dashboard definitions for Kea and BIND 9 that can be imported into Grafana very easily. Learn more about Prometheus and Grafana on their websites: `Prometheus ` and `Grafana `. kea-2.0.2/doc/sphinx/arm/shell.rst0000644000175000017500000001367614206773363013722 00000000000000.. _kea-shell: ************* The Kea Shell ************* .. _shell-overview: Overview of the Kea Shell ========================= Kea 1.2.0 introduced the Control Agent (CA, see :ref:`kea-ctrl-agent`), which provides a RESTful control interface over HTTP. That API is typically expected to be used by various IPAMs and similar management systems. Nevertheless, there may be cases when an administrator wants to send a command to the CA directly, and the Kea shell provides a way to do this. It is a simple command-line, scripting-friendly, text client that is able to connect to the CA, send it commands with parameters, retrieve the responses, and display them. As the primary purpose of the Kea shell is as a tool in a scripting environment, it is not interactive. However, following simple guidelines it can be run manually. Kea 1.9.0 introduced basic HTTP authentication support. Shell Usage =========== ``kea-shell`` is run as follows: .. code-block:: console $ kea-shell [--host hostname] [--port number] [--path path] [--auth-user] [--auth-password] [--timeout seconds] [--service service-name] [command] where: - ``--host hostname`` specifies the hostname of the CA. If not specified, "localhost" is used. - ``--port number`` specifies the TCP port on which the CA listens. If not specified, 8000 is used. - ``--path path`` specifies the path in the URL to connect to. If not specified, an empty path is used. As the CA listens at the empty path, this parameter is useful only with a reverse proxy. - ``--auth-user`` specifies the user id for basic HTTP authentication. If not specified or specified as the empty string authentication is not used. - ``--auth-password`` specifies the password for basic HTTP authentication. If not specified but the user id is specified an empty password is used. - ``--timeout seconds`` specifies the timeout (in seconds) for the connection. If not given, 10 seconds is used. - ``--service service-name`` specifies the target of a command. If not given, the CA will be used as the target. May be used more than once to specify multiple targets. - ``command`` specifies the command to be sent. If not specified, the ``list-commands`` command is used. Other switches are: - ``-h`` - prints a help message. - ``-v`` - prints the software version. See :ref:`shell-tls` for TLS/HTTPS support new command line arguments. Once started, the shell reads parameters for the command from standard input, which are expected to be in JSON format. When all have been read, the shell establishes a connection with the CA using HTTP, sends the command, and awaits a response. Once that is received, it is displayed on standard output. For a list of available commands, see :ref:`ctrl-channel`; additional commands may be provided by hooks libraries. For a list of all supported commands from the CA, use the ``list-commands`` command. The following shows a simple example of usage: .. code-block:: console $ kea-shell --host 192.0.2.1 --port 8001 --service dhcp4 list-commands ^D After the command line is entered, the program waits for command parameters to be entered. Since ``list-commands`` does not take any arguments, CTRL-D (represented in the above example by "^D") is pressed to indicate end-of-file and terminate the parameter input. The shell then contacts the CA and prints out the list of available commands returned for the service named ``dhcp4``. It is envisaged that the Kea shell will be most frequently used in scripts; the next example shows a simple scripted execution. It sends the command "config-write" to the CA (the ``--service`` parameter has not been used), along with the parameters specified in param.json. The result will be stored in result.json. .. code-block:: console $ cat param.json "filename": "my-config-file.json" $ cat param.json | kea-shell --host 192.0.2.1 config-write > result.json When a reverse proxy is used to de-multiplex requests to different servers, the default empty path in the URL is not enough, so the ``--path`` parameter should be used. For instance, if requests to the "/kea" path are forwarded to the CA this can be used: .. code-block:: console $ kea-shell --host 192.0.2.1 --port 8001 --path kea ... The Kea shell requires Python to be installed on the system. It has been tested with Python 2.7 and various versions of Python 3, up to 3.5. Since not every Kea deployment uses this feature and there are deployments that do not have Python, the Kea shell is not enabled by default. To use it, specify ``--enable-shell`` when running "configure" during the installation of Kea. When building on Debian systems, also ``--with-site-packages=...`` may be useful. The Kea shell is intended to serve more as a demonstration of the RESTful interface's capabilities (and, perhaps, an illustration for people interested in integrating their management environments with Kea) than as a serious management client. It is not likely to be significantly expanded in the future; it is, and will remain, a simple tool. .. note:: When using this tool with basic HTTP authentication please keep in mind that command line arguments are not hidden to local users. .. _shell-tls: TLS support =========== Starting with 1.9.6, kea-shell supports HTTPS connections. The TLS/HTTPS support requires python 3. Additional command line arguments are: - ``--ca`` Specifies the file or directory name of the Certification Authority. If not specified HTTPS is not used. - ``--cert`` Specifies the file name of the user end-entity public key certificate. If specified, the file name of the user key must be specified too. - ``--key`` Specifies the file name of the user key file. If specified the file name of the user certificate must be specified too. Note that encrypted key files are not supported. For example, a basic HTTPS request to get a list of commands could look like this: .. code-block:: console $ kea-shell --host 127.0.0.1 --port 8000 --ca ./kea-ca.crt list-commands kea-2.0.2/doc/sphinx/arm/hooks-class-cmds.rst0000644000175000017500000001453314206773363015756 00000000000000.. _class-cmds-library: class_cmds: Class Commands ========================== This section describes the Class Commands hooks library, which exposes several control commands for manipulating client classes (part of the Kea DHCP servers' configurations) without the need to restart those servers. Using these commands it is possible to add, update, delete, and list client classes configured for a given server. .. note:: This library may only be loaded by the ``kea-dhcp4`` or ``kea-dhcp6`` process. The Class Commands hooks library is currently available only to ISC customers with a paid support contract. .. _command-class-add: The class-add Command ~~~~~~~~~~~~~~~~~~~~~ The ``class-add`` command adds a new client class to the DHCP server configuration. This class is appended at the end of the list of classes used by the server and may depend on any of the already-configured client classes. The following example demonstrates how to add a new client class to the DHCPv4 server configuration: :: { "command": "class-add", "arguments": { "client-classes": [ { "name": "ipxe_efi_x64", "test": "option[93].hex == 0x0009", "next-server": "192.0.2.254", "server-hostname": "hal9000", "boot-file-name": "/dev/null" } ] } } Note that the ``client-classes`` parameter is a JSON list, but it allows only a single client class to be present. Here is the response to the ``class-add`` command in our example: :: { "result": 0, "text": "Class 'ipxe_efi_x64' added." } .. _command-class-update: The class-update Command ~~~~~~~~~~~~~~~~~~~~~~~~ The ``class-update`` command updates an existing client class in the DHCP server configuration. If the client class with the given name does not exist, the server returns the result code of 3, which means that the server configuration is not modified and the client class does not exist. The ``class-add`` command must be used instead to create the new client class. The ``class-update`` command has the same argument structure as the ``class-add`` command: :: { "command": "class-update", "arguments": { "client-classes": [ { "name": "ipxe_efi_x64", "test": "option[93].hex == 0x0017", "next-server": "0.0.0.0", "server-hostname": "xfce", "boot-file-name": "/dev/null" } ] } } Here is the response for our example: :: { "result": 0, "text": "Class 'ipxe_efi_x64' updated." } Any parameter of the client class can be modified with this command, except ``name``. There is currently no way to rename the class, because the class name is used as a key for searching the class to be updated. To achieve a similar effect to renaming the class, an existing class can be removed with the ``class-del`` command and then added again with a different name using ``class-add``. Note, however, that the class with the new name will be added at the end of the list of configured classes. .. _command-class-del: The class-del Command ~~~~~~~~~~~~~~~~~~~~~ The ``class-del`` command is used to remove a particular class from the server configuration. The class to be removed is identified by name. The class is not removed if there are other classes depending on it; to remove such a class, the dependent classes must be removed first. The following is a sample command removing the ``ipxe_efi_x64`` class: :: { "command": "class-del", "arguments": { { "name": "ipxe_efi_x64" } } } Here is the response to the ``class-del`` command in our example, when the specified client class has been found: :: { "result": 0, "text": "Class 'ipxe_efi_x64' deleted." } If the class does not exist, the result of 3 is returned. .. _command-class-list: The class-list Command ~~~~~~~~~~~~~~~~~~~~~~ ``class-list`` is used to retrieve a list of all client classes. This command includes no arguments: :: { "command": "class-list" } Here is the response of the server in our example, including the list of client classes: :: { "result": 0, "text": "2 classes found", "arguments": { "client-classes": [ { "name": "ipxe_efi_x64" }, { "name": "pxeclient" } ] } } Note that the returned list does not contain full class definitions, but merely class names. To retrieve full class information, the ``class-get`` command should be used. .. _command-class-get: The class-get Command ~~~~~~~~~~~~~~~~~~~~~ ``class-get`` is used to retrieve detailed information about a specified class. The command structure is very simple: :: { "command": "class-get", "arguments": { "name": "pxeclient" } } If the class with the specified name does not exist, the status code of 3 is returned. If the specified client class exists, the class details are returned in the following format: :: { "result": 0, "text": "Class 'pxeclient' definition returned", "arguments": { "client-classes": [ { "name": "pxeclient", "only-if-required": true, "test": "option[vendor-class-identifier].text == 'PXEClient'", "option-def": [ { "name": "configfile", "code": 209, "type": "string" } ], "option-data": [ ], "next-server": "0.0.0.0", "server-hostname": "xfce", "boot-file-name": "/dev/null" } ] } } Note that the example above is DHCPv4-specific; the last three parameters are only returned by the DHCPv4 server and are never returned by the DHCPv6 server. Also, some of the parameters provided in this example may not be returned if they are not specified for the class. Specifically, ``only-if-required``, ``test``, and ``option-def`` are not returned if they are not specified for the class. kea-2.0.2/doc/sphinx/arm/hooks-host-cache.rst0000644000175000017500000002162714206773363015745 00000000000000.. _hooks-host-cache: host_cache: Caching Host Reservations ===================================== Some database backends, such as RADIUS, are considered slow and may take a long time to respond. Since Kea in general is synchronous, backend performance directly affects DHCP performance. To minimize the impact and improve performance, the Host Cache library provides a way to cache information from the database locally. This includes negative caching, i.e. the ability to remember that there is no client information in the database. .. note:: This library may only be loaded by the ``kea-dhcp4`` or ``kea-dhcp6`` process. In principle, this hooks library can be used with any backend that may introduce performance degradation (MySQL, PostgreSQL, Cassandra, or RADIUS). Host Cache must be loaded for the RADIUS accounting mechanism to work. The Host Cache hooks library is currently very simple. It takes only one optional parameter ("maximum"), which defines the maximum number of hosts to be cached. If not specified, the default value of 0 is used, which means there is no limit. This hooks library can be loaded the same way as any other hooks library; for example, this configuration could be used: :: "Dhcp4": { # Your regular DHCPv4 configuration parameters here. "hooks-libraries": [ { "library": "/usr/local/lib/kea/hooks/libdhc_host_cache.so", "parameters": { # Tells Kea to never cache more than 1000 hosts. "maximum": 1000 } } ] Once loaded, the Host Cache hooks library provides a number of new commands which can be used either over the control channel (see :ref:`ctrl-channel-client`) or the RESTful API (see :ref:`agent-overview`). An example RESTful API client is described in :ref:`shell-overview`. The following sections describe the commands available. .. _command-cache-flush: The cache-flush Command ~~~~~~~~~~~~~~~~~~~~~~~ This command allows removal of a specified number of cached host entries. It takes one parameter, which defines the number of hosts to be removed. An example usage looks as follows: :: { "command": "cache-flush", "arguments": 1000 } This command will remove 1000 hosts. To delete all cached hosts, please use cache-clear instead. The hosts are stored in FIFO (first-in, first-out) order, so the oldest entries are always removed. .. _command-cache-clear: The cache-clear Command ~~~~~~~~~~~~~~~~~~~~~~~ This command allows removal of all cached host entries. An example usage looks as follows: :: { "command": "cache-clear" } This command will remove all hosts. To delete only a certain number of cached hosts, please use cache-flush instead. .. _command-cache-size: The cache-size Command ~~~~~~~~~~~~~~~~~~~~~~ This command returns the number of host entries. An example usage looks as follows: :: { "command": "cache-size" } .. _command-cache-write: The cache-write Command ~~~~~~~~~~~~~~~~~~~~~~~ In general, the cache content is considered a runtime state and the server can be shut down or restarted as usual; the cache will then be repopulated after restart. However, there are some cases when it is useful to store the contents of the cache. One such case is RADIUS, where the cached hosts also retain additional cached RADIUS attributes; there is no easy way to obtain this information again, because renewing clients send their packet to the DHCP server directly. Another use case is when an administrator wants to restart the server and, for performance reasons, wants it to start with a hot (populated) cache. This command allows writing the contents of the in-memory cache to a file on disk. It takes one parameter, which defines the filename. An example usage looks as follows: :: { "command": "cache-write", "arguments": "/tmp/kea-host-cache.json" } This causes the contents to be stored in the /tmp/kea-host-cache.json file. That file can then be loaded with the cache-load command or processed by any other tool that is able to understand JSON format. .. _command-cache-load: The cache-load Command ~~~~~~~~~~~~~~~~~~~~~~ See the previous section for a discussion of use cases where it may be useful to write and load contents of the host cache to disk. This command allows the contents of a file on disk to be loaded into an in-memory cache. It takes one parameter, which defines the filename. An example usage looks as follows: :: { "command": "cache-load", "arguments": "/tmp/kea-host-cache.json" } This command will store the contents to the /tmp/kea-host-cache.json file. That file can then be loaded with the cache-load command or processed by any other tool that is able to understand JSON format. .. _command-cache-get: The cache-get Command ~~~~~~~~~~~~~~~~~~~~~ This command is similar to cache-write, but instead of writing the cache contents to disk, it returns the contents to whoever sent the command. This command allows the contents of a file on disk to be loaded into an in-memory cache. It takes one parameter, which defines the filename. An example usage looks as follows: :: { "command": "cache-get" } This command will return all the cached hosts. Note that the response may be large. .. _command-cache-get-by-id: The cache-get-by-id Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command is similar to cache-get, but instead of returning the whole content it returns only the entries matching the given identifier. It takes one parameter, which defines the identifier of wanted cached host reservations. An example usage looks as follows: :: { "command": "cache-get-by-id", "arguments": { "hw-address": "01:02:03:04:05:06" } } This command will return all the cached hosts with the given hardware address. .. _command-cache-insert: The cache-insert Command ~~~~~~~~~~~~~~~~~~~~~~~~ This command may be used to manually insert a host into the cache; there are very few use cases when this command might be useful. This command expects its arguments to follow the usual syntax for specifying host reservations (see :ref:`host-reservation-v4` or :ref:`host-reservation-v6`), with one difference: the subnet-id value must be specified explicitly. An example command that will insert an IPv4 host into the host cache looks as follows: :: { "command": "cache-insert", "arguments": { "hw-address": "01:02:03:04:05:06", "subnet-id4": 4, "subnet-id6": 0, "ip-address": "192.0.2.100", "hostname": "somehost.example.org", "client-classes4": [ ], "client-classes6": [ ], "option-data4": [ ], "option-data6": [ ], "next-server": "192.0.0.2", "server-hostname": "server-hostname.example.org", "boot-file-name": "bootfile.efi", "host-id": 0 } } An example command that will insert an IPv6 host into the host cache looks as follows: :: { "command": "cache-insert", "arguments": { "hw-address": "01:02:03:04:05:06", "subnet-id4": 0, "subnet-id6": 6, "ip-addresses": [ "2001:db8::cafe:babe" ], "prefixes": [ "2001:db8:dead:beef::/64" ], "hostname": "", "client-classes4": [ ], "client-classes6": [ ], "option-data4": [ ], "option-data6": [ ], "next-server": "0.0.0.0", "server-hostname": "", "boot-file-name": "", "host-id": 0 } } .. _command-cache-remove: The cache-remove Command ~~~~~~~~~~~~~~~~~~~~~~~~ Sometimes it is useful to remove a single entry from the host cache. A good use case is a situation where the device is up, Kea has already provided configuration, and the host entry is in cache. As a result of administrative action (e.g. the customer hasn't paid their bills or has perhaps been upgraded to better service), the information in the backend (e.g. MySQL or RADIUS) is being updated. However, since the cache is in use, Kea does not notice the change as the cached values are used. The cache-remove command can solve this problem by removing a cached entry after administrative changes. The cache-remove command works similarly to the reservation-get command. It allows querying by two parameters: either subnet-id4 or subnet-id6; or ip-address (may be an IPv4 or IPv6 address), hw-address (specifies hardware/MAC address), duid, circuit-id, client-id, or flex-id. An example command to remove an IPv4 host with reserved address 192.0.2.1 from a subnet with a subnet-id 123 looks as follows: :: { "command": "cache-remove", "arguments": { "ip-address": "192.0.2.1", "subnet-id": 123 } } Another example that removes an IPv6 host identifier by DUID and specific subnet-id is: :: { "command": "cache-remove", "arguments": { "duid": "00:01:ab:cd:f0:a1:c2:d3:e4", "subnet-id": 123 } } kea-2.0.2/doc/sphinx/arm/config-templates.rst0000644000175000017500000000222414206773363016037 00000000000000.. _config-templates: Configuration Templates ======================= The following sections include configuration templates that are proposed configuration for certain deployment types. The example configuration files are also available in the Kea sources in the `doc/examples` directory. .. include:: template-power-user-home.md At the very least, the lines marked in yellow must be adjusted to match the actual deployment. Server1's Control Agent configuration file: .. literalinclude:: template-power-user-home-ca-1.conf :language: javascript :emphasize-lines: 9, 12 :linenos: Server1's DHCPv4 configuration file: .. literalinclude:: template-power-user-home-dhcp4-1.conf :language: javascript :emphasize-lines: 25,76,81,121,133,147,151,154-158,166-180,190-199 :linenos: Server2's Control Agent configuration file: .. literalinclude:: template-power-user-home-ca-2.conf :language: javascript :emphasize-lines: 9, 12 :linenos: Server2's DHCPv4 configuration file: .. literalinclude:: template-power-user-home-dhcp4-2.conf :language: javascript :emphasize-lines: 25,76,81,121,133,147,151,154-158,166-180,190-199 :linenos: kea-2.0.2/doc/sphinx/arm/hooks-run-script.rst0000644000175000017500000002655214206773363016037 00000000000000.. _hooks-run-script: Run Script Support ================== This hooks library adds support for calling an external script for specific packet processing hook points. The library is available since Kea 1.9.5 and can be loaded in a similar way as other hook libraries by the ``kea-dhcp4`` and ``kea-dhcp6`` processes. .. code-block:: json { "hooks-libraries": [ { "library": "/usr/local/lib/libdhcp_run_script.so", "parameters": { "name": "/full_path_to/script_name.sh", "sync": false } } ] } The parameters contain the 'name' which indicates the full path to the external script to be called on each hookpoint, and also the 'sync' option to be able to wait synchronously for the script to finish execution. If the 'sync' parameter is false, then the script will be launched and Kea will not wait for the execution to finish, causing all the OUT parameters of the script (including next step) to be ignored. .. note:: The script inherits all privileges from the server which calls it. .. note:: Currently the functionality underneath 'sync' parameter is not implemented and enabling synchronous calls to external scripts is not supported. .. _hooks-run-script-hook-points: This library has several hook points functions implemented which will be called at the specific packet processing stage. The dhcpv4 hook points: :: lease4_renew lease4_expire lease4_recover leases4_committed lease4_release lease4_decline The dhcpv6 hook points: :: lease6_renew lease6_rebind lease6_expire lease6_recover leases6_committed lease6_release lease6_decline Each hook point extracts the Kea internal data and exports it as string environment variables. These parameters are shared with the target script using the child process environment. The only parameter passed to the call of the target script is the name of the hook point. An example of a script implementing all hook points is presented below. :: #!/bin/bash unknown_handle() { echo "Unhandled function call ${*}" exit 123 } lease4_renew () { ... } lease4_expire () { ... } lease4_recover () { ... } leases4_committed () { ... } lease4_release () { ... } lease4_decline () { ... } lease6_renew () { ... } lease6_rebind () { ... } lease6_expire () { ... } lease6_recover () { ... } leases6_committed () { ... } lease6_release () { ... } lease6_decline () { ... } case "$1" in "lease4_renew") lease4_renew ;; "lease4_expire") lease4_expire ;; "lease4_recover") lease4_recover ;; "leases4_committed") leases4_committed ;; "lease4_release") lease4_release ;; "lease4_decline") lease4_decline ;; "lease6_renew") lease6_renew ;; "lease6_rebind") lease6_rebind ;; "lease6_expire") lease6_expire ;; "lease6_recover") lease6_recover ;; "leases6_committed") leases6_committed ;; "lease6_release") lease6_release ;; "lease6_decline") lease6_decline ;; *) unknown_handle "${@}" ;; esac .. _hooks-run-script-exported-environment-variables: Available parameters for each hook point are presented below. DHCPv4: lease4_renew :: QUERY4_TYPE QUERY4_TXID QUERY4_LOCAL_ADDR QUERY4_LOCAL_PORT QUERY4_REMOTE_ADDR QUERY4_REMOTE_PORT QUERY4_IFACE_INDEX QUERY4_IFACE_NAME QUERY4_HOPS QUERY4_SECS QUERY4_FLAGS QUERY4_CIADDR QUERY4_SIADDR QUERY4_YIADDR QUERY4_GIADDR QUERY4_RELAYED QUERY4_HWADDR QUERY4_HWADDR_TYPE QUERY4_LOCAL_HWADDR QUERY4_LOCAL_HWADDR_TYPE QUERY4_REMOTE_HWADDR QUERY4_REMOTE_HWADDR_TYPE QUERY4_OPTION_82 QUERY4_OPTION_82_SUB_OPTION_1 QUERY4_OPTION_82_SUB_OPTION_2 SUBNET4_ID SUBNET4_NAME SUBNET4_PREFIX SUBNET4_PREFIX_LEN PKT4_CLIENT_ID PKT4_HWADDR PKT4_HWADDR_TYPE LEASE4_ADDRESS LEASE4_CLTT LEASE4_HOSTNAME LEASE4_HWADDR LEASE4_HWADDR_TYPE LEASE4_STATE LEASE4_SUBNET_ID LEASE4_VALID_LIFETIME LEASE4_CLIENT_ID lease4_expire :: LEASE4_ADDRESS LEASE4_CLTT LEASE4_HOSTNAME LEASE4_HWADDR LEASE4_HWADDR_TYPE LEASE4_STATE LEASE4_SUBNET_ID LEASE4_VALID_LIFETIME LEASE4_CLIENT_ID REMOVE_LEASE lease4_recover :: LEASE4_ADDRESS LEASE4_CLTT LEASE4_HOSTNAME LEASE4_HWADDR LEASE4_HWADDR_TYPE LEASE4_STATE LEASE4_SUBNET_ID LEASE4_VALID_LIFETIME LEASE4_CLIENT_ID leases4_committed :: QUERY4_TYPE QUERY4_TXID QUERY4_LOCAL_ADDR QUERY4_LOCAL_PORT QUERY4_REMOTE_ADDR QUERY4_REMOTE_PORT QUERY4_IFACE_INDEX QUERY4_IFACE_NAME QUERY4_HOPS QUERY4_SECS QUERY4_FLAGS QUERY4_CIADDR QUERY4_SIADDR QUERY4_YIADDR QUERY4_GIADDR QUERY4_RELAYED QUERY4_HWADDR QUERY4_HWADDR_TYPE QUERY4_LOCAL_HWADDR QUERY4_LOCAL_HWADDR_TYPE QUERY4_REMOTE_HWADDR QUERY4_REMOTE_HWADDR_TYPE QUERY4_OPTION_82 QUERY4_OPTION_82_SUB_OPTION_1 QUERY4_OPTION_82_SUB_OPTION_2 LEASES4_SIZE DELETED_LEASES4_SIZE If LEASES4_SIZE or DELETED_LEASES4_SIZE are non zero, then each lease will have its own unique identifier as shown below. First index starts at 0. :: LEASES4_AT0_ADDRESS LEASES4_AT0_CLTT LEASES4_AT0_HOSTNAME LEASES4_AT0_HWADDR LEASES4_AT0_HWADDR_TYPE LEASES4_AT0_STATE LEASES4_AT0_SUBNET_ID LEASES4_AT0_VALID_LIFETIME LEASES4_AT0_CLIENT_ID DELETED_LEASES4_AT0_ADDRESS DELETED_LEASES4_AT0_CLTT DELETED_LEASES4_AT0_HOSTNAME DELETED_LEASES4_AT0_HWADDR DELETED_LEASES4_AT0_HWADDR_TYPE DELETED_LEASES4_AT0_STATE DELETED_LEASES4_AT0_SUBNET_ID DELETED_LEASES4_AT0_VALID_LIFETIME DELETED_LEASES4_AT0_CLIENT_ID lease4_release :: QUERY4_TYPE QUERY4_TXID QUERY4_LOCAL_ADDR QUERY4_LOCAL_PORT QUERY4_REMOTE_ADDR QUERY4_REMOTE_PORT QUERY4_IFACE_INDEX QUERY4_IFACE_NAME QUERY4_HOPS QUERY4_SECS QUERY4_FLAGS QUERY4_CIADDR QUERY4_SIADDR QUERY4_YIADDR QUERY4_GIADDR QUERY4_RELAYED QUERY4_HWADDR QUERY4_HWADDR_TYPE QUERY4_LOCAL_HWADDR QUERY4_LOCAL_HWADDR_TYPE QUERY4_REMOTE_HWADDR QUERY4_REMOTE_HWADDR_TYPE QUERY4_OPTION_82 QUERY4_OPTION_82_SUB_OPTION_1 QUERY4_OPTION_82_SUB_OPTION_2 LEASE4_ADDRESS LEASE4_CLTT LEASE4_HOSTNAME LEASE4_HWADDR LEASE4_HWADDR_TYPE LEASE4_STATE LEASE4_SUBNET_ID LEASE4_VALID_LIFETIME LEASE4_CLIENT_ID lease4_decline :: QUERY4_TYPE QUERY4_TXID QUERY4_LOCAL_ADDR QUERY4_LOCAL_PORT QUERY4_REMOTE_ADDR QUERY4_REMOTE_PORT QUERY4_IFACE_INDEX QUERY4_IFACE_NAME QUERY4_HOPS QUERY4_SECS QUERY4_FLAGS QUERY4_CIADDR QUERY4_SIADDR QUERY4_YIADDR QUERY4_GIADDR QUERY4_RELAYED QUERY4_HWADDR QUERY4_HWADDR_TYPE QUERY4_LOCAL_HWADDR QUERY4_LOCAL_HWADDR_TYPE QUERY4_REMOTE_HWADDR QUERY4_REMOTE_HWADDR_TYPE QUERY4_OPTION_82 QUERY4_OPTION_82_SUB_OPTION_1 QUERY4_OPTION_82_SUB_OPTION_2 LEASE4_ADDRESS LEASE4_CLTT LEASE4_HOSTNAME LEASE4_HWADDR LEASE4_HWADDR_TYPE LEASE4_STATE LEASE4_SUBNET_ID LEASE4_VALID_LIFETIME LEASE4_CLIENT_ID DHCPv6: lease6_renew :: QUERY6_TYPE QUERY6_TXID QUERY6_LOCAL_ADDR QUERY6_LOCAL_PORT QUERY6_REMOTE_ADDR QUERY6_REMOTE_PORT QUERY6_IFACE_INDEX QUERY6_IFACE_NAME QUERY6_REMOTE_HWADDR QUERY6_REMOTE_HWADDR_TYPE QUERY6_PROTO QUERY6_CLIENT_ID LEASE6_ADDRESS LEASE6_CLTT LEASE6_HOSTNAME LEASE6_HWADDR LEASE6_HWADDR_TYPE LEASE6_STATE LEASE6_SUBNET_ID LEASE6_VALID_LIFETIME LEASE6_DUID LEASE6_IAID LEASE6_PREFERRED_LIFETIME LEASE6_PREFIX_LEN LEASE6_TYPE PKT6_IA_IAID PKT6_IA_IA_TYPE PKT6_IA_IA_T1 PKT6_IA_IA_T2 lease6_rebind :: QUERY6_TYPE QUERY6_TXID QUERY6_LOCAL_ADDR QUERY6_LOCAL_PORT QUERY6_REMOTE_ADDR QUERY6_REMOTE_PORT QUERY6_IFACE_INDEX QUERY6_IFACE_NAME QUERY6_REMOTE_HWADDR QUERY6_REMOTE_HWADDR_TYPE QUERY6_PROTO QUERY6_CLIENT_ID LEASE6_ADDRESS LEASE6_CLTT LEASE6_HOSTNAME LEASE6_HWADDR LEASE6_HWADDR_TYPE LEASE6_STATE LEASE6_SUBNET_ID LEASE6_VALID_LIFETIME LEASE6_DUID LEASE6_IAID LEASE6_PREFERRED_LIFETIME LEASE6_PREFIX_LEN LEASE6_TYPE PKT6_IA_IAID PKT6_IA_IA_TYPE PKT6_IA_IA_T1 PKT6_IA_IA_T2 lease6_expire :: LEASE6_ADDRESS LEASE6_CLTT LEASE6_HOSTNAME LEASE6_HWADDR LEASE6_HWADDR_TYPE LEASE6_STATE LEASE6_SUBNET_ID LEASE6_VALID_LIFETIME LEASE6_DUID LEASE6_IAID LEASE6_PREFERRED_LIFETIME LEASE6_PREFIX_LEN LEASE6_TYPE REMOVE_LEASE lease6_recover :: LEASE6_ADDRESS LEASE6_CLTT LEASE6_HOSTNAME LEASE6_HWADDR LEASE6_HWADDR_TYPE LEASE6_STATE LEASE6_SUBNET_ID LEASE6_VALID_LIFETIME LEASE6_DUID LEASE6_IAID LEASE6_PREFERRED_LIFETIME LEASE6_PREFIX_LEN LEASE6_TYPE leases6_committed :: QUERY6_TYPE QUERY6_TXID QUERY6_LOCAL_ADDR QUERY6_LOCAL_PORT QUERY6_REMOTE_ADDR QUERY6_REMOTE_PORT QUERY6_IFACE_INDEX QUERY6_IFACE_NAME QUERY6_REMOTE_HWADDR QUERY6_REMOTE_HWADDR_TYPE QUERY6_PROTO QUERY6_CLIENT_ID LEASES6_SIZE DELETED_LEASES6_SIZE If LEASES6_SIZE or DELETED_LEASES6_SIZE are non zero, then each lease will have its own unique identifier as shown below. First index starts at 0. :: LEASES6_AT0_ADDRESS LEASES6_AT0_CLTT LEASES6_AT0_HOSTNAME LEASES6_AT0_HWADDR LEASES6_AT0_HWADDR_TYPE LEASES6_AT0_STATE LEASES6_AT0_SUBNET_ID LEASES6_AT0_VALID_LIFETIME LEASES6_AT0_DUID LEASES6_AT0_IAID LEASES6_AT0_PREFERRED_LIFETIME LEASES6_AT0_PREFIX_LEN LEASES6_AT0_TYPE DELETED_LEASES6_AT0_ADDRESS DELETED_LEASES6_AT0_CLTT DELETED_LEASES6_AT0_HOSTNAME DELETED_LEASES6_AT0_HWADDR DELETED_LEASES6_AT0_HWADDR_TYPE DELETED_LEASES6_AT0_STATE DELETED_LEASES6_AT0_SUBNET_ID DELETED_LEASES6_AT0_VALID_LIFETIME DELETED_LEASES6_AT0_DUID DELETED_LEASES6_AT0_IAID DELETED_LEASES6_AT0_PREFERRED_LIFETIME DELETED_LEASES6_AT0_PREFIX_LEN DELETED_LEASES6_AT0_TYPE lease6_release :: QUERY6_TYPE QUERY6_TXID QUERY6_LOCAL_ADDR QUERY6_LOCAL_PORT QUERY6_REMOTE_ADDR QUERY6_REMOTE_PORT QUERY6_IFACE_INDEX QUERY6_IFACE_NAME QUERY6_REMOTE_HWADDR QUERY6_REMOTE_HWADDR_TYPE QUERY6_PROTO QUERY6_CLIENT_ID LEASE6_ADDRESS LEASE6_CLTT LEASE6_HOSTNAME LEASE6_HWADDR LEASE6_HWADDR_TYPE LEASE6_STATE LEASE6_SUBNET_ID LEASE6_VALID_LIFETIME LEASE6_DUID LEASE6_IAID LEASE6_PREFERRED_LIFETIME LEASE6_PREFIX_LEN LEASE6_TYPE lease6_decline :: QUERY6_TYPE QUERY6_TXID QUERY6_LOCAL_ADDR QUERY6_LOCAL_PORT QUERY6_REMOTE_ADDR QUERY6_REMOTE_PORT QUERY6_IFACE_INDEX QUERY6_IFACE_NAME QUERY6_REMOTE_HWADDR QUERY6_REMOTE_HWADDR_TYPE QUERY6_PROTO QUERY6_CLIENT_ID LEASE6_ADDRESS LEASE6_CLTT LEASE6_HOSTNAME LEASE6_HWADDR LEASE6_HWADDR_TYPE LEASE6_STATE LEASE6_SUBNET_ID LEASE6_VALID_LIFETIME LEASE6_DUID LEASE6_IAID LEASE6_PREFERRED_LIFETIME LEASE6_PREFIX_LEN LEASE6_TYPE kea-2.0.2/doc/sphinx/arm/integrations.rst0000644000175000017500000000060214206773363015302 00000000000000********************************* Integration with external systems ********************************* Kea provides optional support for a variety of external systems, such as RADIUS, NETCONF, YANG, and GSS-TSIG. The following sections describe how to compile Kea with those additional capabilities and how to configure them. .. include:: ext-netconf.rst .. include:: ext-gss-tsig.rst kea-2.0.2/doc/sphinx/arm/ext-gss-tsig.rst0000644000175000017500000013216314206773363015142 00000000000000 .. _gss-tsig: GSS-TSIG ======== .. _gss-tsig-overview: GSS-TSIG Overview ----------------- Kea provides a support for DNS updates, which can be protected using Transaction Signatures (or TSIG). This protection is often adequate. However some systems, in particular Active Directory (AD) on Microsoft Windows servers, chose to adopt more complex GSS-TSIG approach that offers additional capabilities as using negotiated dynamic keys. Kea provides the support of GSS-TSIG to protect DNS updates sent by the Kea DHCP-DDNS (aka D2) server in a premium hook, called `gss_tsig`. The GSS-TSIG is defined in `RFC 3645 `__. The GSS-TSIG protocol itself is an implementation of generic GSS-API v2 services, defined in `RFC 2743 `__. Many protocols are involved in this mechanism: - Kerberos 5 `RFC 4120 `__ which provides the security framework; - GSS-API (Generic Security Services Application Program Interface) `RFC 2743 `__ for the API, `RFC 2744 `__ for C bindings and `RFC 4121 `__ for the application to Kerberos 5; - SPNEGO (Simple and Protected GSS-API Negotiation Mechanism) `RFC 4178 `__ for the negotiation; - DNS update `RFC 2136 `__; - TSIG (Secret Key Transaction Authentication for DNS) `RFC 8945 `__ which protects DNS exchanges; - Secure Domain Name System (DNS) Dynamic Update `RFC 3007 `__ which is the application of TSIG to the DNS update protection; - TKEY (Secret Key Establishment for DNS) `RFC 2930 `__ which establishes secret keys for TSIG by transmitting crypto payloads between DNS parties; - GSS-TSIG `RFC 3645 `__ which is the application of GSS-API to TSIG. To summarize, GSS-API for Kerberos 5 with SPNEGO and TKEY are used to negotiate a security context between the Kea D2 server and a DNS server: .. figure:: ../uml/tkey.* The security context is then used by GSS-TSIG to protect updates: .. figure:: ../uml/update.* The Kea implementation of GSS-TSIG uses a GSS-API for Kerberos 5 with SPNEGO library. Two implementations meet this criteria: MIT Kerberos 5 and Heimdal. .. _gss-tsig-install: GSS-TSIG Compilation -------------------- The following procedure was tested on Ubuntu 20.10 and 21.04. Similar approach can be applied to other systems. 1. Obtain the kea sources and premium packages, extract kea sources, then extract premium packages into `premium/` directory within Kea source tree. 2. Run autoreconf: .. code-block:: console autoreconf -i 3. Make sure ``./configure --help`` shows the ``--with-gssapi`` option. 4. Install either MIT (``libkrb5-dev``) or Heimdal (``heimdal-dev``) library, for instance: .. code-block:: console sudo apt install libkrb5-dev 5. Run configure with the ``--with-gssapi`` option: .. code-block:: console ./configure --with-gssapi .. note: It is ``--with-gssapi`` (without dash between gss and api) to keep consistency with BIND 9 option. The ``--with-gssapi`` requires ``krb5-config`` tool to be present. This tool is provided by both MIT Kerberos 5 and Heimdal, on some systems where both Kerberos 5 and Heimdal are installed it is a symbolic link to one of them. If it's not in your standard location, you may specify it with ``--with-gssapi=/path/to/krb5-config``. It is strongly recommended to use default installation locations as provided by packages. The ``./configure`` script should complete with a successful GSS-API detection, similar to this: :: GSS-API support: GSSAPI_CFLAGS: -isystem /usr/include/mit-krb5 GSSAPI_LIBS: -L/usr/lib/x86_64-linux-gnu/mit-krb5 -Wl,-Bsymbolic-functions -Wl,-z,relro -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err 6. Compile as usual ``make -jX`` where X is the number of CPU cores available. 7. After compilation, the gss_tsig hook is available in the ``premium/src/hooks/d2/gss_tsig`` directory. It can be loaded by the Kea DHCP-DDNS (D2) daemon. The gss_tsig was developed using the MIT Kerberos 5 implementation but Heimdal is supported too. Note that Heimdal is picky about security sensitive file permissions and is known to emit an unclear error message. It is a good idea to keep these files as plain, with one link and no access for the group or other users. The krb5-config script should provide an ``--all`` option which identifies the implementation: in any report about the GSS-TSIG report please add the result of the ``--all`` option of the krb5-config used to configure Kea. .. _gss-tsig-deployment: GSS-TSIG Deployment ------------------- Before using GSS-TSIG, a GSS-TSIG capable DNS server, such as BIND 9 or alternatively Microsoft Active Directory, must be deployed. Other GSS-TSIG capable implementations may work, but were not tested. Kerberos 5 Setup ~~~~~~~~~~~~~~~~ There are two kinds of key tables (keytab files): the system one used by servers and client tables used by clients. For Kerberos 5, Kea is a **client**. Install the Kerberos 5 client library and kadmin tool: .. code-block:: console sudo apt install krb5-kdc krb5-admin-server The following examples use the ``EXAMPLE.ORG`` realm to demonstrate required configuration steps and settings. The Kerberos 5 client library must be configured (to accept incoming requests) for the realm ``EXAMPLE.ORG`` by updating the ``krb5.conf`` file (e.g. on Linux: /etc/krb5.conf): .. code-block:: ini [libdefaults] default_realm = EXAMPLE.ORG kdc_timesync = 1 ccache_type = 4 forwardable = true proxiable = true [realms] EXAMPLE.ORG = { kdc = kdc.example.org admin_server = kdc.example.org } In addition to the ``krb5.conf`` file, the ``kdc.conf`` file can be used (e.g. on Linux: /etc/krb5kdc/kdc.conf): .. code-block:: ini [kdcdefaults] kdc_ports = 750,88 [realms] EXAMPLE.ORG = { database_name = /var/lib/krb5kdc/principal admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab acl_file = /etc/krb5kdc/kadm5.acl key_stash_file = /etc/krb5kdc/stash kdc_ports = 750,88 max_life = 10h 0m 0s max_renewable_life = 7d 0h 0m 0s master_key_type = des3-hmac-sha1 #supported_enctypes = aes256-cts:normal aes128-cts:normal default_principal_flags = +preauth } The kadmind daemon ACL (Access Control List) must be configured to give permissions to the DNS client principal to access the Kerberos 5 database. (e.g. on Linux: /etc/krb5kdc/kadm5.acl): .. code-block:: ini DHCP/admin.example.org@EXAMPLE.ORG * The admin password for the default realm must be set: .. code-block:: console krb5_newrealm The following message will be displayed and you will be required to type the password for the default realm: .. code-block:: console This script should be run on the master KDC/admin server to initialize a Kerberos realm. It will ask you to type in a master key password. This password will be used to generate a key that is stored in /etc/krb5kdc/stash. You should try to remember this password, but it is much more important that it be a strong password than that it be remembered. However, if you lose the password and /etc/krb5kdc/stash, you cannot decrypt your Kerberos database. Loading random data Initializing database '/var/lib/krb5kdc/principal' for realm 'EXAMPLE.ORG', master key name 'K/M@EXAMPLE.ORG' You will be prompted for the database Master Password. It is important that you NOT FORGET this password. Enter KDC database master key: You will be required to retype the password: .. code-block:: console Re-enter KDC database master key to verify: If successfully applied, the following message will be displayed: .. code-block:: console Now that your realm is set up you may wish to create an administrative principal using the addprinc subcommand of the kadmin.local program. Then, this principal can be added to /etc/krb5kdc/kadm5.acl so that you can use the kadmin program on other computers. Kerberos admin principals usually belong to a single user and end in /admin. For example, if jruser is a Kerberos administrator, then in addition to the normal jruser principal, a jruser/admin principal should be created. Don't forget to set up DNS information so your clients can find your KDC and admin servers. Doing so is documented in the administration guide. Next step consists in creating the principals for the Bind9 DNS server (the service protected by the GSS-TSIG TKEY) and for the DNS client (the Kea DHCP-DDNS server). The Bind9 DNS server principal (used for authentication) is created the following way: .. code-block:: console kadmin.local -q "addprinc -randkey DNS/server.example.org" If successfully created, the following message will be displayed: .. code-block:: console No policy specified for DNS/server.example.org@EXAMPLE.ORG; defaulting to no policy Authenticating as principal root/admin@EXAMPLE.ORG with password. Principal "DNS/server.example.org@EXAMPLE.ORG" created. The DNS server principal must be exported so that it can be used by the Bind 9 DNS server. Only this principal is required and is is exported to the keytab file with the name ``dns.keytab``. .. code-block:: console kadmin.local -q "ktadd -k /tmp/dns.keytab DNS/server.example.org" If successfully exported, the following message will be displayed: .. code-block:: console Authenticating as principal root/admin@EXAMPLE.ORG with password. Entry for principal DNS/server.example.org with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/tmp/dns.keytab. Entry for principal DNS/server.example.org with kvno 2, encryption type aes128-cts-hmac-sha1-96 added to keytab WRFILE:/tmp/dns.keytab. The DHCP client principal (used by the Kea DHCP-DDNS server) is created the following way: .. code-block:: console kadmin.local -q "addprinc -randkey DHCP/admin.example.org" If successfully created, the following message will be displayed: .. code-block:: console No policy specified for DHCP/admin.example.org@EXAMPLE.ORG; defaulting to no policy Authenticating as principal root/admin@EXAMPLE.ORG with password. Principal "DHCP/admin.example.org@EXAMPLE.ORG" created. The DHCP client principal must be exported so that it can be used by the Kea DHCP-DDNS server and GSS-TSIG hook library. It is exported to the client keytab file with the name ```dhcp.keytab```. .. code-block:: console kadmin.local -q "ktadd -k /tmp/dhcp.keytab DHCP/admin.example.org" Finally, the krb5-admin-server must be restarted: .. code-block:: console systemctl restart krb5-admin-server.service Bind 9 with GSS-TSIG Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Bind 9 DNS server must be configured to use GSS-TSIG and to use the previously exported DNS server principal from the keytab file ``dns.keytab``. Updating the ``named.conf`` file is required: .. code-block:: console options { ... directory "/var/cache/bind"; dnssec-validation auto; listen-on-v6 { any; }; tkey-gssapi-keytab "/etc/bind/dns.keytab"; }; zone "example.org" { type master; file "/var/lib/bind/db.example.org"; update-policy { grant "DHCP/admin.example.org@EXAMPLE.ORG" zonesub any; }; }; zone "84.102.10.in-addr.arpa" { type master; file "/etc/bind/db.10"; }; The zone files should have an entry for the server principal FQDN ``server.example.org``. The ``/etc/bind/db.10`` file needs to be created or updated: .. code-block:: console ; ; BIND reverse data file for local loopback interface ; $TTL 604800 ; 1 week @ IN SOA server.example.org. root.example.org. ( 2 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ; Negative Cache TTL ) ; @ IN NS ns. 40 IN PTR ns.example.org. The ``/var/lib/bind/db.example.org`` file needs to be created or updated: .. code-block:: console $ORIGIN . $TTL 604800 ; 1 week example.org IN SOA server.example.org. root.example.org. ( 8 ; serial 604800 ; refresh (1 week) 86400 ; retry (1 day) 2419200 ; expire (4 weeks) 604800 ; minimum (1 week) ) NS example.org. A ${BIND9_IP_ADDR} AAAA ::1 $ORIGIN example.org. kdc A ${KDC_IP_ADDR} server A ${BIND9_IP_ADDR} After any configuration change the server must be reloaded or restarted: .. code-block:: console systemctl restart named.service It is possible to get status or restart logs: .. code-block:: console systemctl status named.service journalctl -u named | tail -n 30 Windows Active Directory Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This sub-section is based on an Amazon AWS provided Microsoft Windows Server 2016 with Active Directory pre-installed so describes only the steps used for GSS-TSIG deployment (for complete configuration process please refer to Microsoft documentation or other external resources. We found `this `__ tutorial very useful during configuration of our internal QA testing systems. Two Active Directory (AD) user accounts are needed: - the first account is used to download AD information, for instance the client key table of Kea - the second account will be mapped to the Kea DHCP client principal Kea needs to know: - the server IP address - the domain/realm name: the domain is in lower case, the realm in upper case, both without a final dot - the server name The second account (named ``kea`` below) is used to create a Service Principal Name (SPN): .. code-block:: console setspn -S DHCP/kea. kea After a shared secret key is generated and put in a key table file: .. code-block:: console ktpass -princ DHCP/kea.@ -mapuser kea +rndpass -mapop set -ptype KRB5_NT_PRINCIPAL -out dhcp.keytab The ``dhcp.keytab`` takes the same usage as for Unix Kerberos. GSS-TSIG Troubleshooting ~~~~~~~~~~~~~~~~~~~~~~~~ While testing GSS-TSIG integration with Active Directory we came across one very cryptic error: .. code-block:: console INFO [kea-dhcp-ddns.gss-tsig-hooks/4678.139690935890624] GSS_TSIG_VERIFY_FAILED GSS-TSIG verify failed: gss_verify_mic failed with GSSAPI error: Major = 'A token had an invalid Message Integrity Check (MIC)' (393216), Minor = 'Packet was replayed in wrong direction' (100002). In our case problem was that Kea DDNS was trying to perform update of reverse DNS zone while it was not configured. Easy solution was to add reverse DNS zone similar to the one configured in Kea. To do it open `DNS Manager` choose DNS from the list, from drop down list choose `Reverse Lookup Zones` click `Action` and `New Zone` then follow New Zone Wizard to add new zone. .. _gss-tsig-using: Using GSS-TSIG -------------- There is a number of steps required to enable the GSS-TSIG mechanism: 1. the gss_tsig hook library has to be loaded by the D2 server 2. the GSS-TSIG capable DNS servers have to be specified with their parameters An excerpt from D2 server is provided below. More examples are available in the ``doc/examples/ddns`` directory in the Kea sources. .. code-block:: javascript :linenos: :emphasize-lines: 57-107 { "DhcpDdns": { // The following parameters are used to receive NCRs (NameChangeRequests) // from the local Kea DHCP server. Make sure your kea-dhcp4 and kea-dhcp6 // matches this. "ip-address": "127.0.0.1", "port": 53001, "dns-server-timeout" : 1000, // Forward zone: secure.example.org. It uses GSS-TSIG. It is served // by two DNS servers, which listen for DDNS requests at 192.0.2.1 // and 192.0.2.2. "forward-ddns": { "ddns-domains": [ // DdnsDomain for zone "secure.example.org." { "name": "secure.example.org.", "comment": "DdnsDomain example", "dns-servers": [ { // This server has an entry in gss/servers and // thus will use GSS-TSIG. "ip-address": "192.0.2.1" }, { // This server also has an entry there, so will // use GSS-TSIG, too. "ip-address": "192.0.2.2", "port": 5300 } ] } ] }, // Reverse zone: we want to update the reverse zone "2.0.192.in-addr.arpa". "reverse-ddns": { "ddns-domains": [ { "name": "2.0.192.in-addr.arpa.", "dns-servers": [ { // There is GSS-TSIG definition for this server (see // DhcpDdns/gss-tsig/servers), so it will use // Krb/GSS-TSIG. "ip-address": "192.0.2.1" } ] } ] }, // Need to add gss-tsig hook here "hooks-libraries": [ { "library": "/opt/lib/libddns_gss_tsig.so", "parameters": { // This section governs the GSS-TSIG integration. Each server // mentioned in forward-ddns and/or reverse-ddns needs to have // an entry here to be able to use GSS-TSIG defaults (optional, // if specified they apply to all the GSS-TSIG servers, unless // overwritten on specific server level). "server-principal": "DNS/server.example.org@EXAMPLE.ORG", "client-principal": "DHCP/admin.example.org@EXAMPLE.ORG", "client-keytab": "FILE:/etc/dhcp.keytab", // toplevel only "credentials-cache": "FILE:/etc/ccache", // toplevel only "tkey-lifetime": 3600, // 1 hour "rekey-interval": 2700, // 45 minutes "retry-interval": 120, // 2 minutes "tkey-protocol": "TCP", "fallback": false, // The list of GSS-TSIG capable servers "servers": [ { // First server (identification is required) "id": "server1", "domain-names": [ ], // if not specified or empty, will // match all domains that want to // use this IP+port pair "ip-address": "192.0.2.1", "port": 53, "server-principal": "DNS/server1.example.org@EXAMPLE.ORG", "client-principal": "DHCP/admin1.example.org@EXAMPLE.ORG", "tkey-lifetime": 7200, // 2 hours "rekey-interval": 5400, // 90 minutes "retry-interval": 240, // 4 minutes "tkey-protocol": "TCP", "fallback": true // if no key is available fallback to the // standard behavior (vs skip this server) }, { // The second server (it has most of the parameters missing // as those are using the defaults specified above) "id": "server2", "ip-address": "192.0.2.2", "port": 5300 } ] } } ] // Additional parameters, such as logging, control socket and // others omitted for clarity. } } This configuration file contains a number of extra elements. First, a list of forward and/or reverse domains with related DNS servers identified by their IP+port pairs is defined. If port is not specified, the default of 53 is assumed. This is similar to basic mode with no authentication or authentication done using TSIG keys, with the exception that static TSIG keys are not referenced by name. Second, the ``libddns_gss_tsig.so`` library has to be specified on the ``hooks-libraries`` list. This hook takes many parameters. The most important one is ``servers``, which is a list of GSS-TSIG capable servers. If there are several servers and they share some characteristics, the values can be specified in ``parameters`` scope as defaults. In the example above, the defaults that apply to all servers unless otherwise specified on per server scope, are defined in lines 63 through 68. The defaults can be skipped if there is only one server defined or all servers have different values. .. table:: List of available parameters +-------------------+----------+---------+---------------------+--------------------------------+ | Name | Scope | Type | Default value | Description | | | | | | | +===================+==========+=========+=====================+================================+ | client-keytab | global / | string | empty | the Kerberos **client** key | | | server | | | table | +-------------------+----------+---------+---------------------+--------------------------------+ | credentials-cache | global / | string | empty | the Kerberos credentials cache | | | server | | | | +-------------------+----------+---------+---------------------+--------------------------------+ | server-principal | global / | string | empty | the Kerberos principal name of | | | server | | | the DNS server that will | | | | | | receive updates | +-------------------+----------+---------+---------------------+--------------------------------+ | client-principal | global / | string | empty | the Kerberos principal name of | | | server | | | the Kea D2 service | +-------------------+----------+---------+---------------------+--------------------------------+ | tkey-protocol | global / | string | "TCP" | the protocol used to establish | | | server | "TCP" / | | the security context with the | | | | "UDP" | | DNS servers | +-------------------+----------+---------+---------------------+--------------------------------+ | tkey-lifetime | global / | uint32 | | 3600 seconds | the lifetime of GSS-TSIG keys | | | server | | | ( 1 hour ) | | +-------------------+----------+---------+---------------------+--------------------------------+ | rekey-interval | global / | uint32 | | 2700 seconds | the time interval the keys are | | | server | | | ( 45 minutes ) | checked for rekeying | +-------------------+----------+---------+---------------------+--------------------------------+ | retry-interval | global / | uint32 | | 120 seconds | the time interval to retry to | | | server | | | ( 2 minutes ) | create a key if any error | | | | | | occurred previously | +-------------------+----------+---------+---------------------+--------------------------------+ | fallback | global / | true / | false | the behavior to fallback to | | | server | false | | non GSS-TSIG when GSS-TSIG | | | | | | should be used but no GSS-TSIG | | | | | | key is available. | +-------------------+----------+---------+---------------------+--------------------------------+ | exchange-timeout | global / | uint32 | | 3000 milliseconds | the time used to wait for the | | | server | | | ( 3 seconds ) | GSS-TSIG TKEY exchange to | | | | | | finish before it timeouts | +-------------------+----------+---------+---------------------+--------------------------------+ | user-context | global / | string | empty | the user provided data in JSON | | | server | | | format (will not be used by | | | | | | the GSS-TSIG hook) | +-------------------+----------+---------+---------------------+--------------------------------+ | comment | global / | string | empty | ignored | | | server | | | | +-------------------+----------+---------+---------------------+--------------------------------+ | id | server | string | empty | identifier to a DNS server | | | | | | (required) | +-------------------+----------+---------+---------------------+--------------------------------+ | domain-names | server | list of | empty | the many to one relationship | | | | strings | | between D2 DNS servers and | | | | | | GSS-TSIG DNS servers | +-------------------+----------+---------+---------------------+--------------------------------+ | ip-address | server | IPv4 / | empty | the IP address at which the | | | | IPv6 | | GSS-TSIG DNS server listens | | | | address | | for DDNS and TKEY requests | | | | | | (required) | +-------------------+----------+---------+---------------------+--------------------------------+ | port | server | uint16 | 53 | the DNS transport port at | | | | | | which the GSS-TSIG DNS server | | | | | | listens for DDNS and TKEY | | | | | | requests | +-------------------+----------+---------+---------------------+--------------------------------+ The global parameters are described below: - ``client-keytab`` specifies the Kerberos **client** key table. For instance, ``FILE:`` can be used to point to a specific file. This parameter can be specified only once, in the parameters scope, and is the equivalent of setting the ``KRB5_CLIENT_KTNAME`` environment variable. The empty value is silently ignored. - ``credentials-cache`` specifies the Kerberos credentials cache. For instance ``FILE:`` can be used to point to a file or if using a directory which supports more than one principal ``DIR:``. This parameter can be specified only once, in the parameters scope, and is the equivalent of setting the ``KRB5CCNAME`` environment variable. The empty value is silently ignored. - ``server-principal`` is the Kerberos principal name of the DNS server that will receive updates. In plain words, this is the DNS server's name in the Kerberos system. This parameter is mandatory. It uses the typical Kerberos notation: ``/@``. - ``client-principal`` is the Kerberos principal name of the Kea D2 service. It is optional. It uses the typical Kerberos notation: ``/@``. - ``tkey-protocol`` determines which protocol is used to establish the security context with the DNS servers. Currently the only supported values are TCP (the default) and UDP. - ``tkey-lifetime`` determines the lifetime of GSS-TSIG keys in the TKEY protocol. The value must be greater than the ``rekey-interval`` value. It is expressed in seconds and it default to 3600 seconds (one hour) if not specified. - ``rekey-interval`` governs the time interval the keys for each configured server are checked for rekeying, i.e. a new key is created to replace the current usable one when its age is greater than the ``rekey-interval`` value. The value must be smaller than the ``tkey-lifetime`` value (it is recommend between 50% and 80% of the ``tkey-lifetime`` value). It is expressed in seconds and it defaults to 2700 seconds (45 minutes, 75% of one hour) if not specified. - ``retry-interval`` governs the time interval to retry to create a key if any error occurred previously for any configured server. The value must be smaller than the ``rekey-interval`` value, and should be at most 1/3 of the difference between ``tkey-lifetime`` and ``rekey-interval``. It is expressed in seconds and it defaults to 120 seconds (2 minutes) if not specified. - ``fallback`` governs the behavior when GSS-TSIG should be used (a matching DNS server is configured) but no GSS-TSIG key is available. If configured to false (the default) this server is skipped, if configured to true the DNS server is ignored and the DNS update is sent with the configured DHCP-DDNS protection e.g. TSIG key or without any protection when none was configured. - ``exchange-timeout`` governs the time used to wait for the GSS-TSIG TKEY exchange to finish before it timeouts. It is expressed in milliseconds and it defaults to 3000 milliseconds (3 seconds) if not specified. - ``user-context`` is an optional parameter (see :ref:`user-context` for a general description of user contexts in Kea). - ``comment`` is allowed but currently ignored. - ``servers`` specifies the list of DNS servers where GSS-TSIG is enabled. The server map parameters are described below: - ``id`` assigns an identifier to a DNS server. It is used for statistics and commands. It is required, must be not empty and unique. - ``domain-names`` governs the many to one relationship between D2 DNS servers and GSS-TSIG DNS servers: for each domain name of this list, a D2 DNS server for this domain with the IP address and port is looked for. An empty list (the default) means that all domains match. - ``ip-address`` specifies the IP address at which the GSS-TSIG DNS server listens for DDNS and TKEY requests. It is a mandatory parameter. - ``port`` specifies the DNS transport port at which the GSS-TSIG DNS server listens for DDNS and TKEY requests. It defaults to 53. - ``server-principal`` is the Kerberos principal name of the DNS server that will receive updates. The server principal parameter per server takes precedence. It is a mandatory parameter which must be specified at least at the global or the server level. - ``client-principal`` is the Kerberos principal name of the Kea D2 service for this DNS server. The client principal parameter per server takes precedence. It is an optional parameter i.e. to not specify it at both the global and the server level is accepted. - ``tkey-protocol`` determines which protocol is used to establish the security context with the DNS server. The TKEY protocol parameter per server takes precedence. Default and supported values are the same as for the global level parameter. - ``tkey-lifetime`` determines the lifetime of GSS-TSIG keys in the TKEY protocol for the DNS server. The TKEY lifetime parameter per server takes precedence. Default and supported values are the same as for the global level parameter. - ``rekey-interval`` governs the time interval the keys for this particular server are checked for rekeying, i.e. a new key is created to replace the current usable one when its age is greater than the ``rekey-interval`` value. The value must be smaller than the ``tkey-lifetime`` value (it is recommend between 50% and 80% of the ``tkey-lifetime`` value). The rekey interval parameter per server takes precedence. Default and supported values are the same as for the global level parameter. - ``retry-interval`` governs the time interval to retry to create a key if any error occurred previously for this particular server. The value must be smaller than the ``rekey-interval`` value, and should be at most 1/3 of the difference between ``tkey-lifetime`` and ``rekey-interval``. The retry interval parameter per server takes precedence. Default and supported values are the same as for the global level parameter. - ``fallback`` governs the behavior when GSS-TSIG should be used (a matching DNS server is configured) but no GSS-TSIG key is available. The fallback parameter per server takes precedence. Default and supported values are the same as for the global level parameter. - ``exchange-timeout`` governs the time used to wait for the GSS-TSIG TKEY exchange to finish before it timeouts. The exchange timeout parameter per server takes precedence. Default and supported values are the same as for the global level parameter. - ``user-context`` is an optional parameter (see :ref:`user-context` for a general description of user contexts in Kea). - ``comment`` is allowed but currently ignored. GSS-TSIG Automatic Key Removal ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The server will periodically delete keys which expired more than 3 times the maximum key lifetime (``tkey-lifetime`` parameter). The user has the option to purge keys on demand by using ``gss-tsig-purge-all`` command (see :ref:`command-gss-tsig-purge-all`) or ``gss-tsig-purge`` command (see :ref:`command-gss-tsig-purge`). GSS-TSIG Configuration for Deployment ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When using the Kerberos 5 and Bind9 setup of :ref:`gss-tsig-deployment` the local resolver must point to the Bind9 named server address and local Kerberos be configured by putting in the ``krb5.conf`` file: .. code-block:: ini [libdefaults] default_realm = EXAMPLE.ORG kdc_timesync = 1 ccache_type = 4 forwardable = true proxiable = true [realms] EXAMPLE.ORG = { kdc = kdc.example.org admin_server = kdc.example.org } With Windows AD the DNS service is provided by AD. AD also provides the Kerberos service and the ``krb5.conf`` file becomes: .. code-block:: ini [libdefaults] default_realm = kdc_timesync = 1 ccache_type = 4 forwardable = true proxiable = true [realms] ${REALM} = { kdc = admin_server = } Even when the GSS-API library can use the secret from the client key table it is far better to get and cache credentials. This can be done manually by: .. code-block:: console kinit -k -t /tmp/dhcp.keytab DHCP/admin.example.org or when using AD: .. code-block:: console kinit -k -t /tmp/dhcp.keytab DHCP/kea. The credential cache can be displayed using ``klist``. In production it is better to rely on a Kerberos Credential Manager as the System Security Services Daemon (``sssd``). The server principal will be "DNS/server.example.org@EXAMPLE.ORG¨ or for AD "DNS/.@". .. _stats-gss-tsig: GSS-TSIG Statistics ------------------- The GSS-TSIG hook library introduces new statistics at global and per DNS server levels: - ``gss-tsig-key-created`` - number of created GSS-TSIG keys - ``tkey-sent`` - sent TKEY exchange initial requests - ``tkey-success`` - TKEY exchanges which completed with a success - ``tkey-timeout`` - TKEY exchanges which completed on timeout - ``tkey-error`` - TKEY exchanges which completed with an error other than timeout The relationship between keys and DNS servers are very different between the D2 code and static TSIG keys, and GSS-TSIG keys and DNS servers: - a static TSIG key can be shared between many DNS servers - a GSS-TSIG key is used only by one DNS server inside a dedicated set of keys. .. _commands-gss-tsig: GSS-TSIG Commands ----------------- The GSS-TSIG hook library supports some commands which are described below. .. _command-gss-tsig-get-all: The gss-tsig-get-all Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command lists GSS-TSIG servers and keys. An example command invocation looks like this: .. code-block:: json { "command": "gss-tsig-get-all" } An example response returning 1 GSS-TSIG servers and 1 keys: .. code-block:: json { "result": 0, "text": "1 GSS-TSIG servers and 1 keys", "arguments": { "gss-tsig-servers": [ { "id": "foo", "ip-address": "192.1.2.3", "port": 53, "server-principal": "DNS/foo.com@FOO.COM", "key-name-suffix": "foo.com.", "tkey-lifetime": 3600, "tkey-protocol": "TCP", "keys": [ { "name": "1234.sig-foo.com.", "inception-date": "2021-09-05 12:23:36.281176", "server-id": "foo", "expire-date": "2021-09-05 13:23:36.281176", "status": "not yet ready", "tkey-exchange": true } ] }, { "id": "bar", "ip-address": "192.1.2.4", "port": 53, "server-principal": "DNS/bar.com@FOO.COM", "key-name-suffix": "bar.com.", "tkey-lifetime": 7200, "tkey-protocol": "UDP", "keys": [ ] } ] } } .. _command-gss-tsig-get: The gss-tsig-get Command ~~~~~~~~~~~~~~~~~~~~~~~~ This command retrieves information about the specified GSS-TSIG server. An example command invocation looks like this: .. code-block:: json { "command": "gss-tsig-get", "arguments": { "server-id": "foo" } } An example response returning information about server 'foo': .. code-block:: json { "result": 0, "text": "GSS-TSIG server[foo] found", "arguments": { "id": "foo", "ip-address": "192.1.2.3", "port": 53, "server-principal": "DNS/foo.com@FOO.COM", "key-name-suffix": "foo.com.", "tkey-lifetime": 3600, "tkey-protocol": "TCP", "keys": [ { "name": "1234.sig-foo.com.", "server-id": "foo", "inception-date": "2021-09-05 12:23:36.281176", "expire-date": "2021-09-05 13:23:36.281176", "status": "not yet ready", "tkey-exchange": true } ] } } .. _command-gss-tsig-list: The gss-tsig-list Command ~~~~~~~~~~~~~~~~~~~~~~~~~ This command lists GSS-TSIG server IDs and key names. An example command invocation looks like this: .. code-block:: json { "command": "gss-tsig-list" } An example response returning 2 GSS-TSIG servers and 3 keys: .. code-block:: json { "result": 0, "text": "2 GSS-TSIG servers and 3 keys", "arguments": { "gss-tsig-servers": [ "foo", "bar" ], "gss-tsig-keys": [ "1234.example.com.", "5678.example.com.", "43888.example.org." ] } } .. _command-gss-tsig-key-get: The gss-tsig-key-get Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command retrieves information about the specified GSS-TSIG key. An example command invocation looks like this: .. code-block:: json { "command": "gss-tsig-key-get", "arguments": { "key-name": "1234.sig-foo.com." } } An example response returning information about GSS-TSIG key '1234.sig-foo.com.': .. code-block:: json { "result": 0, "text": "GSS-TSIG key '1234.sig-foo.com.' found", "arguments": { "name": "1234.sig-foo.com.", "server-id": "foo", "inception-date": "2021-09-05 12:23:36.281176", "expire-date": "2021-09-05 13:23:36.281176", "status": "not yet ready", "tkey-exchange": true } } .. _command-gss-tsig-key-expire: The gss-tsig-key-expire Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command expires the specified GSS-TSIG key. An example command invocation looks like this: .. code-block:: json { "command": "gss-tsig-key-expire", "arguments": { "key-name": "1234.sig-foo.com." } } An example response informing about GSS-TSIG key '1234.sig-foo.com.' being expired: .. code-block:: json { "result": 0, "text": "GSS-TSIG key '1234.sig-foo.com.' expired" } .. _command-gss-tsig-key-del: The gss-tsig-key-del Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command deletes the specified GSS-TSIG key. An example command invocation looks like this: .. code-block:: json { "command": "gss-tsig-key-del", "arguments": { "key-name": "1234.sig-foo.com." } } An example response informing about GSS-TSIG key '1234.sig-foo.com.' being deleted: .. code-block:: json { "result": 0, "text": "GSS-TSIG key '1234.sig-foo.com.' deleted" } .. _command-gss-tsig-purge-all: The gss-tsig-purge-all Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command removes not usable GSS-TSIG keys. An example command invocation looks like this: .. code-block:: json { "command": "gss-tsig-purge-all" } An example response informing about 2 GSS-TSIG keys being purged: .. code-block:: json { "result": 0, "text": "2 purged GSS-TSIG keys" } .. _command-gss-tsig-purge: The gss-tsig-purge Command ~~~~~~~~~~~~~~~~~~~~~~~~~~ This command removes not usable GSS-TSIG keys for the specified server. An example command invocation looks like this: .. code-block:: json { "command": "gss-tsig-purge", "arguments": { "server-id": "foo" } } An example response informing about 2 GSS-TSIG keys for server 'foo' being purged: .. code-block:: json { "result": 0, "text": "2 purged keys for GSS-TSIG server[foo]" } .. _command-gss-tsig-rekey-all: The gss-tsig-rekey-all Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The command rekeys i.e. unconditionally creates new GSS-TSIG keys for all DNS servers. An example command invocation looks like this: .. code-block:: json { "command": "gss-tsig-rekey-all" } An example response informing that a rekey was scheduled: .. code-block:: json { "result": 0, "text": "rekeyed" } This command should be use for instance when the DHCP-DDNS server is reconnected to the network. .. _command-gss-tsig-rekey: The gss-tsig-rekey Command ~~~~~~~~~~~~~~~~~~~~~~~~~~ The command rekeys i.e. unconditionally creates new GSS-TSIG keys for a specified DNS server. An example command invocation looks like this: .. code-block:: json { "command": "gss-tsig-purge", "arguments": { "server-id": "foo" } } An example response informing that a rekey was scheduled: .. code-block:: json { "result": 0, "text": "GSS-TSIG server[foo] rekeyed" } A typical usage of this command is when a DNS server was rebooted so existing GSS-TSIG keys shared with this server can no longer be used. kea-2.0.2/doc/sphinx/arm/hooks-radius.rst0000644000175000017500000005533414206773363015220 00000000000000.. _hooks-radius: radius: RADIUS Server Support ============================= The RADIUS hooks library allows Kea to interact with two types of RADIUS servers: access and accounting. Although the most common DHCP and RADIUS integration is done on the DHCP relay-agent level (DHCP clients send DHCP packets to DHCP relays; those relays contact the RADIUS server and depending on the response either send the packet to the DHCP server or drop it), it does require DHCP relay hardware to support RADIUS communication. Also, even if the relay has the necessary support, it is often not flexible enough to send and receive additional RADIUS attributes. As such, the alternative looks more appealing: to extend the DHCP server to talk to RADIUS directly. That is the goal this library intends to fulfill. .. note:: This library may only be loaded by the ``kea-dhcp4`` or the ``kea-dhcp6`` process. The major feature of this hooks library is the ability to use RADIUS authorization. When a DHCP packet is received, the Kea server sends an Access-Request to the RADIUS server and waits for a response. The server then sends back either an Access-Accept with specific client attributes, or an Access-Reject. There are two cases supported here: first, the Access-Accept includes a Framed-IP-Address (for DHCPv4) or Framed-IPv6-Address (for DHCPv6), which will be interpreted by Kea as an instruction to assign that specified IPv4 or IPv6 address. This effectively means RADIUS can act as an address-reservation database. The second case supported is the ability to assign clients to specific pools based on a RADIUS response. In this case, the RADIUS server sends back an Access-Accept with a Framed-Pool (IPv4) or Framed-IPv6-Pool (IPv6). In both cases, Kea interprets those attributes as client classes. With the addition of the ability to limit access to pools to specific classes (see :ref:`classification-pools`), RADIUS can be used to force the client to be assigned a dynamic address from a specific pool. Furthermore, the same mechanism can be used to control what kind of options the client will get if there are DHCP options specified for a particular class. .. _hooks-radius-install: Compilation and Installation of the RADIUS Hook ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following section describes how to compile and install the software on CentOS 7.0. Other systems may differ slightly. .. note:: Starting with Kea 1.7.0, ISC now provides Kea software and hooks in convenient to use native DEB and RPM packages. This includes the RADIUS hook and the required patched version of the FreeRADIUS client library. The software compilation for RADIUS is complicated. unless you have specific reasons to compile it yourself, you should seriously consider using native packages. STEP 1: Install dependencies Several tools are needed to build the dependencies and Kea itself. The following commands should install them: .. code-block:: console $ sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm $ sudo yum install gcc-g++ openssl-devel log4cplus-devel wget git STEP 2: Install FreeRADIUS The Kea RADIUS hooks library uses the FreeRADIUS client library to conduct RADIUS communication. Unfortunately, the standard 1.1.7 release available from the project website https://freeradius.org/sub_projects/ has several serious deficiencies; ISC engineers observed a segmentation fault during testing. Also, the base version of the library does not offer asynchronous transmissions, which are essential for effective accounting implementation. Both of these issues were addressed by ISC engineers, and the changes have been reported to the FreeRADIUS client project. Acceptance of those changes is outside of ISC's control, so until those are processed, it is strongly recommended to use the FreeRADIUS client with ISC's patches. To download and compile this version, please use the following steps: .. code-block:: console $ git clone https://github.com/fxdupont/freeradius-client.git $ cd freeradius-client/ $ git checkout iscdev $ ./configure $ make $ sudo make install Additional parameters may be passed to the configure script, if needed. Once installed, the FreeRADIUS client will be installed in /usr/local. This is the default path where Kea will be looking for it. It can be installed in a different directory; if so, make sure to add that path to the configure script when compiling Kea. STEP 3: Install a recent BOOST version Kea requires a reasonably recent Boost version. Unfortunately, the version available in CentOS 7 is too old, so a newer Boost version is necessary. Furthermore, CentOS 7 has an old version of the g++ compiler that does not handle the latest Boost versions. Fortunately, Boost 1.65 meets both requirements; it is both recent enough for Kea and able to be compiled using the g++ 4.8 version in CentOS. To download and compile Boost 1.65, please use the following commands: .. code-block:: console $ wget -nd https://boostorg.jfrog.io/artifactory/main/release/1.65.1/source/boost_1_65_1.tar.gz $ tar -zxvf boost_1_65_1.tar.gz $ cd boost_1_65_1/ $ ./bootstrap.sh $ ./b2 --without-python $ sudo ./b2 install Note that the b2 script may optionally take extra parameters; one of them specifies the destination path where the sources are to be compiled. Alternatively, some systems provide newer boost packages. For example, CentOS 7 provides ``boost169-devel``. If you install it with ``yum install boost169-devel``, you will need to point Kea to it with: .. code-block:: console $ ./configure --with-boost-include=/usr/include/boost169 --with-boost-lib-dir=/usr/lib64/boost169 STEP 4: Compile and install Kea Obtain the Kea sources either by downloading them from the git repository or extracting the tarball. Use one of those commands to obtain the Kea sources. Choice 1: get from github .. code-block:: console $ git clone https://github.com/isc-projects/kea.git Choice 2: get a tarball and extract it .. parsed-literal:: $ tar -zxvf kea-|release|.tar.gz The next step is to extract the premium Kea package that contains the RADIUS repository into the Kea sources. After the tarball is extracted, the Kea sources should have a premium/ subdirectory. .. parsed-literal:: $ cd kea $ tar -zxvf ../kea-premium-radius-|release|.tar.gz Once this is done, verify that the Kea sources look similar to this: .. code-block:: console $ ls -l total 952 -rw-r--r-- 1 thomson staff 6192 Apr 25 17:38 AUTHORS -rw-r--r-- 1 thomson staff 29227 Apr 25 17:38 COPYING -rw-r--r-- 1 thomson staff 360298 Apr 25 20:00 ChangeLog -rw-r--r-- 1 thomson staff 645 Apr 25 17:38 INSTALL -rw-r--r-- 1 thomson staff 5015 Apr 25 17:38 Makefile.am -rw-r--r-- 1 thomson staff 587 Apr 25 17:38 README -rw-r--r-- 1 thomson staff 62323 Apr 25 17:38 configure.ac drwxr-xr-x 12 thomson staff 408 Apr 26 19:04 doc drwxr-xr-x 7 thomson staff 238 Apr 25 17:38 examples drwxr-xr-x 5 thomson staff 170 Apr 26 19:04 ext drwxr-xr-x 8 thomson staff 272 Apr 26 19:04 m4macros drwxr-xr-x 20 thomson staff 680 Apr 26 11:22 premium drwxr-xr-x 10 thomson staff 340 Apr 26 19:04 src drwxr-xr-x 14 thomson staff 476 Apr 26 19:04 tools The makefiles must be regenerated using ``autoreconf``. The next step is to configure Kea, and there are several essential steps necessary here. Running ``autoreconf -if`` is necessary to compile the premium package that contains RADIUS. Also, the --with-freeradius option is necessary to tell Kea where the FreeRADIUS client sources can be found. Also, since the non-standard Boost is used, the path to it must be specified. .. code-block:: console $ autoreconf -i $ ./configure --with-freeradius=/path/to/freeradius --with-boost-include=/path/to/boost --with-boost-lib-dir=/path/to/boost/state/lib For example, assuming the FreeRADIUS client was installed in the default directory (/usr/local) and the Boost 1.65 sources were compiled in /home/thomson/devel/boost1_65_1, the configure path should look as follows: .. code-block:: console $ ./configure --with-freeradius=/usr/local \ --with-boost-include=/home/thomson/devel/boost_1_65_1 \ --with-boost-lib-dir=/home/thomson/devel/boost_1_65_1/stage/lib After some checks, the configure script should print a report similar to the following: .. parsed-literal:: Kea source configure results: -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Package: Name: kea Version: |release| Extended version: |release| (tarball) OS Family: Linux Hooks directory: /usr/local/lib/kea/hooks Premium hooks: yes Included Hooks: forensic_log flex_id host_cmds subnet_cmds radius host_cache C++ Compiler: CXX: g++ --std=c++11 CXX_VERSION: g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16) CXX_STANDARD: 201103 DEFS: -DHAVE_CONFIG_H CPPFLAGS: -DOS_LINUX -DBOOST_ASIO_HEADER_ONLY CXXFLAGS: -g -O2 LDFLAGS: -lpthread KEA_CXXFLAGS: -Wall -Wextra -Wnon-virtual-dtor -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare -pthread -Wno-missing-field-initializers -fPIC Python: PYTHON_VERSION: not needed (because kea-shell is disabled) Boost: BOOST_VERSION: 1.65.1 BOOST_INCLUDES: -I/home/thomson/devel/boost_1_65_1 BOOST_LIBS: -L/home/thomson/devel/boost_1_65_1/stage/lib -lboost_system OpenSSL: CRYPTO_VERSION: OpenSSL 1.0.2k 26 Jan 2017 CRYPTO_CFLAGS: CRYPTO_INCLUDES: CRYPTO_LDFLAGS: CRYPTO_LIBS: -lcrypto Botan: no Log4cplus: LOG4CPLUS_VERSION: 1.1.3 LOG4CPLUS_INCLUDES: -I/usr/include LOG4CPLUS_LIBS: -L/usr/lib -L/usr/lib64 -llog4cplus Flex/bison: FLEX: flex BISON: bison -y MySQL: no PostgreSQL: no Cassandra CQL: no Google Test: no Google Benchmark: no FreeRADIUS client: FREERADIUS_INCLUDE: -I/usr/local/include FREERADIUS_LIB: -L/usr/local/lib -lfreeradius-client FREERADIUS_DICTIONARY: /usr/local/etc/radiusclient/dictionary Developer: Enable Debugging: no Google Tests: no Valgrind: not found C++ Code Coverage: no Logger checks: no Generate Documentation: no Parser Generation: no Kea-shell: no Perfdhcp: no Please make sure that the compilation includes the following: - RADIUS listed in Included Hooks; - FreeRADIUS client directories printed and pointing to the right directories; - Boost version at least 1.65.1. The versions available in CentOS 7 (1.48 and 1.53) are too old. Once the configuration is complete, compile Kea using make. If the system has more than one core, using the "-j N" option is recommended to speed up the build. .. code-block:: console $ make -j5 $ sudo make install .. _hooks-radius-config: RADIUS Hook Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~ The RADIUS hook is a library that has to be loaded by either DHCPv4 or DHCPv6 Kea servers. Unlike some other available hooks libraries, this one takes many parameters. For example, this configuration could be used: :: "Dhcp4": { # Your regular DHCPv4 configuration parameters here. "hooks-libraries": [ { # Note that RADIUS requires host-cache for proper operation, # so that library is loaded as well. "library": "/usr/local/lib/kea/hooks/libdhcp_host_cache.so" }, { "library": "/usr/local/lib/kea/hooks/libdhc_radius.so", "parameters": { # Specify where FreeRADIUS dictionary could be located "dictionary": "/usr/local/etc/freeradius/dictionary", # Specify which address to use to communicate with RADIUS servers "bindaddr": "*", # more RADIUS parameters here } } ] RADIUS is a complicated environment. As such, it is not feasible to provide a default configuration that works for everyone. However, we do have one example that showcases some of the more common features. Please see doc/examples/kea4/hooks-radius.json in the Kea sources. The RADIUS hook library supports the following global configuration flags, which correspond to FreeRADIUS client library options: - ``bindaddr`` (default "*") - specifies the address to be used by the hooks library in communication with RADIUS servers. The "*" special value tells the kernel to choose the address. - ``canonical-mac-address`` (default false) - specifies whether MAC addresses in attributes follow the canonical RADIUS format (lowercase pairs of hexadecimal digits separated by '-'). - ``client-id-pop0`` (default false) - used with flex-id, removes the leading zero (or pair of zeroes in DHCPv6) type in client-id (aka duid in DHCPv6). Implied by client-id-printable. - ``client-id-printable`` (default false) - checks whether the client-id/duid content is printable and uses it as is instead of in hexadecimal. Implies client-id-pop0 and extract-duid as 0 and 255 are not printable. - ``deadtime`` (default 0) is a mechanism to try unresponsive servers after responsive servers. Its value specifies the number of seconds after which a server is considered not to have answered, so 0 disables the mechanism. As the asynchronous communication does not use locks or atomics, it is recommended that you do not use this feature when running in this mode. - ``dictionary`` (default set by configure at build time) - is the attribute and value dictionary. Note that it is a critical parameter. You may find dictionary examples in the FreeRADIUS repository under the etc directory. - ``extract-duid`` (default true) - extracts the embedded duid from an RFC 4361-compliant DHCPv4 client-id. Implied by client-id-printable. - ``identifier-type4`` (default client-id) - specifies the identifier type to build the User-Name attribute. It should be the same as the host identifier, and when the flex-id hook library is used the replace-client-id must be set to true; client-id will be used with client-id-pop0. - ``identifier-type6`` (default duid) - specifies the identifier type to build the User-Name attribute. It should be the same as the host identifier, and when the flex-id hook library is used the replace-client-id must be set to true; duid will be used with client-id-pop0. - ``realm`` (default "") - is the default realm. - ``reselect-subnet-address`` (default false) - uses the Kea reserved address/RADIUS Framed-IP-Address or Framed-IPv6-Address to reselect subnets where the address is not in the subnet range. - ``reselect-subnet-pool`` (default false) - uses the Kea client-class/RADIUS Frame-Pool to reselect subnets where no available pool can be found. - ``retries`` (default 3) - is the number of retries before trying the next server. Note that it is not supported for asynchronous communication. - ``session-history`` (default "") - is the name of the file providing persistent storage for accounting session history. - ``timeout`` (default 10) - is the number of seconds during which a response is awaited. When ``reselect-subnet-pool`` or ``reselect-subnet-address`` is set to true at the reception of RADIUS Access-Accept, the selected subnet is checked against the client-class name or the reserved address; if it does not match, another subnet is selected among matching subnets. Two services are supported: - ``access`` - the authentication service. - ``accounting`` - the accounting service. Configuration of services is divided into two parts: - Servers that define RADIUS servers that the library is expected to contact. Each server may have the following items specified: - ``name`` - specifies the IP address of the server (it is possible to use a name which will be resolved, but it is not recommended). - ``port`` (default RADIUS authentication or accounting service) - specifies the UDP port of the server. Note that the FreeRADIUS client library by default uses ports 1812 (authorization) and 1813 (accounting). Some server implementations use 1645 (authorization) and 1646 (accounting). The "port" parameter may be used to adjust as needed. - ``secret`` - authenticates messages. There may be up to eight servers. Note that when no server is specified, the service is disabled. - Attributes which define additional information that the Kea server will send to a RADIUS server. The parameter must be identified either by a name or type. Its value can be specified in one of three possible ways: data (which defines a plain text value), raw (which defines the value in hex), or expr (which defines an expression, which will be evaluated for each incoming packet independently). - ``name`` - the name of the attribute. - ``type`` - the type of the attribute. Either the type or the name must be provided, and the attribute must be defined in the dictionary. - ``data`` - the first of three ways to specify the attribute content. The data entry is parsed by the FreeRADIUS library, so values defined in the dictionary of the attribute may be used. - ``raw`` - the second of three ways to specify the attribute content; it specifies the content in hexadecimal. Note that it does not work with integer-content attributes (date, integer, and IPv4 address); a string-content attribute (string, IPv6 address, and IPv6 prefix) is required. - ``expr`` - the last way to specify the attribute content. It specifies an evaluation expression which must return a not-empty string when evaluated with the DHCP query packet. Currently this is restricted to the access service. For example, to specify a single access server available on localhost that uses "xyz123" as a secret, and tell Kea to send three additional attributes (Password, Connect-Info, and Configuration-Token), the following snippet could be used: :: "parameters": { # Other RADIUS parameters here "access": { # This starts the list of access servers "servers": [ { # These are parameters for the first (and only) access server "name": "127.0.0.1", "port": 1812, "secret": "xyz123" } # Additional access servers could be specified here ], # This defines a list of additional attributes Kea will send to each # access server in Access-Request. "attributes": [ { # This attribute is identified by name (must be present in the # dictionary) and has static value (i.e. the same value will be # sent to every server for every packet) "name": "Password", "data": "mysecretpassword" }, { # It is also possible to specify an attribute using its type, # rather than a name. 77 is Connect-Info. The value is specified # using hex. Again, this is a static value. It will be sent the # same for every packet and to every server. "type": 77, "raw": "65666a6a71" }, { # This example shows how an expression can be used to send dynamic # value. The expression (see Section 13) may take any value from # the incoming packet or even its metadata (e.g. the interface # it was received over from) "name": "Configuration-Token", "expr": "hexstring(pkt4.mac,':')" } ] # End of attributes }, # End of access # Accounting parameters. "accounting": { # This starts the list of accounting servers "servers": [ { # These are parameters for the first (and only) accounting server "name": "127.0.0.1", "port": 1813, "secret": "sekret" } # Additional accounting servers could be specified here ] } } Customization is sometimes required for certain attributes by devices belonging to various vendors. This is a great way to leverage the expression evaluation mechanism. For example, MAC addresses which you might use as a convenience value for the User-Name attribute most likely will appear in colon-hexadecimal notation ``de:ad:be:ef:ca:fe``, but it might need to be expressed in: * hyphen-hexadecimal notation ``de-ad-be-ef-ca-fe`` .. code-block:: json { "parameters": { "access": { "attributes": [ { "name": "User-Name", "expr": "hexstring(pkt4.mac, '-')" } ] } } } * period-separated hexadecimal notation ``dead.beef.cafe``, preferred by Cisco devices .. code-block:: json { "parameters": { "access": { "attributes": [ { "name": "User-Name", "expr": "concat(concat(concat(substring(hexstring(pkt4.mac, ''), 0, 4), '.'), concat(substring(hexstring(pkt4.mac, ''), 4, 4), '.'), concat(substring(hexstring(pkt4.mac, ''), 8, 4), '.'))" } ] } } } For the RADIUS hooks library to operate properly in DHCPv4, the Host Cache hooks library must also be loaded. The reason for this is somewhat complex. In a typical deployment, the DHCP clients send their packets via DHCP relay which inserts certain Relay Agent Information options, such as circuit-id or remote-id. The values of those options are then used by the Kea DHCP server to formulate the necessary attributes in the Access-Request message sent to the RADIUS server. However, once the DHCP client gets its address, it then renews by sending packets directly to the DHCP server. As a result, the relays are not able to insert their RAI options, and the DHCP server cannot send the Access-Request queries to the RADIUS server by using just the information from incoming packets. Kea needs to keep the information received during the initial Discover/Offer exchanges and use it again later when sending accounting messages. This mechanism is implemented based on user context in host reservations. (See :ref:`user-context` and :ref:`user-context-hooks` for details.) The host cache mechanism allows the information retrieved by RADIUS to be stored and later used for sending accounting and access queries to the RADIUS server. In other words, the host-cache mechanism is mandatory, unless administrators do not want RADIUS communication for messages other than Discover and the first Request from each client. kea-2.0.2/doc/sphinx/arm/stats.rst0000644000175000017500000003517714206773363013751 00000000000000.. _stats: ********** Statistics ********** Statistics Overview =================== Both Kea DHCP servers support statistics gathering. A working DHCP server encounters various events that can cause certain statistics to be collected. For example, a DHCPv4 server may receive a packet (the pkt4-received statistic increases by one) that after parsing is identified as a DHCPDISCOVER (pkt4-discover-received). The server processes it and decides to send a DHCPOFFER representing its answer (the pkt4-offer-sent and pkt4-sent statistics increase by one). Such events happen frequently, so it is not uncommon for the statistics to have values in the high thousands. They can serve as an easy and powerful tool for observing a server's and a network's health. For example, if the pkt4-received statistic stops growing, it means that the clients' packets are not reaching the server. There are four types of statistics: - *integer* - this is the most common type. It is implemented as a 64-bit integer (int64_t in C++), so it can hold any value between -2^63 to 2^63-1. - *floating point* - this type is intended to store floating-point precision. It is implemented as a C++ double type. - *duration* - this type is intended for recording time periods. It uses the \`boost::posix_time::time_duration type, which stores hours, minutes, seconds, and microseconds. - *string* - this type is intended for recording statistics in textual form. It uses the C++ std::string type. During normal operation, the DHCPv4 and DHCPv6 servers gather statistics. For a list of DHCPv4 and DHCPv6 statistics, see :ref:`dhcp4-stats` and :ref:`dhcp6-stats`, respectively. To extract data from the statistics module, the control channel can be used. See :ref:`ctrl-channel` for details. It is possible to retrieve a single statistic or all statistics, reset statistics (i.e. set to a neutral value, typically zero), or even completely remove a single statistic or all statistics. See the section :ref:`command-stats` for a list of statistics-oriented commands. Statistics can be used by external tools to monitor Kea. One example of such a tool is Stork. See :ref:`stork` for details on how to use it to retrieve statistics periodically (and use other data sources) to get better insight into Kea health and operational status. .. _stats-lifecycle: Statistics Lifecycle ==================== In Kea 1.6.0 version and earlier, when the Kea server is started some of the statistics are initially not initialized. For example, the ``pkt4-received`` statistic is not available until the first DHCP packet is received. In the later Kea versions, this behavior has been changed and all of the statistics supported by the servers are initialized upon the servers' startup and should be returned in response to the commands such as ``statistic-get-all``. The runtime statistics concerning DHCP packets processed is initially set to 0 and is reset upon the server restart. Per-subnet statistics are recalculated when reconfiguration takes place. In general, once a statistic is initialized it is held in the manager until explicitly removed, by ``statistic-remove`` or ``statistic-remove-all`` being called, or when the server is shut down. Removing a statistic that is updated frequently makes little sense, as it will be re-added when the server code next records that statistic. The ``statistic-remove`` and ``statistic-remove-all`` commands are intended to remove statistics that are not expected to be observed in the near future. For example, a misconfigured device in a network may cause clients to report duplicate addresses, so the server will report increasing values of pkt4-decline-received. Once the problem is found and the device is removed, the system administrator may want to remove the pkt4-decline-received statistic, so it will not be reported anymore. If a duplicate address is ever detected again, the server will add this statistic back. .. _command-stats: Commands for Manipulating Statistics ==================================== There are several commands defined that can be used for accessing (-get), resetting to zero or a neutral value (-reset), or removing a statistic completely (-remove). We can change the statistics time based limit (-sample-age-set) and size based limit (-sample-count-set) which control how long or how many samples of a given statistic are retained. The difference between reset and remove is somewhat subtle. The reset command sets the value of the statistic to zero or a neutral value, so after this operation, the statistic will have a value of 0 (integer), 0.0 (float), 0h0m0s0us (duration), or "" (string). When requested, a statistic with the values mentioned will be returned. ``Remove`` removes a statistic completely, so the statistic will no longer be reported. Please note that the server code may add it back if there is a reason to record it. .. note:: The following sections describe commands that can be sent to the server; the examples are not fragments of a configuration file. For more information on sending commands to Kea, see :ref:`ctrl-channel`. .. _command-statistic-get: The statistic-get Command ------------------------- The ``statistic-get`` command retrieves a single statistic. It takes a single-string parameter called ``name``, which specifies the statistic name. An example command may look like this: :: { "command": "statistic-get", "arguments": { "name": "pkt4-received" } } The server returns details of the requested statistic, with a result of 0 indicating success and the specified statistic as the value of the "arguments" parameter. If the requested statistic is not found, the response will contain an empty map, i.e. only { } as an argument, but the status code will still indicate success (0). An example response: :: { "command": "statistic-get", "arguments": { "pkt4-received": [ [ 125, "2019-07-30 10:11:19.498739" ], [ 100, "2019-07-30 10:11:19.498662" ] ] }, "result": 0 } .. _command-statistic-reset: The statistic-reset Command --------------------------- The ``statistic-reset`` command sets the specified statistic to its neutral value: 0 for integer, 0.0 for float, 0h0m0s0us for time duration, and "" for string type. It takes a single-string parameter called ``name``, which specifies the statistic name. An example command may look like this: :: { "command": "statistic-reset", "arguments": { "name": "pkt4-received" } } If the specific statistic is found and the reset is successful, the server responds with a status of 0, indicating success, and an empty parameters field. If an error is encountered (e.g. the requested statistic was not found), the server returns a status code of 1 (error) and the text field contains the error description. .. _command-statistic-remove: The statistic-remove Command ---------------------------- The ``statistic-remove`` command attempts to delete a single statistic. It takes a single-string parameter called ``name``, which specifies the statistic name. An example command may look like this: :: { "command": "statistic-remove", "arguments": { "name": "pkt4-received" } } If the specific statistic is found and its removal is successful, the server responds with a status of 0, indicating success, and an empty parameters field. If an error is encountered (e.g. the requested statistic was not found), the server returns a status code of 1 (error) and the text field contains the error description. .. _command-statistic-get-all: The statistic-get-all Command ----------------------------- The ``statistic-get-all`` command retrieves all statistics recorded. An example command may look like this: :: { "command": "statistic-get-all", "arguments": { } } The server responds with details of all recorded statistics, with a result set to 0 to indicate that it iterated over all statistics (even when the total number of statistics is zero). An example response returning all collected statistics: :: { "command": "statistic-get-all", "arguments": { "cumulative-assigned-addresses": [ [ 0, "2019-07-30 10:04:28.386740" ] ], "declined-addresses": [ [ 0, "2019-07-30 10:04:28.386733" ] ], "reclaimed-declined-addresses": [ [ 0, "2019-07-30 10:04:28.386735" ] ], "reclaimed-leases": [ [ 0, "2019-07-30 10:04:28.386736" ] ], "subnet[1].assigned-addresses": [ [ 0, "2019-07-30 10:04:28.386740" ] ], "subnet[1].cumulative-assigned-addresses": [ [ 0, "2019-07-30 10:04:28.386740" ] ], "subnet[1].declined-addresses": [ [ 0, "2019-07-30 10:04:28.386743" ] ], "subnet[1].reclaimed-declined-addresses": [ [ 0, "2019-07-30 10:04:28.386745" ] ], "subnet[1].reclaimed-leases": [ [ 0, "2019-07-30 10:04:28.386747" ] ], "subnet[1].total-addresses": [ [ 200, "2019-07-30 10:04:28.386719" ] ] }, "result": 0 } .. _command-statistic-reset-all: The statistic-reset-all Command ------------------------------- The ``statistic-reset`` command sets all statistics to their neutral values: 0 for integer, 0.0 for float, 0h0m0s0us for time duration, and "" for string type. An example command may look like this: :: { "command": "statistic-reset-all", "arguments": { } } If the operation is successful, the server responds with a status of 0, indicating success, and an empty parameters field. If an error is encountered, the server returns a status code of 1 (error) and the text field contains the error description. .. _command-statistic-remove-all: The statistic-remove-all Command -------------------------------- The ``statistic-remove-all`` command attempts to delete all statistics. An example command may look like this: :: { "command": "statistic-remove-all", "arguments": { } } If the removal of all statistics is successful, the server responds with a status of 0, indicating success, and an empty parameters field. If an error is encountered, the server returns a status code of 1 (error) and the text field contains the error description. .. _command-statistic-sample-age-set: The statistic-sample-age-set Command ---------------------------------------- The ``statistic-sample-age-set`` command sets time based limit for collecting samples for a given statistic. It takes two parameters a string called ``name``, which specifies the statistic name and an integer value called ``duration``, which specifies the time limit for the given statistic in seconds. An example command may look like this: :: { "command": "statistic-sample-age-set", "arguments": { "name": "pkt4-received", "duration": 1245 } } The server will respond with message about successfully set limit for the given statistic, with a result set to 0 indicating success and an empty parameters field. If an error is encountered (e.g. the requested statistic was not found), the server returns a status code of 1 (error) and the text field contains the error description. .. _command-statistic-sample-age-set-all: The statistic-sample-age-set-all Command -------------------------------------------- The ``statistic-sample-age-set-all`` command sets time based limits for collecting samples for all statistics. It takes a single-integer parameter called ``duration``, which specifies the time limit for statistic in seconds. An example command may look like this: :: { "command": "statistic-sample-age-set-all", "arguments": { "duration": 1245 } } The server will respond with message about successfully set limit for all statistics, with a result set to 0 indicating success and an empty parameters field. If an error is encountered, the server returns a status code of 1 (error) and the text field contains the error description. .. _command-statistic-sample-count-set: The statistic-sample-count-set Command ------------------------------------------ The ``statistic-sample-count-set`` command sets size based limit for collecting samples for a given statistic. An example command may look like this: :: { "command": "statistic-sample-count-set", "arguments": { "name": "pkt4-received", "max-samples": 100 } } The server will respond with message about successfully set limit for the given statistic, with a result set to 0 indicating success and an empty parameters field. If an error is encountered (e.g. the requested statistic was not found), the server returns a status code of 1 (error) and the text field contains the error description. .. _command-statistic-sample-count-set-all: The statistic-sample-count-set-all Command ---------------------------------------------- The ``statistic-sample-count-set-all`` command sets size based limits for collecting samples for all statistics. An example command may look like this: :: { "command": "statistic-sample-count-set-all", "arguments": { "max-samples": 100 } } The server will respond with message about successfully set limit for all statistics, with a result set to 0 indicating success and an empty parameters field. If an error is encountered, the server returns a status code of 1 (error) and the text field contains the error description. .. _time-series: Time Series =========== Previously, by default, each statistic held only a single data point. When Kea attempted to record a new value, the existing previous value was overwritten. That approach has the benefit of taking up little memory and it covers most cases reasonably well. However, there may be cases where you need to have many data points for some process. For example, some processes, such as received packet size, packet processing time or number of database queries needed to process a packet, are not cumulative and it would be useful to keep many data points, perhaps to do some form of statistical analysis afterwards. Since Kea 1.6, by default, each statistic holds 20 data points. Setting such a limit prevents unlimited memory growth. There are two ways to define the limits: time based (e.g. keep samples from the last 5 minutes) and size based. It's possible to change the size based limit by using one of two commands: ``statistic-sample-count-set``, to set size limit for single statistic and ``statistic-sample-count-set-all`` for setting size based limits for all statistics. To set time based limits for single statistic use ``statistic-sample-age-set``, and ``statistic-sample-age-set-all`` to set time based limits for all statistics. For a given statistic only one type of limit can be active. It means that storage is limited only by time based limit or size based, never by both of them. kea-2.0.2/doc/sphinx/arm/database-connectivity.rst0000644000175000017500000000505314206773363017061 00000000000000.. _database-connectivity: ********************* Database Connectivity ********************* Kea servers (kea-dhcp4 and kea-dhcp6) can be configured to use a variety of database backends for leases, hosts, and configuration. All of them may be configured to support automatic recovery when connectivity is lost (see ``max-reconnect-tries``, ``reconnect-wait-time``and ``on-fail``). It is important to understand how and when automatic recovery comes into play. Automatic recovery, when configured, only operates after a successful startup or reconfiguration during which connectivity to all backends has been successfully established. During server startup, the inability to connect to any of the configured backends is always considered fatal. A fatal error is logged and the server exits, based on the idea that the configuration should be valid at startup. Exiting to the operating system allows nanny scripts to detect the problem. During dynamic reconfiguration, all backends are disconnected and then reconnected using the new configuration. If connectivity to any of the backends cannot be established, the server logs a fatal error but remains up. It is able to process commands but does not serve clients. This allows the configuration to be corrected via command, if required. During normal operations, if connectivity to any of the backends is lost and automatic recovery for that backend is enabled, the server disconnects from the respective backend and then attempts to reconnect. During the recovery process, the server ceases to serve clients according to the ``on-fail`` configured option and continues to respond to commands. The ``on-fail`` parameter configures the actions the server should take when a connection is lost. It can have one of the following values: - ``stop-retry-exit`` which indicates that the server should stop the service while it tries to recover the connection and exit if recovery is not successful after ``max-reconnect-tries``. - ``serve-retry-exit`` which indicates that the server should not stop the service while it tries to recover the connection and exit if recovery is not successful after ``max-reconnect-tries``. - ``serve-retry-continue`` which indicates that the server should not stop the service while it tries to recover the connection and not exit if recovery is not successful after ``max-reconnect-tries``. If connectivity to all backends is restored, the server returns to normal operations. If the connection can not be restored and the server is configured to exit, it issues a fatal error before shutdown. kea-2.0.2/doc/sphinx/arm/security.rst0000644000175000017500000004622314206773363014454 00000000000000.. _security: ************ Kea Security ************ Kea was designed to be installed into a protected environment in a network datacenter. It was not hardened from a security perspective. However, we have recently added support for basic HTTP authentication and TLS. .. _tls: TLS/HTTPS support ================= Kea versions 1.9.6 and later include TLS support for better security. TLS is used to secure HTTP communication. There are three levels of protection possible: - no TLS. The connection is plain text, unencrypted HTTP. This is the only option available in versions prior to Kea 1.9.6. - encryption, which protects against passive attacks and eavesdropping. In this case the server is authenticated but the client is not. This is the typical mode when securing a web site, where clients and servers are not under the control of a common entity. - mutual authentication between the client and the server. This is the strictest security mode and is the default when TLS is enabled. .. note:: TLS mutual authentication is for TLS entities only. When TLS and an HTTP authentication scheme are used together, there is no binding between the two security mechanisms and therefore no proof that the TLS client and server are the same as the HTTP authentication client and server. .. _tls_config: Building Kea with TLS/HTTPS support --------------------------------------- TLS/HTTPS support is available with either the OpenSSL or the Botan cryptographic libraries. There are some constraints on the boost library that must be used: - OpenSSL versions older than 1.0.2 are obsolete and should not be used. Kea TLS support was not tested with and is not supported on these versions. - OpenSSL version 1.0.2 has extended support for OpenSSL premium customers so should not be considered except for these premium customers. Kea TLS support was tested but is not supported on this version. - OpenSSL versions >= 1.1.x were tested and are supported. Many recent operating system versions include TLS 1.3 support. - OpenSSL 3.x is not yet released and Kea does not build with it. - LibreSSL 3.2.4 was tested. LibreSSL shares the OpenSSL 1.0.2 API so it should work but is not supported. - Botan 1.x versions are obsolete and should not be used. Kea TLS support was not tested and is not supported with these versions. - Botan versions >= 2.14.0 were tested and are supported. Note the TLS support requires the four asio header files which are included in Botan packages / installation only when Botan was configured with the ``--with-boost`` option. Sadly, many packages provided by operating systems, such as Ubuntu 20.10, did not build Botan with boost support, therefore making those packages unusable directly for Kea. It is still possible to take these files from the corresponding Botan distribution and to install them manually in the Botan include directory but of course this should be a last resort procedure. Note that without these header files or with a Botan version older than 2.14.0 Kea can still build but the TLS/HTTPS support is disabled: any attempt to use it will fail with a fatal error. - Very old boost versions provide SSL support (based on OpenSSL) without offering a choice of the TLS version: Kea can still use them but they are not recommended. - Boost versions older than 1.64 provide SSL support with a fixed choice of the TLS version: Kea enforces the use of TLS 1.2 with them. - Boost versions 1.64 or newer provide SSL support with a generic TLS version: the best (highest) version available on both peers is selected. TLS/HTTPS configuration ----------------------- The TLS configuration parameters are: - the ``trust-anchor`` string parameter specifies the name of a file or directory where the certification authority (CA) certificate of the other peer can be found. With OpenSSL the directory must include hash symbolic links. With Botan the directory is recursively searched for certificates. - the ``cert-file`` string parameter specifies the name of the file containing the end-entity certificate of the Kea instance being configured. - the ``key-file`` string parameter specifies the private key of the end-entity certificate of the Kea instance being configured. The file must not be encrypted and it is highly recommended to restrict its access. The three string parameters must be either all unspecified (TLS disabled) or all specified (TLS enabled). TLS is asymmetric: the authentication of the server by the client is mandatory but the authentication of the client by the server is optional. In TLS terms this means the server can require the client certificate or not so there is a server-specific TLS parameter. - the ``cert-required`` boolean parameter allows a server to not require the client certificate. Its default value is true which means to require the client certificate and to authenticate the client. This flag has no meaning on the client side: the server always provides a certificate which is validated by the client. Objects in files must be in the PEM format. Files can contain more than one certificate but this was not tested and is not supported. Botan requires CA certificates to have the standard CA certificate attributes, verifies that end-entity certificates are version 3 certificates (as required by the TLS standard) and supports only PKCS 8 files for the private key. .. note:: Some cryptographic libraries (e.g. Botan and recent OpenSSL) enforce minimal strength (i.e. key length), e.g. at least 2048 for RSA. A sample set of certificates and associated objects is available at ``src/lib/asiolink/testutils/ca`` in sources with a ``doc.txt`` file explaining how they were generated using the openssl command. These files are for testing purposes only. **Do not use them in production,** TLS handshake, the phase where the cryptographic parameters are exchanged and authentication is verified, can fail in multiple ways. Error messages often do not really help to find the source of the problem. Both OpenSSL and Botan provide a command line tool with a verify command which can be used to understand and fix it. Securing Kea deployment ======================= Below is a list of considerations should the administrator wish to improve Kea's security. In many cases, there are trade-offs between convenience and security. Component-based design ---------------------- The Kea architecture is modular, with separate daemons for separate tasks. A Kea deployment could include DHCPv4, DHCPv6, and Dynamic DNS daemons, a control agent daemon run on each application server, the kea-lfc utility for doing periodic lease file cleanup, and MySQL and or PostgreSQL databases, run either locally on the application servers or accessed over the internal network, and a Stork monitoring system. This modular architecture allows the administrator to minimize the attack surface by minimizing the code that is loaded and running. For example, kea-dhcp-ddns should not be run unless DNS updates are required. Similarly, kea-lfc will never be triggered (and can be safely removed or never installed) if memfile is not used. You can minimize potential Kea security issues by running only those processes required in your environment. Limiting application permissions ---------------------------------- The DHCPv4 and DHCPv6 protocols assume the server will open privileged UDP port 67 (DHCPv4) or 547 (DHCPv6). Under normal circumstances that requires root access. However, with the use of the capabilities mechanism on Linux systems, Kea can run from an unprivileged account. See :ref:`non-root` Section for details on how to run Kea without root access. The CA (Control Agent) can accept incoming HTTP or HTTPS connections. The default port is 8000, which doesn't require privileged access. Securing Kea administrative access ---------------------------------- The three primary Kea daemons (`kea-dhcp4`, `kea-dhcp6` and `kea-dhcp-ddns`) all support a control channel, which is implemented as a UNIX socket. The control channel is disabled by default, but most configuration examples have it enabled as it's a very popular feature. It opens a UNIX socket. To read from or write to this socket, generally root access is required, although if Kea is configured to run as non-root, the owner of the process can write to it. Access can be controlled using normal file access control on POSIX systems (owner, group, others, read/write). Kea configuration is controlled by a JSON file on the Kea server. This file can be viewed or edited by anyone with file permissions (permissions controlled by the operating system). Note that passwords are stored in clear text in the configuration file, so anyone with access to read the configuration file can find this information. As a practical matter, anyone with permission to edit the configuration file has control over Kea. Limiting user permission to read or write the Kea configuration file is an important security step. Securing database connections ----------------------------- Kea can optionally use an external MySQL, PostgreSQL or Cassandra database to store configuration, host reservations, leases or for forensic logging. The use of databases is a popular feature, but it is optional. It's also possible to store data in a flat file on disk. When using a database, Kea will store and use credentials in the form of username, password, host, port and database name in order to authenticate with the database. **These are stored in clear text in the configuration file.** Depending on the database configuration, it's also possible to check if the system user matches the database username. Consult MySQL or PostgreSQL manuals for details. Kea does not support SSL/TLS connection to databases yet. There is a community contributed patch available for `SSL support for MySQL `_ and `SSL support for Cassandra `_. If the communication channel to the database is a concern, the database can be run locally on the Kea server. Information leakage through logging ----------------------------------- Kea can log a whole configuration with included passwords and secrets in it. This problem has been fixed in Kea 1.9.7 by replacing the value of all entries finishing by `password` or `secret` with asterisks as it is already done for database logs. Logs are sent to stdout, stderr, files or syslog. System file permissions system apply to stdout/stderr and files. Syslog may export the logs over the network exposing them further to possible snooping. Cryptography components ----------------------- Kea has support for two cryptographic libraries: Botan and OpenSSL. This creates both compile and run-time dependencies. The library is chosen at compilation time. The binaries use only one library that is chosen at compilation time. Most deployments use OpenSSL, because it's much more popular, but Botan remains a fully supported alternative. The primary use cases for the cryptographic libraries are: - TLS support for CA (Control Agent), introduced in Kea 1.9.6 - TSIG signatures when sending DNS Updates - calculating DHCID records when sending DNS Updates. - random number generation (but not for usage requiring a crypto grade generator). For OpenSSL and Botan, only the low level crypto interface is used (e.g. libcrypto). Kea does not link with libssl. Some dependent software systems, for instance database client libraries, can also depend on a crypto library. One way to limit exposure for potential OpenSSL or Botan vulnerabilities is to not use the DDNS. The libraries would still be needed to build and run Kea, but the code would never be used, so any potential bugs in the libraries would not be exploitable. TSIG signatures --------------- Kea supports the following algorithms when signing DNS Updates with TSIG signatures: - HMAC-MD5 - HMAC-SHA1 - HMAC-SHA224 - HMAC-SHA256 - HMAC-SHA384 - HMAC-SHA512 See :ref:`d2-tsig-key-list-config` Section for an up to date list. Kea uses SHA256 to calculate DHCID records. This is irrelevant from the cryptography perspective, as the DHCID record is only used to generate unique identifiers for two devices that may have been assigned the same IP address at different times. Raw socket support ------------------ In principle, Kea DHCPv4 uses raw sockets to receive traffic from clients. The difficulty is with receiving packets from devices that don't have an IPv4 address yet. When dealing with direct traffic (where both client and server are connected to the same link, not separated by relays), the kernel normally drops the packet as the source IP address is 0.0.0.0. Therefore Kea needs to open raw sockets to be able to receive this traffic. However, this is not necessary if all the traffic is coming via relays, which is often the case in many networks. In that case normal UDP sockets can be used instead. There is a `dhcp-socket-type` parameter that controls that behavior. The default is to permit raw socket usage, as it is most versatile. When using raw sockets, Kea is able to receive raw layer 2 packets, bypassing most firewalls (including iptables). This effectively means that when raw sockets are used, the iptables can't be used to block DHCP traffic. This is a design choice of the Linux kernel. Kea can be switched to use UDP sockets. This is an option when all traffic is relayed. It will not work for directly connected devices. While Kea is limited to UDP sockets, iptables should work properly. If raw sockets are not required, disabling this access can improve security. Remote Administrative Access ---------------------------- Kea's Control Agent (CA) exposes a REST API over HTTP or HTTPS (HTTP over TLS). The CA is an optional feature that is disabled by default, but it is very popular. When enabled, it listens on the loopback address (127.0.0.1 or ::1) by default, unless configured otherwise. See :ref:`tls` Section about protecting the TLS traffic. Limiting the incoming connections with a firewall, such as iptables, is generally a good idea. Note that in HA (High Availability) deployments, DHCP partners connect to each other using CA connection. Authentication for Kea's REST API --------------------------------- Kea 1.9.0 added support for basic HTTP authentication `RFC 7617 `_ to control access for incoming REST commands over HTTP. The credentials (username, password) are stored in a local Kea configuration file on disk. The username is logged with the API command so it is possible to determine which authenticated user performed each command. The access control details are logged using a dedicated ``auth`` logger. Basic HTTP authentication is weak on its own as there are known dictionary attacks, but those attacks require man-in-the-middle to get access to the HTTP traffic. That can be eliminated by using basic HTTP authentication exclusively over TLS. In fact, if possible, using client certificates for TLS is better than using basic HTTP authentication. Kea 1.9.2 introduced a new ``auth`` hook point. With this new hook point, it is possible to develop an external hook library to extend the access controls, integrate with another authentication authority, or add role-based access control to the Control Agent. Kea security processes ====================== The following sections discuss how the development team ensures code quality and handles vulnerabilities. Vulnerability Handling ---------------------- ISC is an experienced and active participant in the industry standard vulnerability disclosure process and maintains accurate documentation on our process and vulnerabilities in ISC software. See https://kb.isc.org/docs/aa-00861 for ISC's Software Defect and Security Vulnerability Disclosure Policy. In case of a security vulnerability in Kea, ISC will notify support customers ahead of the public disclosure, and will provide a patch and/or updated installer package that remediates the vulnerability. When a security update is published, both the source tarballs and the ISC-maintained packages are published on the same day. This enables users of the native Linux update mechanisms (such as Debian's and Ubuntu's apt or RedHat's dnf) to update their systems promptly. Code quality and testing ------------------------ Kea undergoes extensive tests during its development. The following is an excerpt from all the processes that are used to ensure adequate code quality: - Each line of code goes through a formal review before it is accepted. The review process is documented and available publicly. - Roughly 50% of the source code is dedicated to unit tests. As of Dec. 2020, there are over 6000 unit tests and the number is increasing with time. Unit tests are required to commit any new feature. - There are around 1500 system tests for Kea. These simulate both correct and invalid situations, covering network packets (mostly DHCP, but also DNS, HTTP, HTTPS and others), command-line usage, API calls, database interactions, scripts and more. - There are performance tests with over 80 scenarios that test Kea overall performance and resiliency to various levels of traffic, measuring various metrics (latency, leases per seconds, packets per seconds, CPU usage, memory utilization and others). - Kea uses CI (Continuous Integration). This means that the great majority of tests (all unit and system tests, and in some cases also performance tests) are run for every commit. Many lighter tests are run on branches, before the code is even accepted. - Negative testing. Many unit and system tests check for negative scenarios, such as incomplete, broken, truncated packets, API commands, configuration files, incorrect sequences (such as sending packets in invalid order) and more. - The Kea development team uses many tools that perform automatic code quality checks, such as danger, as well as internally-developed sanity checkers. - The Kea team uses static code analyzers: Coverity Scan, shellcheck, danger. - The Kea team uses dynamic code analyzers: Valgrind, Thread Sanitizer (TSAN). Fuzz testing ------------ The Kea team has a process for running fuzz testing, using `AFL `_. There are two modes which are run. The first mode fuzzes incoming packets, effectively throwing millions of mostly broken packets at Kea per day. The second mode fuzzes configuration structures and forces Kea to attempt to load them. Kea has been fuzzed since around 2018 in both modes. The input seeds (the data being used to generate or "fuzz" other input) are changed periodically. Release integrity ----------------- Software releases are signed with PGP, and distributed via the ISC web site, which is itself DNSSEC-signed, so you can be confident the software has not been tampered with. Bus Factor ---------- According to `Core Infrastructure project `_, a "bus factor" or a "truck factor" is the minimum number of project members that have to suddenly disappear from a project ("hit by a bus") before the project stalls due to lack of knowledgeable or competent personnel. It's hard to estimate precisely, but the bus factor for Kea is somewhere around 5. As of 2021, there are 6 core developers and 2 QA engineers, with many additional casual contributors (product manager, support team, IT, etc). The team is geographically dispersed. kea-2.0.2/doc/sphinx/arm/config.rst0000644000175000017500000002252014206773363014044 00000000000000.. _kea-config: ***************** Kea Configuration ***************** Kea uses JSON structures to represent server configurations. The following sections describe how the configuration structures are organized. .. _json: JSON Configuration ================== JSON is the notation used throughout the Kea project. The most obvious usage is for the configuration file, but JSON is also used for sending commands over the Management API (see :ref:`ctrl-channel`) and for communicating between DHCP servers and the DDNS update daemon. Typical usage assumes that the servers are started from the command line, either directly or using a script, e.g. ``keactrl``. The configuration file is specified upon startup using the ``-c`` parameter. .. _json-format: JSON Syntax ----------- Configuration files for the DHCPv4, DHCPv6, DDNS, Control Agent, and NETCONF modules are defined in an extended JSON format. Basic JSON is defined in `RFC 7159 `__ and `ECMA 404 `__. In particular, the only boolean values allowed are true or false (all lowercase). The capitalized versions (True or False) are not accepted. Even though the JSON standard (ECMA 404) does not require JSON objects (i.e. name/value maps) to have unique entries, Kea implements them using a C++ STL map with unique entries. Therefore, if there are multiple values for the same name in an object/map, the last value overwrites previous values. Since Kea 1.9.0, configuration file parsers raise a syntax error in such cases. Kea components use extended JSON with additional features allowed: - Shell comments: any text after the hash (#) character is ignored. - C comments: any text after the double slashes (//) character is ignored. - Multiline comments: any text between /\* and \*/ is ignored. This comment can span multiple lines. - File inclusion: JSON files can include other JSON files by using a statement of the form \. The configuration file consists of a single object (often colloquially called a map) started with a curly bracket. It comprises only one of the "Dhcp4", "Dhcp6", "DhcpDdns", "Control-agent", or "Netconf" objects. It is possible to define additional elements but they will be ignored. A very simple configuration for DHCPv4 could look like this: :: # The whole configuration starts here. { # DHCPv4 specific configuration starts here. "Dhcp4": { "interfaces-config": { "interfaces": [ "eth0" ], "dhcp-socket-type": "raw" }, "valid-lifetime": 4000, "renew-timer": 1000, "rebind-timer": 2000, "subnet4": [{ "pools": [ { "pool": "192.0.2.1-192.0.2.200" } ], "subnet": "192.0.2.0/24" }], # Now loggers are inside the DHCPv4 object. "loggers": [{ "name": "*", "severity": "DEBUG" }] } # The whole configuration structure ends here. } More examples are available in the installed ``share/doc/kea/examples`` directory. .. note:: As of Kea 1.6.0, the "Logging" element was removed and its contents (the "loggers" object) moved inside the configuration objects (maps) for the respective Kea modules. For example, the "Dhcp4" map contains the "loggers" object, specifying logging configuration for the DHCPv4 server. Support for the top-level "Logging" object was removed in Kea 1.7.10. The specification for supporting several elements (e.g. "Dhcp4", "Dhcp6") in one file was removed in Kea 1.7.10, so each component now requires a separate configuration file. To avoid repetition of mostly similar structures, examples in the rest of this guide will showcase only the subset of parameters appropriate for a given context. For example, when discussing the IPv6 subnets configuration in DHCPv6, only subnet6 parameters will be mentioned. It is implied that the remaining elements (the global map that holds Dhcp6) are present, but they are omitted for clarity. Usually, locations where extra parameters may appear are denoted by an ellipsis (...). .. _user-context: Comments and User Context ------------------------- Shell, C, or C++ style comments are all permitted in the JSON configuration file if the file is used locally. This is convenient and works in simple cases where the configuration is kept statically using a local file. However, since comments are not part of JSON syntax, most JSON tools detect them as errors. Another problem with them is that once Kea loads its configuration, the shell, C, and C++ style comments are ignored. If commands such as ``config-get`` or ``config-write`` are used, those comments are lost. An example of such comments was presented in the previous section. Historically, to address the problem, Kea code allowed the use of `comment` strings as valid JSON entities. This had the benefit of being retained through various operations (such as ``config-get``), or allowing processing by JSON tools. An example JSON comment looks like this: :: "Dhcp4": { "subnet4": [{ "subnet": "192.0.2.0/24", "pools": [{ "pool": "192.0.2.10 - 192.0.2.20" }], "comment": "second floor" }] } However, the facts that the comment could only be a single line, and that it was not possible to add any other information in a more structured form, were frustrating. One specific example was a request to add floor levels and building numbers to subnets. This was one of the reasons why the concept of user context was introduced. It allows adding an arbitrary JSON structure to most Kea configuration structures. This has a number of benefits compared to earlier approaches. First, it is fully compatible with JSON tools and Kea commands. Second, it allows storing simple comment strings, but it can also store much more complex data, such as multiple lines (as a string array), extra typed data (such as floor numbers being actual numbers), and more. Third, the data is exposed to hooks, so it is possible to develop third-party hooks that take advantage of that extra information. An example user context looks like this: :: "Dhcp4": { "subnet4": [{ "subnet": "192.0.2.0/24", "pools": [{ "pool": "192.0.2.10 - 192.0.2.20" }], "user-context": { "comment": "second floor", "floor": 2 } }] } User contexts can store an arbitrary data file as long as it has valid JSON syntax and its top-level element is a map (i.e. the data must be enclosed in curly brackets). However, some hook libraries may expect specific formatting; please consult the specific hook library documentation for details. In a sense the user-context mechanism has superseded the JSON comment capabilities; ISC encourages administrators to use user-context instead of the older mechanisms. To promote this way of storing comments, Kea compared converts JSON comments to user-context on the fly. However, if the configuration uses the old JSON comment, the ``config-get`` command returns a slightly modified configuration. It is not uncommon for a call for ``config-set`` followed by a ``config-get`` to receive a slightly different structure. The best way to avoid this problem is simply to abandon JSON comments and use user-context. For a discussion about user-context used in hooks, see :ref:`user-context-hooks`. Simplified Notation ------------------- It is sometimes convenient to refer to a specific element in the configuration hierarchy. Each hierarchy level is separated by a slash. If there is an array, a specific instance within that array is referenced by a number in square brackets (with numbering starting at zero). For example, in the above configuration the valid-lifetime in the Dhcp4 component can be referred to as Dhcp4/valid-lifetime, and the pool in the first subnet defined in the DHCPv4 configuration as Dhcp4/subnet4[0]/pool. .. include:: config-backend.rst Configuration Files Inclusion ----------------------------- The parser provides the ability to include files. The syntax was chosen to look similar to how Apache includes PHP scripts in HTML code. This particular syntax was chosen to emphasize that the include directive is an additional feature and not a part of JSON syntax. The inclusion is implemented as a stack of files. You can use the include directive in nested includes. Up to ten nesting levels are supported. This arbitrarily chosen limit is protection against recursive inclusions. The include directive has the form: :: The *[PATH]* pattern should be replaced with an absolute path or a path relative to the current working directory at the time the Kea process was launched. To include one file from another, use the following syntax: .. code-block:: javascript { "Dhcp6": { "interfaces-config": { "interfaces": [ "*" ]}, "preferred-lifetime": 3000, "rebind-timer": 2000, "renew-timer": 1000, "valid-lifetime": 4000 } } where the content of "subnets.json" may be: :: "subnet4": [ { "id": 123, "subnet": "192.0.2.0/24" }, { "id": 234, "subnet": "192.0.3.0/24" }, { "id": 345, "subnet": "10.0.0.0/8" } ], kea-2.0.2/doc/sphinx/arm/agent.rst0000644000175000017500000003406114206773363013700 00000000000000.. _kea-ctrl-agent: ********************* The Kea Control Agent ********************* .. _agent-overview: Overview of the Kea Control Agent ================================= The Kea Control Agent (CA) is a daemon which exposes a RESTful control interface for managing Kea servers. The daemon can receive control commands over HTTP and either forward these commands to the respective Kea servers or handle these commands on its own. The determination whether the command should be handled by the CA or forwarded is made by checking the value of the `service` parameter, which may be included in the command from the controlling client. The details of the supported commands, as well as their structures, are provided in :ref:`ctrl-channel`. The CA can use hook libraries to provide support for additional commands or to program custom behavior of existing commands. Such hook libraries must implement callouts for the ``control_command_receive`` hook point. Details about creating new hook libraries and supported hook points can be found in the `Kea Developer's Guide `__. The CA processes received commands according to the following algorithm: - Pass command into any installed hooks (regardless of service value(s)). If the command is handled by a hook, return the response. - If the service specifies one or more services, forward the command to the specified services and return the accumulated responses. - If the service is not specified or is an empty list, handle the command if the CA supports it. .. _agent-configuration: Configuration ============= The following example demonstrates the basic CA configuration. :: { "Control-agent": { "http-host": "10.20.30.40", "http-port": 8000, "trust-anchor": "/path/to/the/ca-cert.pem", "cert-file": "/path/to/the/agent-cert.pem", "key-file": "/path/to/the/agent-key.pem", "cert-required": true, "authentication": { "type": "basic", "realm": "kea-control-agent", "clients": [ { "user": "admin", "password": "1234" } ] }, "control-sockets": { "dhcp4": { "comment": "main server", "socket-type": "unix", "socket-name": "/path/to/the/unix/socket-v4" }, "dhcp6": { "socket-type": "unix", "socket-name": "/path/to/the/unix/socket-v6", "user-context": { "version": 3 } }, "d2": { "socket-type": "unix", "socket-name": "/path/to/the/unix/socket-d2" }, }, "hooks-libraries": [ { "library": "/opt/local/control-agent-commands.so", "parameters": { "param1": "foo" } } ], "loggers": [ { "name": "kea-ctrl-agent", "severity": "INFO" } ] } } The ``http-host`` and ``http-port`` parameters specify an IP address and port to which HTTP service will be bound. In the example configuration provided above, the RESTful service will be available at the URL ``https://10.20.30.40:8000/``. If these parameters are not specified, the default URL is ``http://127.0.0.1:8000/``. When using Kea's HA hook library with multi-threading, make sure that the address:port combination used for CA is different from the HA peer URLs, which are strictly for internal HA traffic between the peers. User commands should still be sent via CA. The ``trust-anchor``, ``cert-file``, ```key-file``, and ``cert-required`` parameters specify the TLS setup for HTTP, i.e. HTTPS. If these parameters are not specified, HTTP is used. The TLS/HTTPS support in Kea is described in :ref:`tls`. As mentioned in :ref:`agent-overview`, the CA can forward received commands to the Kea servers for processing. For example, ``config-get`` is sent to retrieve the configuration of one of the Kea services. When the CA receives this command, including a ``service`` parameter indicating that the client wishes to retrieve the configuration of the DHCPv4 server, the CA forwards the command to that server and passes the received response back to the client. More about the ``service`` parameter and the general structure of commands can be found in :ref:`ctrl-channel`. The CA uses UNIX domain sockets to forward control commands and receive responses from other Kea services. The ``dhcp4``, ``dhcp6``, and ``d2`` maps specify the files to which UNIX domain sockets are bound. In the configuration above, the CA connects to the DHCPv4 server via ``/path/to/the/unix/socket-v4`` to forward the commands to it. Obviously, the DHCPv4 server must be configured to listen to connections via this same socket. In other words, the command-socket configuration for the DHCPv4 server and the CA (for that server) must match. Consult :ref:`dhcp4-ctrl-channel`, :ref:`dhcp6-ctrl-channel`, and :ref:`d2-ctrl-channel` to learn how the socket configuration is specified for the DHCPv4, DHCPv6, and D2 services. .. warning:: ``dhcp4-server``, ``dhcp6-server``, and ``d2-server`` were renamed to ``dhcp4``, ``dhcp6``, and ``d2`` respectively in Kea 1.2. If migrating from Kea 1.2, be sure to modify the CA configuration to use this new naming convention. User contexts can store arbitrary data as long as they are in valid JSON syntax and their top-level element is a map (i.e. the data must be enclosed in curly brackets). Some hook libraries may expect specific formatting; please consult the relevant hook library documentation for details. User contexts can be specified on either global scope, control socket, basic authentication, or loggers. One other useful feature is the ability to store comments or descriptions; the parser translates a "comment" entry into a user context with the entry, which allows a comment to be attached within the configuration itself. Basic HTTP authentication was added in Kea 1.9.0; it protects against unauthorized uses of the control agent by local users. For protection against remote attackers, HTTPS and reverse proxy of :ref:`agent-secure-connection` provide stronger security. The authentication is described in the ``authentication`` block with the mandatory ``type`` parameter, which selects the authentication. Currently only the basic HTTP authentication (type basic) is supported. The ``realm`` authentication parameter is used for error messages when the basic HTTP authentication is required but the client is not authorized. When the ``clients`` authentication list is configured and not empty, basic HTTP authentication is required. Each element of the list specifies a user ID and a password. The user ID is mandatory, must be not empty, and must not contain the colon (:) character. The password is optional; when it is not specified an empty password is used. .. note:: The basic HTTP authentication user ID and password are encoded in UTF-8, but the current Kea JSON syntax only supports the Latin-1 (i.e. 0x00..0xff) Unicode subset. Hook libraries can be loaded by the Control Agent in the same way as they are loaded by the DHCPv4 and DHCPv6 servers. The CA currently supports one hook point - ``control_command_receive`` - which makes it possible to delegate processing of some commands to the hook library. The ``hooks-libraries`` list contains the list of hook libraries that should be loaded by the CA, along with their configuration information specified with ``parameters``. Please consult :ref:`logging` for the details on how to configure logging. The CA's root logger's name is ``kea-ctrl-agent``, as given in the example above. .. _agent-secure-connection: Secure Connections (in Versions Prior to Kea 1.9.6) =================================================== The Control Agent does not natively support secure HTTP connections, like SSL or TLS, before Kea 1.9.6. To set up a secure connection, please use one of the available third-party HTTP servers and configure it to run as a reverse proxy to the Control Agent. Kea has been tested with two major HTTP server implementations working as a reverse proxy: Apache2 and nginx. Example configurations, including extensive comments, are provided in the ``doc/examples/https/`` directory. The reverse proxy forwards HTTP requests received over a secure connection to the Control Agent using unsecured HTTP. Typically, the reverse proxy and the Control Agent are running on the same machine, but it is possible to configure them to run on separate machines as well. In this case, security depends on the protection of the communications between the reverse proxy and the Control Agent. Apart from providing the encryption layer for the control channel, a reverse proxy server is also often used for authentication of the controlling clients. In this case, the client must present a valid certificate when it connects via reverse proxy. The proxy server authenticates the client by checking whether the presented certificate is signed by the certificate authority used by the server. To illustrate this, the following is a sample configuration for the nginx server running as a reverse proxy to the Kea Control Agent. The server enables authentication of the clients using certificates. :: # The server certificate and key can be generated as follows: # # openssl genrsa -des3 -out kea-proxy.key 4096 # openssl req -new -x509 -days 365 -key kea-proxy.key -out kea-proxy.crt # # The CA certificate and key can be generated as follows: # # openssl genrsa -des3 -out ca.key 4096 # openssl req -new -x509 -days 365 -key ca.key -out ca.crt # # # The client certificate needs to be generated and signed: # # openssl genrsa -des3 -out kea-client.key 4096 # openssl req -new -key kea-client.key -out kea-client.csr # openssl x509 -req -days 365 -in kea-client.csr -CA ca.crt \ # -CAkey ca.key -set_serial 01 -out kea-client.crt # # Note that the "common name" value used when generating the client # and the server certificates must differ from the value used # for the CA certificate. # # The client certificate must be deployed on the client system. # In order to test the proxy configuration with "curl", run a # command similar to the following: # # curl -k --key kea-client.key --cert kea-client.crt -X POST \ # -H Content-Type:application/json -d '{ "command": "list-commands" }' \ # https://kea.example.org/kea # # curl syntax for basic authentication is -u user:password # # # nginx configuration starts here. events { } http { # HTTPS server server { # Use default HTTPS port. listen 443 ssl; # Set server name. server_name kea.example.org; # Server certificate and key. ssl_certificate /path/to/kea-proxy.crt; ssl_certificate_key /path/to/kea-proxy.key; # Certificate Authority. Client certificates must be signed by the CA. ssl_client_certificate /path/to/ca.crt; # Enable verification of the client certificate. ssl_verify_client on; # For URLs such as https://kea.example.org/kea, forward the # requests to http://127.0.0.1:8000. location /kea { proxy_pass http://127.0.0.1:8000; } } } .. .. note:: Note that the configuration snippet provided above is for testing purposes only. It should be modified according to the security policies and best practices of the administrator's organization. When using an HTTP client without TLS support, such as ``kea-shell``, it is possible to use an HTTP/HTTPS translator such as ``stunnel`` in client mode. A sample configuration is provided in the ``doc/examples/https/shell/`` directory. Secure Connections (in Kea 1.9.6 and Newer) =========================================== Since Kea 1.9.6, the Control Agent natively supports secure HTTP connections using TLS. This allows protection against users from the node where the agent runs, something that a reverse proxy cannot provide. More about TLS/HTTPS support in Kea can be found in :ref:`tls`. TLS is configured using three string parameters, giving file names and a boolean parameter: - The ``trust-anchor`` specifies the Certification Authority file name or directory path. - The ``cert-file`` specifies the server certificate file name. - The ``key-file`` specifies the private key file name. The file must not be encrypted. - The ``cert-required`` specifies whether client certificates are required or optional. The default is to require them and to perform mutual authentication. The file format is PEM. Either all the string parameters are specified and HTTP over TLS aka HTTPS is used, or none is specified and plain HTTP is used. Configuring only one or two string parameters results in an error. .. note:: When client certificates are not required, only the server side is authenticated, i.e. the communication is encrypted with an unknown client. This protects only against passive attacks; active attacks, such as "Man in the Middle," are still possible. .. note:: No standard HTTP authentication scheme cryptographically binds its end entity with TLS. This means that the TLS client and server can be mutually authenticated, but there is no proof they are the same as for the HTTP authentication. Since Kea 1.9.6, the ``kea-shell`` tool supports TLS. .. _agent-launch: Starting the Control Agent ========================== The CA is started by running its binary and specifying the configuration file it should use. For example: .. code-block:: console $ ./kea-ctrl-agent -c /usr/local/etc/kea/kea-ctrl-agent.conf It can be started by keactrl as well (see :ref:`keactrl`). .. _agent-clients: Connecting to the Control Agent =============================== For an example of a tool that can take advantage of the RESTful API, see :ref:`kea-shell`. kea-2.0.2/doc/sphinx/arm/hooks.rst0000644000175000017500000043742014206773363013733 00000000000000.. _hooks-libraries: *************** Hooks Libraries *************** .. _hooks-libraries-introduction: Introduction ============ Kea is both flexible and customizable, via the use of "hooks." This feature lets Kea load one or more dynamically linked libraries (known as "hooks libraries") and, at various points in its processing ("hook points"), call functions in them. Those functions perform whatever custom processing is required. The hooks concept allows the core Kea code to remain reasonably small by moving features to external libraries that some, but not all, users find useful. Those with no need for specific functions can simply choose not to load the libraries. Hooks libraries are loaded by individual Kea processes, not by Kea as a whole. This means, among other things, that it is possible to associate one set of libraries with the DHCP4 server and a different set with the DHCP6 server. Another point to note is that it is possible for a process to load multiple libraries. When processing reaches a hook point, Kea calls the hooks library functions attached to it. If multiple libraries have attached a function to a given hook point, Kea calls all of them, in the order in which the libraries are specified in the configuration file. The order may be important; consult the documentation of the libraries for specifics. When a Kea process unloads a library, it expects the ``dlclose`` function removes all library symbols and removes the library code from address space on the last reference. This behavior is not required by the POSIX standard and at least the musl library used by default by Alpine Linux implements the ``dlclose`` function as a no operation. On such systems a library is loaded forever in a process, for instance it is not possible to replace a library binary by another version using configuration change or reload: the process must be stopped and relaunched. The next section describes how to configure hooks libraries. Users who are interested in writing their own hooks library can find information in the `Hooks Developer's Guide section of the Kea Developer's Guide `__. Note that some libraries are available under different licenses. Please also note that some libraries may require additional dependencies and/or compilation switches to be enabled, e.g. the RADIUS library introduced in Kea 1.4 requires the FreeRadius-client library to be present. If --with-freeradius option is not specified, the RADIUS library will not be built. Installing Hook Packages ======================== .. note:: For more details about installing the Kea Premium Hooks package, please read `this Knowledge Base article `__. Some hook packages are included in the base Kea sources. There is no need to do anything special to compile or install them, as they are covered by the usual building and installation procedures. Please refer to :ref:`installation` for a general overview of the installation process. ISC provides several additional premium hooks in the form of packages, which follow a similar installation procedure but with several additional steps. For our users' convenience, the premium hooks installation procedure is described in this section. 1. Download the package; detailed instructions are provided separately on how to get it. The package will be a file with a name similar to kea-premium-|release|.tar.gz. (The name may vary depending on the package purchased.) 2. Administrators who still have the sources for the corresponding version of the open-source Kea package still on their system from the initial Kea installation should skip this step. Otherwise, extract the Kea source from the original tarball that was downloaded. For example, with a download of Kea |release|., there should be a tarball called kea-|release|.tar.gz on the system. Unpack this tarball: .. parsed-literal:: $ tar -zxvf kea-|release|.tar.gz This will unpack the tarball into the kea-|release| subdirectory of the current working directory. 3. Unpack the Kea premium tarball into the directory into which Kea was unpacked. Once Kea |release| has been unpacked into a kea-|release| subdirectory and the Kea premium tarball is in the current directory, the following steps will unpack the premium tarball into the correct location: .. parsed-literal:: $ cd kea-|release| $ tar -xvf ../kea-premium-|release|.tar.gz Note that unpacking the Kea premium package will put the files into a directory named "premium". Regardless of the name of the package, the directory will always be called "premium", although its contents will vary depending on the premium package. 4. Run ``autoreconf`` tools. This step is necessary to update Kea's build script to include the additional directory. If this tool is not already available on the system, install the ``automake`` and ``autoconf`` tools. To generate the configure script, please use: :: $ autoreconf -i 5. Rerun configure, using the same configure options that were used when originally building Kea. It is possible to verify that configure has detected the premium package by inspecting the summary printed when it exits. The first section of the output should look something like this: .. parsed-literal:: Package: Name: kea Version: |release| Extended version: |release| (tarball) OS Family: Linux Using GNU sed: yes Premium package: yes Included Hooks: forensic_log flex_id host_cmds subnet_cmds radius host_cache class_cmds cb_cmds lease_query The last line indicates which specific hooks were detected. Note that some hooks may require their own dedicated switches, e.g. the RADIUS hook requires extra switches for FreeRADIUS. Please consult later sections of this chapter for details. 6. Rebuild Kea. :: $ make If the machine has multiple CPU cores, an interesting option to consider here is using the argument -j X, where X is the number of available cores. 7. Install Kea sources along with the hooks: :: $ sudo make install Note that as part of the installation procedure, the install script will eventually venture into the premium/ directory and will install additional hook libraries and associated files. The installation location of the hooks libraries depends on whether the --prefix parameter was specified in the configure script. If not, the default location will be /usr/local/lib/kea/hooks. The proper installation of the libraries can be verified with this command: :: $ ls -l /usr/local/lib/kea/hooks/*.so /usr/local/lib/kea/hooks/libdhcp_class_cmds.so /usr/local/lib/kea/hooks/libdhcp_flex_id.so /usr/local/lib/kea/hooks/libdhcp_flex_option.so /usr/local/lib/kea/hooks/libdhcp_host_cmds.so /usr/local/lib/kea/hooks/libdhcp_lease_cmds.so /usr/local/lib/kea/hooks/libdhcp_legal_log.so /usr/local/lib/kea/hooks/libdhcp_subnet_cmds.so The exact list returned will depend on the packages installed. If the directory was specified via --prefix, the hooks libraries will be located in {prefix directory}/lib/kea/hooks. Configuring Hooks Libraries =========================== The hooks libraries for a given process are configured using the ``hooks-libraries`` keyword in the configuration for that process. (Note that the word "hooks" is plural.) The value of the keyword is an array of map structures, with each structure corresponding to a hooks library. For example, to set up two hooks libraries for the DHCPv4 server, the configuration would be: :: "Dhcp4": { : "hooks-libraries": [ { "library": "/opt/charging.so" }, { "library": "/opt/local/notification.so", "parameters": { "mail": "spam@example.com", "floor": 13, "debug": false, "users": [ "alice", "bob", "charlie" ], "languages": { "french": "bonjour", "klingon": "yl'el" } } } ] : } .. .. note:: This syntax is effective as of Kea 1.1.0, to facilitate the specification of library-specific parameters. Libraries should allow a parameter entry for comments, as is the case with many configuration scopes. .. .. note:: In all versions of Kea since 1.1.0, libraries are reloaded even if their lists have not changed, because the parameters specified for the library (or the files those parameters point to) may have changed. Libraries may have additional parameters that are not mandatory, in the sense that there may be libraries that do not require them. However, for a specific library there is often a specific requirement to specify a certain set of parameters. Please consult the documentation for each individual library for details. In the example above, the first library has no parameters. The second library has five parameters: specifying mail (string parameter), floor (integer parameter), debug (boolean parameter), lists (list of strings), and maps (containing strings). Nested parameters can be used if the library supports it. This topic is explained in detail in the `Hooks Developer's Guide section of the Kea Developer's Guide `__. Notes: - The full path to each library should be given. - As noted above, the order in which the hooks are called may be important; consult the documentation for each library for specifics. - An empty list has the same effect as omitting the ``hooks-libraries`` configuration element altogether. .. note:: There is one case where this is not true: if Kea is running with a configuration that contains a ``hooks-libraries`` item, and that item is removed and the configuration reloaded, the removal will be ignored and the libraries remain loaded. As a workaround, instead of removing the ``hooks-libraries`` item, change it to an empty list. This will be fixed in a future version of Kea. At the present time, only the kea-dhcp4 and kea-dhcp6 processes support hooks libraries. Available Hooks Libraries ========================= As described above, the hooks functionality provides a way to customize a Kea server without modifying the core code. ISC has chosen to take advantage of this feature to provide functions that may only be useful to a subset of Kea users. To this end, ISC has created some hooks libraries, discussed in the following sections. .. note:: Some of these libraries are available with the base code, while others will be shared with organizations supporting development of Kea. Users who would like to get access to those premium libraries should consider purchasing a support contract from ISC. This includes professional support, advance security notifications, input into ISC's roadmap planning, and many other benefits, while helping make Kea sustainable in the long term. The following table provides a list of libraries currently available from ISC. It is important to pay attention to which libraries may be loaded by which Kea processes. It is a common mistake to configure the ``kea-ctrl-agent`` process to load libraries that should, in fact, be loaded by the ``kea-dhcp4`` or ``kea-dhcp6`` processes. If a library from ISC does not work as expected, please make sure that it has been loaded by the correct process per the table below. .. warning:: While the Kea Control Agent includes the "hooks" functionality, (i.e. hooks libraries can be loaded by this process), none of ISC's current hooks libraries should be loaded by the Control Agent. .. tabularcolumns:: |p{0.1\linewidth}|p{0.1\linewidth}|p{0.8\linewidth}| .. table:: List of Available Hooks Libraries :class: longtable :widths: 10 10 80 +-----------------+---------------+------------------------------------------------------------+ | Name | Availability | Description | +=================+===============+============================================================+ | User Check | Kea sources |Reads known users list from a file. Unknown users will be | | | (since 0.8) |assigned a lease from the last subnet defined in the | | | |configuration file, e.g. to redirect them to a captive | | | |portal. This demonstrates how an external source of | | | |information can be used to influence the Kea allocation | | | |engine. This hook is part of the Kea source code and is | | | |available in the src/hooks/dhcp/user_chk directory. | +-----------------+---------------+------------------------------------------------------------+ | Forensic | Support |This library provides hooks that record a detailed log of | | Logging | customers |lease assignments and renewals into a set of log files. In | | | (since 1.1) |many legal jurisdictions companies, especially ISPs, must | | | |record information about the addresses they have leased to | | | |DHCP clients. This library is designed to help with that | | | |requirement. If the information that it records is | | | |sufficient it may be used directly. If your jurisdiction | | | |requires that you save a different set of information, you | | | |may use it as a template or example and create your own | | | |custom logging hooks. In Kea 1.9.8 additional parameters | | | |have been added to give users more flexibility regarding | | | |what information should be logged. | +-----------------+---------------+------------------------------------------------------------+ | Flexible | Support |Kea software provides a way to handle host reservations that| | Identifier | customers |include addresses, prefixes, options, client classes and | | | (since 1.2) |other features. The reservation can be based on hardware | | | |address, DUID, circuit-id or client-id in DHCPv4 and using | | | |hardware address or DUID in DHCPv6. However, there are | | | |sometimes scenarios where the reservation is more complex, | | | |e.g. uses other options that mentioned above, uses part of | | | |specific options or perhaps even a combination of several | | | |options and fields to uniquely identify a client. Those | | | |scenarios are addressed by the Flexible Identifiers hook | | | |application. It allows defining an expression, similar to | | | |the one used in client classification, | | | |e.g. substring(relay6[0].option[37],0,6). Each incoming | | | |packet is evaluated against that expression and its value is| | | |then searched in the reservations database. | +-----------------+---------------+------------------------------------------------------------+ | Flexible | Kea sources |This library provides hooks that compute option values | | Option | (since 1.7.1) |instead of static configured values. An expression is | | | |evaluated on the query packet. Defined add, supersede and | | | |remove actions are applied on the response packet before | | | |it is sent using the evaluation result. | +-----------------+---------------+------------------------------------------------------------+ | Host Commands | Support |Kea provides a way to store host reservations in a | | | customers |database. In many larger deployments it is useful to be able| | | (since 1.2) |to manage that information while the server is running. This| | | |library provides management commands for adding, querying | | | |and deleting host reservations in a safe way without | | | |restarting the server. In particular, it validates the | | | |parameters, so an attempt to insert incorrect data, e.g. add| | | |a host with conflicting identifier in the same subnet will | | | |be rejected. Those commands are exposed via command channel | | | |(JSON over unix sockets) and Control Agent (JSON over | | | |RESTful interface). Additional commands and capabilities | | | |related to host reservations will be added in the future. | +-----------------+---------------+------------------------------------------------------------+ | Subnet Commands | Support |In deployments in which subnet configuration needs to be | | | customers |frequently updated, it is a hard requirement that such | | | (since 1.3) |updates be performed without the need for a full DHCP server| | | |reconfiguration or restart. This hooks library allows for | | | |incremental changes to the subnet configuration such as: | | | |adding a subnet, removing a subnet. It also allows for | | | |listing all available subnets and fetching detailed | | | |information about a selected subnet. The commands exposed by| | | |this library do not affect other subnets or configuration | | | |parameters currently used by the server. | +-----------------+---------------+------------------------------------------------------------+ | Lease Commands | Kea sources |The lease commands hook library offers a number of new | | | (since 1.3) |commands used to manage leases. Kea provides a way to store | | | |lease information in various backends: memfile, MySQL, | | | |PostgreSQL and Cassandra. This library provides a unified | | | |interface that can manipulate leases in an unified, safe | | | |way. In particular, it allows: manipulate leases in memfile | | | |while Kea is running, sanity check changes, check lease | | | |existence and remove all leases belonging to specific | | | |subnet. It can also catch more obscure errors, like adding a| | | |lease with subnet-id that does not exist in the | | | |configuration or configuring a lease to use an address that | | | |is outside of the subnet to which it is supposed to belong. | | | |It provides a way to manage user contexts associated with | | | |leases. | +-----------------+---------------+------------------------------------------------------------+ | High | Kea sources |Minimizing a risk of DHCP service unavailability is achieved| | Availability | (since 1.4) |by setting up a pair of the DHCP servers in a network. Two | | | |modes of operation are supported. The first one is called | | | |load balancing and is sometimes referred to as | | | |active-active. Each server can handle selected groups of | | | |clients in this network or all clients, if it detects that | | | |its partner has become unavailable. It is also possible to | | | |designate one server to serve all DHCP clients, and leave | | | |another server as "standby". This mode is called hot standby| | | |and is sometimes referred to as active-passive. This | | | |server will activate its DHCP function when it detects that | | | |its partner is not available. Such cooperation between the | | | |DHCP servers requires that these servers constantly | | | |communicate with each other to send updates about allocated | | | |leases and to periodically test whether their partners are | | | |still operational. The hook library also provides an ability| | | |to send lease updates to external backup servers, making it | | | |much easier to have a replacement that is almost up to | | | |date. The "libdhcp_ha" library provides such functionality | | | |for Kea DHCP servers. | +-----------------+---------------+------------------------------------------------------------+ | Statistics | Kea sources |The Statistics Commands library provides additional | | Commands | (since 1.4) |commands for retrieving accurate DHCP lease statistics for | | | |Kea DHCP servers that share the same lease database. This | | | |setup is common in deployments where DHCP service redundancy| | | |is required and a shared lease database is used to avoid | | | |lease data replication between the DHCP servers. A feature | | | |was introduced in Kea 1.4.0 that allows tracking lease | | | |allocations within the lease database, thus making the | | | |statistics accessible to all connected DHCP servers. The | | | |Statistics Commands hooks library utilizes this feature and | | | |returns lease statistics for all subnets respectively. | +-----------------+---------------+------------------------------------------------------------+ | RADIUS | Support |The RADIUS Hook library allows Kea to interact with the | | | customers |RADIUS servers using access and accounting mechanisms. The | | | (since 1.4) |access mechanism may be used for access control, assigning | | | |specific IPv4 or IPv6 addresses reserved by RADIUS, | | | |dynamically assigning addresses from designated pools chosen| | | |by RADIUS or rejecting the client's messages altogether. The| | | |accounting mechanism allows a RADIUS server to keep track of| | | |device activity over time. | +-----------------+---------------+------------------------------------------------------------+ | Host Cache | Support |Some of the database backends, such as RADIUS, are | | | customers |considered slow and may take a long time to respond. Since | | | (since 1.4) |Kea in general is synchronous, the backend performance | | | |directly affects the DHCP performance. To minimize the | | | |impact and improve performance, the Host Cache library | | | |provides a way to cache responses from other hosts. This | | | |includes negative caching, i.e. the ability to remember that| | | |there is no client information in the database. | +-----------------+---------------+------------------------------------------------------------+ | Class Commands | Support |This Class Cmds hooks library allows for adding, updating, | | | customers |deleting and fetching configured DHCP client classes without| | | (since 1.5) |the need to restart the DHCP server. | +-----------------+---------------+------------------------------------------------------------+ | MySQL | Kea sources |The MySQL CB hooks library is an implementation of the Kea | | Configuration | (since 1.6) |Configuration Backend for MySQL. It uses a MySQL database as| | Backend | |a repository for the Kea configuration information. The Kea | | | |servers use this library to fetch their configurations. | +-----------------+---------------+------------------------------------------------------------+ | Configuration | Support |The Configuration Backend Commands (CB Commands) hooks | | Backend | customers |library implements a collection of commands to manage the | | Commands | (since 1.6) |configuration information of the Kea servers in the | | | |database. This library may only be used in conjunction with | | | |one of the supported configuration backend implementations. | +-----------------+---------------+------------------------------------------------------------+ | BOOTP | Kea sources |The BOOTP hooks library adds BOOTP support, as defined in | | | (since 1.7.3) |RFC 1497. It recognizes received BOOTP requests: | | | |they are translated into DHCPREQUEST packets, put into the | | | |BOOTP client class and get infinite lifetime leases. | +-----------------+---------------+------------------------------------------------------------+ | Leasequery | Support |The Leasequery hooks library adds support for DHCPv4 | | | customers |Leasequery as described in RFC 4388; and for DHCPv6 | | | (DHCPv4 since |Leasequery as described in RFC 5007. | | | 1.7.8, DHCPv6 | | | | since 1.7.9) | | +-----------------+---------------+------------------------------------------------------------+ | Run Script | Kea sources |The Run Script hooks library adds support to run external | | | (since 1.9.5) |scripts for specific packet processing hook points. There | | | |are several exported environment variables available for | | | |the script. | +-----------------+---------------+------------------------------------------------------------+ | GSS-TSIG | ISC support |This hook library adds support to the Kea D2 server | | | customers |(kea-dhcp-ddns) for using GSS-TSIG to sign DNS updates. | | | (since 2.0.1) | | +-----------------+---------------+------------------------------------------------------------+ ISC hopes to see more hooks libraries become available as time progresses, developed both internally and externally. Since this list may evolve dynamically, it is maintained on a wiki page, available at this link: https://gitlab.isc.org/isc-projects/kea/wikis/Hooks-available. Developers or others who are aware of any hooks libraries not listed there are asked to please send a note to the kea-users or kea-dev mailing lists for updating. The libraries developed by ISC are described in detail in the following sections. user_chk: Checking User Access ============================== The user_chk library is the first hooks library published by ISC. It serves several purposes: - To assign "new" or "unregistered" users to a restricted subnet, while "known" or "registered" users are assigned to unrestricted subnets. - To allow DHCP response options or vendor option values to be customized based on user identity. - To provide a real-time record of user registration activity, which can be sampled by an external consumer. - To serve as a demonstration of various capabilities possible using the hooks interface. Once loaded, the library allows the separation of incoming requests into known and unknown clients. For known clients, packets are processed as usual, although it is possible to override the sending of certain options on a per-host basis. Clients that are not on the known hosts list will be treated as unknown and will be assigned to the last subnet defined in the configuration file. As an example of a use case, this behavior may be implemented to put unknown users into a separate subnet that leads to a "walled garden," where they can only access a registration portal. Once they fill in necessary data, their details are added to the known clients file and they get a proper address after their device is restarted. .. note:: This library was developed several years before the host reservation mechanism became available. Host reservation is much more powerful and flexible, but the user_chk capability to consult an external source of information about clients and alter Kea's behavior remains useful and of educational value. The library reads the /tmp/user_chk_registry.txt file while being loaded and each time an incoming packet is processed. Each line of the file is expected to contain a self-contained JSON snippet which must have the following two entries: - ``type`` - whose value is "HW_ADDR" for IPv4 users or "DUID" for IPv6 users. - ``id`` - whose value is either the hardware address or the DUID from the request formatted as a string of hex digits, with or without ":" delimiters. and may have zero or more of the following entries: - ``bootfile`` - whose value is the pathname of the desired file. - ``tftp_server`` - whose value is the hostname or IP address of the desired server. A sample user registry file is shown below: :: { "type" : "HW_ADDR", "id" : "0c:0e:0a:01:ff:04", "bootfile" : "/tmp/v4bootfile" } { "type" : "HW_ADDR", "id" : "0c:0e:0a:01:ff:06", "tftp_server" : "tftp.v4.example.com" } { "type" : "DUID", "id" : "00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:04", "bootfile" : "/tmp/v6bootfile" } { "type" : "DUID", "id" : "00:01:00:01:19:ef:e6:3b:00:0c:01:02:03:06", "tftp_server" : "tftp.v6.example.com" } As with any other hooks libraries provided by ISC, internals of the user_chk code are well-documented. Users may refer to the `user_chk library section of the Kea Developer's Guide `__ for information on how the code works internally. That, together with the `Hooks Framework section of the Kea Developer's Guide `__ should give users some pointers on how to extend this library and perhaps even write one from scratch. legal_log: Forensic Logging Hooks ================================= This section describes the forensic log hooks library. This library provides hooks that record a detailed log of assignments, renewals, releases and other lease events into a set of log files. Currently this library is only available to ISC customers with a paid support contract. .. note:: This library may only be loaded by the ``kea-dhcp4`` or ``kea-dhcp6`` process. In many legal jurisdictions, companies, especially ISPs, must record information about the addresses they have leased to DHCP clients. This library is designed to help with that requirement. If the information that it records is sufficient, it may be used directly. If a jurisdiction requires that a different set of information be saved, users may use the custom formatting capability to extract information from the inbound request packet, or from the outbound response packet. Use with caution as this might affect server performance. The custom format can not be used for control channel commands. Alternatively, this library may be used as a template or an example for the user's own custom logging hook. The logging is done as a set of hooks to allow it to be customized to any particular need. Modifying a hooks library is easier and safer than updating the core code. In addition by using the hooks features, those users who do not need to log this information can leave it out and avoid any performance penalties. Log File Naming ~~~~~~~~~~~~~~~ The names for the log files have the following form: Legal file names, if using ``day``, ``month`` or ``year`` as time unit: :: path/base-name.CCYYMMDD.txt where ``CC`` represents century, ``YY`` represents current year, ``MM`` represents current month and ``DD`` represents current day. Legal file names, if using ``second`` as time unit: :: path/base-name.TXXXXXXXXXXXXXXXXXXXX.txt where ``XXXXXXXXXXXXXXXXXXXX`` represents time in seconds since epoch. When using ``second`` as the time unit, the file will be rotated when the ``count`` number of seconds pass. In contrast, when using ``day``, ``month`` or ``year`` as time unit, the file will be rotated whenever the ``count`` th day, month or year starts respectively. The ``"path"`` and ``"base-name"`` are supplied in the configuration as described below; see :ref:`forensic-log-configuration`. .. note:: When running Kea servers for both DHCPv4 and DHCPv6, the log names must be distinct. See the examples in :ref:`forensic-log-configuration`. .. _forensic-log-configuration: Configuring the Forensic Log Hooks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To use this functionality, the hook library must be included in the configuration of the desired DHCP server modules. The legal_log library is able to save logs to a text file or to a database (created using ``kea-admin`` see :ref:`mysql-database-create`, :ref:`pgsql-database-create`). The library is installed alongside the Kea libraries in ``[kea-install-dir]/var/lib/kea`` where ``kea-install-dir`` is determined by the "--prefix" option of the configure script. It defaults to ``/usr/local``. Assuming the default value, configuring kea-dhcp4 to load the legal_log library could be done with the following kea-dhcp4 configuration: .. code-block:: json { "Dhcp4": { "hooks-libraries": [ { "library": "/usr/local/lib/kea/hooks/libdhcp_legal_log.so", "parameters": { "path": "/var/lib/kea/log", "base-name": "kea-forensic4" } } ] } } For kea-dhcp6, the configuration is: .. code-block:: json { "Dhcp6": { "hooks-libraries": [ { "library": "/usr/local/lib/kea/hooks/libdhcp_legal_log.so", "parameters": { "path": "/var/lib/kea/log", "base-name": "kea-forensic6" } } ] } } The hook library parameters for the text file configuration are: - ``path`` - the directory in which the forensic file(s) will be written. The default value is ``[prefix]/var/lib/kea``. The directory must exist. - ``base-name`` - an arbitrary value which is used in conjunction with the current system date to form the current forensic file name. It defaults to ``kea-legal``. - ``time-unit`` - configures the time unit used to rotate the log file. Valid values are ``second``, ``day``, ``month`` or ``year``. It defaults to ``day``. - ``count`` - configures the number of time units that need to pass until the log file is rotated. It can be any positive number, or 0 which disables log rotate. It defaults to 1. If log rotate is disabled, a new file will be created when the library is loaded and the new file name is different that any previous file name. Additional actions can be performed just before closing the old file and after opening the new file. These actions must point to an external executable or script and are configured by setting: - ``prerotate`` - external executable or script called with the name of the file that will be closed. Kea will not wait for the process to finish. - ``postrotate`` - external executable or script called with the name of the file that had been opened. Kea will not wait for the process to finish. Custom formatting can be enabled for logging information that can be extracted either from the client's request packet or from the server's response packet. Use with caution as this might affect server performance. The custom format can not be used for control channel commands. Two parameters can be used towards this goal, either together or separately: - ``request-parser-format`` - evaluated parsed expression used to extract and log data from the incoming packet - ``response-parser-format`` - evaluated parsed expression used to extract and log data from the server response packet See :ref:`classification-using-expressions` for a list of expressions. If any of ``request-parser-format`` or ``response-parser-format`` is configured, the default logging format is not used. If both of them are configured, the resulting log message is constructed by concatenating the data extracted from the request and the data extracted from the response. Some data might be available in the request or in the response only and some data might differ in the request packet from the one in the response packet. The lease client context can only be printed using the default format, as this information is not directly stored in the request packet or in the response packet. Additional parameters for the database connection can be specified, e.g: .. code-block:: json { "Dhcp6": { "hooks-libraries": [ { "library": "/usr/local/lib/kea/hooks/libdhcp_legal_log.so", "parameters": { "name": "database-name", "password": "passwd", "type": "mysql", "user": "user-name" } } ] } } For more specific information about database related parameters please refer to :ref:`database-configuration6` and :ref:`database-configuration4`. If it is desired to restrict forensic logging to certain subnets, the "legal-logging" boolean parameter can be specified within a user context of these subnets. For example: .. code-block:: json { "Dhcp4": { "subnet4": [ { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ], "user-context": { "legal-logging": false } } ] } } This configuration disables legal logging for the subnet "192.0.2.0/24". If the "legal-logging" parameter is not specified, it defaults to 'true', which enables legal logging for the subnet. The following example demonstrates how to selectively disable legal logging for an IPv6 subnet: .. code-block:: json { "Dhcp6": { "subnet6": [ { "subnet": "2001:db8:1::/64", "pools": [ { "pool": "2001:db8:1::1-2001:db8:1::ffff" } ], "user-context": { "legal-logging": false } } ] } } See :ref:`dhcp4-user-contexts` and :ref:`dhcp6-user-contexts` to learn more about user contexts in Kea configuration. DHCPv4 Log Entries ~~~~~~~~~~~~~~~~~~ For DHCPv4, the library creates entries based on DHCPREQUEST, DHCPDECLINE, DHCPRELEASE messages et.al. and their responses. The resulting packets and leases are taken into account, intercepted through the following hook points: * pkt4_receive * leases4_committed * pkt4_send * lease4_release * lease4_decline An entry is a single string with no embedded end-of-line markers and a prepended timestamp, and has the following sections: :: timestamp address duration device-id {client-info} {relay-info} {user-context} Where: - timestamp - the current date and time the log entry was written in "%Y-%m-%d %H:%M:%S %Z" strftime format ("%Z" is the time zone name). - address - the leased IPv4 address given out and whether it was assigned, renewed or released. - duration - the lease lifetime expressed in days (if present), hours, minutes, and seconds. A lease lifetime of 0xFFFFFFFF will be denoted with the text "infinite duration". This information is not given when the lease is released. - device-id - the client's hardware address shown as numerical type and hex digit string. - client-info - the DHCP client id option (61) if present, shown as a hex string. When its content is printable it is displayed. - relay-info - for relayed packets the giaddr and the RAI circuit-id, remote-id, and subscriber-id options (option 82 sub options: 1, 2 and 6) if present. The circuit id and remote id are presented as hex strings. When their content is printable it is displayed. - user-context - the optional user context associated with the lease. For instance (line breaks added for readability; they will not be present in the log file): :: 2018-01-06 01:02:03 CET Address: 192.2.1.100 has been renewed for 1 hrs 52 min 15 secs to a device with hardware address: hwtype=1 08:00:2b:02:3f:4e, client-id: 17:34:e2:ff:09:92:54 connected via relay at address: 192.2.16.33, identified by circuit-id: 68:6f:77:64:79 (howdy) and remote-id: 87:f6:79:77:ef or for a release: :: 2018-01-06 01:02:03 CET Address: 192.2.1.100 has been released from a device with hardware address: hwtype=1 08:00:2b:02:3f:4e, client-id: 17:34:e2:ff:09:92:54 connected via relay at address: 192.2.16.33, identified by circuit-id: 68:6f:77:64:79 (howdy) and remote-id: 87:f6:79:77:ef In addition to logging lease activity driven by DHCPv4 client traffic, the hooks library also logs entries for the following lease management control channel commands: lease4-add, lease4-update, and lease4-del. These cannot have custom formatting. Each entry is a single string with no embedded end-of-line markers, and it will typically have the following form: ``lease4-add:`` :: *timestamp* Administrator added a lease of address: *address* to a device with hardware address: *device-id* Depending on the arguments of the add command, it may also include the client-id and duration. Example: :: 2018-01-06 01:02:03 CET Administrator added a lease of address: 192.0.2.202 to a device with hardware address: 1a:1b:1c:1d:1e:1f for 1 days 0 hrs 0 mins 0 secs ``lease4-update:`` :: *timestamp* Administrator updated information on the lease of address: *address* to a device with hardware address: *device-id* Depending on the arguments of the update command, it may also include the client-id and lease duration. Example: :: 2018-01-06 01:02:03 CET Administrator updated information on the lease of address: 192.0.2.202 to a device with hardware address: 1a:1b:1c:1d:1e:1f, client-id: 1234567890 ``lease4-del:`` deletes have two forms, one by address and one by identifier and identifier type: :: *timestamp* Administrator deleted the lease for address: *address* or :: *timestamp* Administrator deleted a lease for a device identified by: *identifier-type* of *identifier* Currently only a type of @b hw-address (hardware address) is supported. Examples: :: 2018-01-06 01:02:03 CET Administrator deleted the lease for address: 192.0.2.202 2018-01-06 01:02:12 CET Administrator deleted a lease for a device identified by: hw-address of 1a:1b:1c:1d:1e:1f The ``request-parser-format`` and ``response-parser-format`` can be used to extract and log data from the incoming packet and server response packet respectively. The configured value is an evaluated parsed expression returning a string. A list of tokens is described in the server classification process. Use with caution as this might affect server performance. If any of them is configured, the default logging format is not used. If both of them are configured, the resulting log message is constructed by concatenating the logged data extracted from the request and the logged data extracted from the response. Some data might be available in the request or in the response only and some data might differ in the incoming packet from the one in the response packet. Examples: .. code-block:: json { "request-parser-format": "ifelse(pkt4.msgtype == 4 or pkt4.msgtype == 7, 'Address: ' + ifelse(option[50].exists, addrtotext(option[50].hex), addrtotext(pkt4.ciaddr)) + ' has been released from a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + ifelse(option[61].exists, ', client-id: ' + hexstring(option[61].hex, ':'), '') + ifelse(pkt4.giaddr == 0.0.0.0, '', ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + ifelse(option[82].option[1].exists, ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), '') + ifelse(option[82].option[2].exists, ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), '') + ifelse(option[82].option[6].exists, ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), '')), '')", "response-parser-format": "ifelse(pkt4.msgtype == 5, 'Address: ' + addrtotext(pkt4.yiaddr) + ' has been assigned for ' + uint32totext(option[51].hex) + ' seconds to a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + ifelse(option[61].exists, ', client-id: ' + hexstring(option[61].hex, ':'), '') + ifelse(pkt4.giaddr == 0.0.0.0, '', ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + ifelse(option[82].option[1].exists, ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), '') + ifelse(option[82].option[2].exists, ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), '') + ifelse(option[82].option[6].exists, ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), '')), '')" } .. raw:: html
Expand here!
{
        "request-parser-format":
            "ifelse(pkt4.msgtype == 4 or pkt4.msgtype == 7,
                'Address: ' +
                ifelse(option[50].exists,
                    addrtotext(option[50].hex),
                    addrtotext(pkt4.ciaddr)) +
                ' has been released from a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') +
                ifelse(option[61].exists,
                    ', client-id: ' + hexstring(option[61].hex, ':'),
                    '') +
                ifelse(pkt4.giaddr == 0.0.0.0,
                    '',
                    ' connected via relay at address: ' + addrtotext(pkt4.giaddr) +
                    ifelse(option[82].option[1].exists,
                        ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'),
                        '') +
                    ifelse(option[82].option[2].exists,
                        ', remote-id: ' + hexstring(option[82].option[2].hex, ':'),
                        '') +
                    ifelse(option[82].option[6].exists,
                        ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'),
                        '')),
                '')",
        "response-parser-format":
            "ifelse(pkt4.msgtype == 5,
                'Address: ' + addrtotext(pkt4.yiaddr) + ' has been assigned for ' + uint32totext(option[51].hex) + ' seconds to a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') +
                ifelse(option[61].exists,
                    ', client-id: ' + hexstring(option[61].hex, ':'),
                    '') +
                ifelse(pkt4.giaddr == 0.0.0.0,
                    '',
                    ' connected via relay at address: ' + addrtotext(pkt4.giaddr) +
                    ifelse(option[82].option[1].exists,
                        ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'),
                        '') +
                    ifelse(option[82].option[2].exists,
                        ', remote-id: ' + hexstring(option[82].option[2].hex, ':'),
                        '') +
                    ifelse(option[82].option[6].exists,
                        ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'),
                        '')),
                '')"
    }

This will log the following data on request and renew: :: Address: 192.2.1.100 has been assigned for 6735 seconds to a device with hardware address: hwtype=1 08:00:2b:02:3f:4e, client-id: 17:34:e2:ff:09:92:54 connected via relay at address: 192.2.16.33, circuit-id: 68:6f:77:64:79, remote-id: 87:f6:79:77:ef, subscriber-id: 1a:2b:3c:4d:5e:6f This will log the following data on release and decline: :: Address: 192.2.1.100 has been released from a device with hardware address: hwtype=1 08:00:2b:02:3f:4e, client-id: 17:34:e2:ff:09:92:54 connected via relay at address: 192.2.16.33, circuit-id: 68:6f:77:64:79, remote-id: 87:f6:79:77:ef, subscriber-id: 1a:2b:3c:4d:5e:6f Similar result can be obtained if configuring ``request-parser-format`` only. Examples: .. code-block:: json { "request-parser-format": "ifelse(pkt4.msgtype == 3, 'Address: ' + ifelse(option[50].exists, addrtotext(option[50].hex), addrtotext(pkt4.ciaddr)) + ' has been assigned' + ifelse(option[51].exists, ' for ' + uint32totext(option[51].hex) + ' seconds', '') + ' to a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + ifelse(option[61].exists, ', client-id: ' + hexstring(option[61].hex, ':'), '') + ifelse(pkt4.giaddr == 0.0.0.0, '', ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + ifelse(option[82].option[1].exists, ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), '') + ifelse(option[82].option[2].exists, ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), '') + ifelse(option[82].option[6].exists, ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), '')), ifelse(pkt4.msgtype == 4 or pkt4.msgtype == 7, 'Address: ' + ifelse(option[50].exists, addrtotext(option[50].hex), addrtotext(pkt4.ciaddr)) + ' has been released from a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + ifelse(option[61].exists, ', client-id: ' + hexstring(option[61].hex, ':'), '') + ifelse(pkt4.giaddr == 0.0.0.0, '', ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + ifelse(option[82].option[1].exists, ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), '') + ifelse(option[82].option[2].exists, ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), '') + ifelse(option[82].option[6].exists, ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), '')), ''))" } .. raw:: html
Expand here!
{
        "request-parser-format":
            "ifelse(pkt4.msgtype == 3,
                'Address: ' +
                ifelse(option[50].exists,
                    addrtotext(option[50].hex),
                    addrtotext(pkt4.ciaddr)) +
                ' has been assigned' +
                ifelse(option[51].exists,
                    ' for ' + uint32totext(option[51].hex) + ' seconds',
                    '') +
                ' to a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') +
                ifelse(option[61].exists,
                    ', client-id: ' + hexstring(option[61].hex, ':'),
                    '') +
                ifelse(pkt4.giaddr == 0.0.0.0,
                    '',
                    ' connected via relay at address: ' + addrtotext(pkt4.giaddr) +
                    ifelse(option[82].option[1].exists,
                        ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'),
                        '') +
                    ifelse(option[82].option[2].exists,
                        ', remote-id: ' + hexstring(option[82].option[2].hex, ':'),
                        '') +
                    ifelse(option[82].option[6].exists,
                        ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'),
                        '')),
                ifelse(pkt4.msgtype == 4 or pkt4.msgtype == 7,
                    'Address: ' +
                    ifelse(option[50].exists,
                        addrtotext(option[50].hex),
                        addrtotext(pkt4.ciaddr)) +
                    ' has been released from a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') +
                    ifelse(option[61].exists,
                        ', client-id: ' + hexstring(option[61].hex, ':'),
                        '') +
                    ifelse(pkt4.giaddr == 0.0.0.0,
                        '',
                        ' connected via relay at address: ' + addrtotext(pkt4.giaddr) +
                        ifelse(option[82].option[1].exists,
                            ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'),
                            '') +
                        ifelse(option[82].option[2].exists,
                            ', remote-id: ' + hexstring(option[82].option[2].hex, ':'),
                            '') +
                        ifelse(option[82].option[6].exists,
                            ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'),
                            '')),
                    ''))"
    }

DHCPv6 Log Entries ~~~~~~~~~~~~~~~~~~ For DHCPv6, the library creates entries based on REQUEST, RENEW, RELEASE, DECLINE messages et.al. and their responses. The resulting packets and leases are taken into account, intercepted through the following hook points: * pkt6_receive * leases6_committed * pkt6_send * lease6_release * lease6_decline An entry is a single string with no embedded end-of-line markers and a prepended timestamp, and has the following sections: :: timestamp address duration device-id {relay-info}* {user-context} Where: - timestamp - the current date and time the log entry was written in "%Y-%m-%d %H:%M:%S %Z" strftime format ("%Z" is the time zone name). - address - the leased IPv6 address or prefix given out and whether it was assigned, renewed or released. - duration - the lease lifetime expressed in days (if present), hours, minutes, and seconds. A lease lifetime of 0xFFFFFFFF will be denoted with the text "infinite duration". This information is not given when the lease is released. - device-id - the client's DUID and hardware address (if present). - relay-info - for relayed packets the content of relay agent messages, remote-id (code 37), subscriber-id (code 38), and interface-id (code 18) options, if present. Note that interface-id option, if present, identifies the whole interface the relay agent received the message on. This typically translates to a single link in the network, but it depends on the specific network topology. Nevertheless, this is useful information to better scope down the location of the device, so it is recorded, if present. - user-context - the optional user context associated with the lease. For instance (line breaks added for readability; they will not be present in the log file): :: 2018-01-06 01:02:03 PST Address:2001:db8:1:: has been assigned for 0 hrs 11 mins 53 secs to a device with DUID: 17:34:e2:ff:09:92:54 and hardware address: hwtype=1 08:00:2b:02:3f:4e (from Raw Socket) connected via relay at address: fe80::abcd for client on link address: 3001::1, hop count: 1, identified by remote-id: 01:02:03:04:0a:0b:0c:0d:0e:0f and subscriber-id: 1a:2b:3c:4d:5e:6f or for a release: :: 2018-01-06 01:02:03 PST Address:2001:db8:1:: has been released from a device with DUID: 17:34:e2:ff:09:92:54 and hardware address: hwtype=1 08:00:2b:02:3f:4e (from Raw Socket) connected via relay at address: fe80::abcd for client on link address: 3001::1, hop count: 1, identified by remote-id: 01:02:03:04:0a:0b:0c:0d:0e:0f and subscriber-id: 1a:2b:3c:4d:5e:6f In addition to logging lease activity driven by DHCPv6 client traffic, the hooks library also logs entries for the following lease management control channel commands: lease6-add, lease6-update, and lease6-del. Each entry is a single string with no embedded end-of-line markers, and it will typically have the following form: ``lease6-add:`` :: *timestamp* Administrator added a lease of address: *address* to a device with DUID: *DUID* Depending on the arguments of the add command, it may also include the hardware address and duration. Example: :: 2018-01-06 01:02:03 PST Administrator added a lease of address: 2001:db8::3 to a device with DUID: 1a:1b:1c:1d:1e:1f:20:21:22:23:24 for 1 days 0 hrs 0 mins 0 secs ``lease6-update:`` :: *timestamp* Administrator updated information on the lease of address: *address* to a device with DUID: *DUID* Depending on the arguments of the update command, it may also include the hardware address and lease duration. Example: :: 2018-01-06 01:02:03 PST Administrator updated information on the lease of address: 2001:db8::3 to a device with DUID: 1a:1b:1c:1d:1e:1f:20:21:22:23:24, hardware address: 1a:1b:1c:1d:1e:1f ``lease6-del:`` deletes have two forms, one by address and one by identifier and identifier type: :: *timestamp* Administrator deleted the lease for address: *address* or :: *timestamp* Administrator deleted a lease for a device identified by: *identifier-type* of *identifier* Currently only a type of DUID is supported. Examples: :: 2018-01-06 01:02:03 PST Administrator deleted the lease for address: 2001:db8::3 2018-01-06 01:02:11 PST Administrator deleted a lease for a device identified by: duid of 1a:1b:1c:1d:1e:1f:20:21:22:23:24 The ``request-parser-format`` and ``response-parser-format`` can be used to extract and log data from the incoming packet and server response packet respectively. The configured value is an evaluated parsed expression returning a string. A list of tokens is described in the server classification process. Use with caution as this might affect server performance. If any of them is configured, the default logging format is not used. If both of them are configured, the resulting log message is constructed by concatenating the logged data extracted from the request and the logged data extracted from the response. Some data might be available in the request or in the response only and some data might differ in the incoming packet from the one in the response packet. Examples: .. code-block:: json { "request-parser-format": "ifelse(pkt6.msgtype == 8 or pkt6.msgtype == 9, ifelse(option[3].option[5].exists, 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), '') + ifelse(option[25].option[26].exists, 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), ''), '')", "response-parser-format": "ifelse(pkt6.msgtype == 7, ifelse(option[3].option[5].exists, 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been assigned for ' + uint32totext(substring(option[3].option[5].hex, 20, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), '') + ifelse(option[25].option[26].exists, 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been assigned for ' + uint32totext(substring(option[25].option[26].hex, 4, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), ''), '')" } .. raw:: html
Expand here!
{
        "request-parser-format":
            "ifelse(pkt6.msgtype == 8 or pkt6.msgtype == 9,
                ifelse(option[3].option[5].exists,
                    'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') +
                    ifelse(relay6[0].peeraddr == '',
                        '',
                        ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) +
                        ifelse(relay6[0].option[37].exists,
                            ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'),
                            '') +
                        ifelse(relay6[0].option[38].exists,
                            ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'),
                            '') +
                        ifelse(relay6[0].option[18].exists,
                            ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'),
                            '')),
                    '') +
                ifelse(option[25].option[26].exists,
                    'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') +
                    ifelse(relay6[0].peeraddr == '',
                        '',
                        ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) +
                        ifelse(relay6[0].option[37].exists,
                            ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'),
                            '') +
                        ifelse(relay6[0].option[38].exists,
                            ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'),
                            '') +
                        ifelse(relay6[0].option[18].exists,
                            ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'),
                            '')),
                    ''),
                '')",
        "response-parser-format":
            "ifelse(pkt6.msgtype == 7,
                ifelse(option[3].option[5].exists,
                    'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been assigned for ' + uint32totext(substring(option[3].option[5].hex, 20, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') +
                    ifelse(relay6[0].peeraddr == '',
                        '',
                        ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) +
                        ifelse(relay6[0].option[37].exists,
                            ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'),
                            '') +
                        ifelse(relay6[0].option[38].exists,
                            ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'),
                            '') +
                        ifelse(relay6[0].option[18].exists,
                            ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'),
                            '')),
                    '') +
                ifelse(option[25].option[26].exists,
                    'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been assigned for ' + uint32totext(substring(option[25].option[26].hex, 4, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') +
                    ifelse(relay6[0].peeraddr == '',
                        '',
                        ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) +
                        ifelse(relay6[0].option[37].exists,
                            ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'),
                            '') +
                        ifelse(relay6[0].option[38].exists,
                            ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'),
                            '') +
                        ifelse(relay6[0].option[18].exists,
                            ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'),
                            '')),
                    ''),
                '')"
    }

This will log the following data on request, renew and rebind for NA: :: Address: 2001:db8:1:: has been assigned for 713 seconds to a device with DUID: 17:34:e2:ff:09:92:54 connected via relay at address: fe80::abcd for client on link address: 3001::1, remote-id: 01:02:03:04:0a:0b:0c:0d:0e:0f, subscriber-id: 1a:2b:3c:4d:5e:6f, connected at location interface-id: 72:65:6c:61:79:31:3a:65:74:68:30 This will log the following data on request, renew and rebind for PD: :: Prefix: 2001:db8:1::/64 has been assigned for 713 seconds to a device with DUID: 17:34:e2:ff:09:92:54 connected via relay at address: fe80::abcd for client on link address: 3001::1, remote-id: 01:02:03:04:0a:0b:0c:0d:0e:0f, subscriber-id: 1a:2b:3c:4d:5e:6f, connected at location interface-id: 72:65:6c:61:79:31:3a:65:74:68:30 This will log the following data on release and decline for NA: :: Address: 2001:db8:1:: has been released from a device with DUID: 17:34:e2:ff:09:92:54 connected via relay at address: fe80::abcd for client on link address: 3001::1, remote-id: 01:02:03:04:0a:0b:0c:0d:0e:0f, subscriber-id: 1a:2b:3c:4d:5e:6f, connected at location interface-id: 72:65:6c:61:79:31:3a:65:74:68:30 This will log the following data on release and decline for PD: :: Prefix: 2001:db8:1::/64 has been released from a device with DUID: 17:34:e2:ff:09:92:54 connected via relay at address: fe80::abcd for client on link address: 3001::1, remote-id: 01:02:03:04:0a:0b:0c:0d:0e:0f, subscriber-id: 1a:2b:3c:4d:5e:6f, connected at location interface-id: 72:65:6c:61:79:31:3a:65:74:68:30 Similar result can be obtained if configuring ``request-parser-format`` only. Examples: .. code-block:: json { "request-parser-format": "ifelse(pkt6.msgtype == 3 or pkt6.msgtype == 5 or pkt6.msgtype == 6, ifelse(option[3].option[5].exists, 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been assigned for ' + uint32totext(substring(option[3].option[5].hex, 20, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), '') + ifelse(option[25].option[26].exists, 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been assigned for ' + uint32totext(substring(option[25].option[26].hex, 4, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), ''), ifelse(pkt6.msgtype == 8 or pkt6.msgtype == 9, ifelse(option[3].option[5].exists, 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), '') + ifelse(option[25].option[26].exists, 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), ''), ''))" } .. raw:: html
Expand here!
{
        "request-parser-format":
            "ifelse(pkt6.msgtype == 3 or pkt6.msgtype == 5 or pkt6.msgtype == 6,
                ifelse(option[3].option[5].exists,
                    'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been assigned for ' + uint32totext(substring(option[3].option[5].hex, 20, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') +
                    ifelse(relay6[0].peeraddr == '',
                        '',
                        ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) +
                        ifelse(relay6[0].option[37].exists,
                            ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'),
                            '') +
                        ifelse(relay6[0].option[38].exists,
                            ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'),
                            '') +
                        ifelse(relay6[0].option[18].exists,
                            ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'),
                            '')),
                    '') +
                ifelse(option[25].option[26].exists,
                    'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been assigned for ' + uint32totext(substring(option[25].option[26].hex, 4, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') +
                    ifelse(relay6[0].peeraddr == '',
                        '',
                        ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) +
                        ifelse(relay6[0].option[37].exists,
                            ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'),
                            '') +
                        ifelse(relay6[0].option[38].exists,
                            ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'),
                            '') +
                        ifelse(relay6[0].option[18].exists,
                            ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'),
                            '')),
                    ''),
                ifelse(pkt6.msgtype == 8 or pkt6.msgtype == 9,
                    ifelse(option[3].option[5].exists,
                        'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') +
                        ifelse(relay6[0].peeraddr == '',
                            '',
                            ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) +
                            ifelse(relay6[0].option[37].exists,
                                ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'),
                                '') +
                            ifelse(relay6[0].option[38].exists,
                                ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'),
                                '') +
                            ifelse(relay6[0].option[18].exists,
                                ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'),
                                '')),
                        '') +
                    ifelse(option[25].option[26].exists,
                        'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') +
                        ifelse(relay6[0].peeraddr == '',
                            '',
                            ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) +
                            ifelse(relay6[0].option[37].exists,
                                ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'),
                                '') +
                            ifelse(relay6[0].option[38].exists,
                                ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'),
                                '') +
                            ifelse(relay6[0].option[18].exists,
                                ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'),
                                '')),
                        ''),
                    ''))"
    }

.. _forensic-log-database: Database Backend ~~~~~~~~~~~~~~~~ Log entries can be inserted into a database when Kea is configured with database backend support. A table named "logs" is used that includes a timestamp (timeuuid for Cassandra) generated by the database software, and a text log with the same format as files without the timestamp. Please refer to :ref:`mysql-database` for information on using a MySQL database; to :ref:`pgsql-database` for PostgreSQL database information; or to :ref:`cql-database` for information on using a Cassandra (CQL) database. The logs table is part of the Kea database schemas. Configuration parameters are extended by standard lease database parameters as defined in :ref:`database-configuration4`. The "type" parameter should be "mysql", "postgresql", "cql", or "logfile". When it is absent or set to "logfile", files are used. This database feature is experimental and will be likely improved, for instance to add an address/prefix index (currently the only index is the timestamp). No specific tools are provided to operate the database, but standard tools may be used, for example, to dump the logs table from a CQL database: :: $ echo 'SELECT dateOf(timeuuid), log FROM logs;' | cqlsh -k database-name system.dateof(timeuuid) | log ---------------------------------+--------------------------------------- 2018-01-06 01:02:03.227000+0000 | Address: 192.2.1.100 has been renewed ... ... (12 rows) Like all the other database-centric features, forensic logging supports database connection recovery which can be enabled by setting the ``on-fail`` parameter. If not specified, the ``on-fail`` parameter defaults to ``serve-retry-continue`` as opposed to the case of lease manager, host manager and config backend where it defaults to ``stop-retry-exit``. In this case, the server will continue serving clients and it will not shut down even if the recovery mechanism fails. If the ``on-fail`` is set to ``serve-retry-exit``, the server will shut down if the connection to the database backend is not restored according to the ``max-reconnect-tries`` and ``reconnect-wait-time`` parameters, but it will continue serving clients while this mechanism is activated. .. _flex-id: flex_id: Flexible Identifiers for Host Reservations =================================================== This section describes a hook application dedicated to generating flexible identifiers for host reservations. The Kea software provides a way to handle host reservations that include addresses, prefixes, options, client classes, and other features. The reservation can be based on hardware address, DUID, circuit-id, or client-id in DHCPv4 and on hardware address or DUID in DHCPv6. However, there are sometimes scenarios where the reservation is more complex; it may use options other than those mentioned above, use parts of specific options, or perhaps even use a combination of several options and fields to uniquely identify a client. Those scenarios are addressed by the Flexible Identifiers hook application. Currently this library is only available to ISC customers with a paid support contract. .. note:: This library may only be loaded by the ``kea-dhcp4`` or ``kea-dhcp6`` process. The library allows the definition of an expression, using notation initially used only for client classification. (See :ref:`classification-using-expressions` for a detailed description of the syntax available.) One notable difference is that for client classification, the expression currently has to evaluate to either true or false, while the flexible identifier expression is expected to evaluate to a string that will be used as an identifier. It is a valid case for the expression to evaluate to an empty string (e.g. in cases where a client does not send specific options). This expression is then evaluated for each incoming packet, and this evaluation generates an identifier that is used to identify the client. In particular, there may be host reservations that are tied to specific values of the flexible identifier. The library can be loaded in a similar way as other hook libraries. It takes a mandatory parameter ``identifier-expression`` and optional boolean parameter ``replace-client-id``: :: "Dhcp6": { "hooks-libraries": [ { "library": "/path/libdhcp_flex_id.so", "parameters": { "identifier-expression": "expression", "replace-client-id": false } }, ... ] } The flexible identifier library supports both DHCPv4 and DHCPv6. Let's consider a case of an IPv6 network that has an independent interface for each of its connected customers. Customers are able to plug in whatever device they want, so any type of identifier (e.g. a client-id) is unreliable. Therefore, the operator may decide to use an option inserted by a relay agent to differentiate between clients. In this particular deployment, the operator has verified that the interface-id is unique for each customer-facing interface, so it is suitable for usage as a reservation. However, only the first six bytes of the interface-id are interesting, because remaining bytes are either randomly changed or not unique between devices. Therefore, the customer decided to use the first six bytes of the interface-id option inserted by the relay agent. After adding ``flex-id``, the ``host-reservation-identifiers`` goal can be achieved by using the following configuration: :: "Dhcp6": { "subnet6": [{ ..., # subnet definition starts here "reservations": [ "flex-id": "'port1234'", # value of the first 8 bytes of the interface-id "ip-addresses": [ "2001:db8::1" ] ], }], # end of subnet definitions "host-reservation-identifiers": ["duid", "flex-id"], # add "flex-id" to reservation identifiers "hooks-libraries": [ { "library": "/path/libdhcp_flex_id.so", "parameters": { "identifier-expression": "substring(relay6[0].option[18].hex,0,8)" } }, ... ] } .. note:: Care should be taken when adjusting the expression. If the expression changes, then all the ``flex-id`` values may change, possibly rendering all reservations based on ``flex-id`` unusable until they are manually updated. It is strongly recommended that administrators start with the expression and a handful of reservations, and then adjust the expression as needed. Once the expression is confirmed to do what is desired of it, host reservations can be deployed on a broader scale. ``flex-id`` values in host reservations can be specified in two ways. First, they can be expressed as a hex string, e.g. bar string can be represented as 626174. Alternatively, it can be expressed as a quoted value (using double and single quotes), e.g. "'bar'". The former is more convenient for printable characters, while hex string values are more convenient for non-printable characters and do not require the use of the ``hexstring`` operator. :: "Dhcp6": { "subnet6": [{ ..., # subnet definition starts here "reservations": [ "flex-id": "01:02:03:04:05:06", # value of the first 8 bytes of the interface-id "ip-addresses": [ "2001:db8::1" ] ], }], # end of subnet definitions "host-reservation-identifiers": ["duid", "flex-id"], # add "flex-id" to reservation identifiers "hooks-libraries": [ { "library": "/path/libdhcp_flex_id.so", "parameters": { "identifier-expression": "vendor[4491].option[1026].hex" } }, ... ] } When ``replace-client-id`` is set to "false" (which is the default setting), the flex-id hook library uses the evaluated flexible identifier solely for identifying host reservations, i.e. searching for reservations within a database. This is the functional equivalent of other identifiers, similar to hardware address or circuit-id. However, this mode of operation implies that if a client device is replaced, it may cause a conflict between an existing lease (allocated to the old device) and the new lease being allocated to the new device. The conflict arises because the same flexible identifier is computed for the replaced device, so the server will try to allocate the same lease. The mismatch between client identifiers sent by the new device and the old device causes the server to refuse this new allocation until the old lease expires. A manifestation of this problem is dependent on the specific expression used as the flexible identifier and is likely to appear if only options and other parameters are used that identify where the device is connected (e.g. circuit-id), rather than the device identification itself (e.g. MAC address). The flex-id library offers a way to overcome the problem with lease conflicts by dynamically replacing the client identifier (or DUID in DHCPv6) with a value derived from the flexible identifier. The server processes the client's query as if the flexible identifier were sent in the client identifier (or DUID) option. This guarantees that a returning client (for which the same flexible identifier is evaluated) will be assigned the same lease despite the client identifier and/or MAC address change. The following is a stub configuration that enables this behavior: :: "Dhcp4": { "hooks-libraries": [ { "library": "/path/libdhcp_flex_id.so", "parameters": { "identifier-expression": "expression", "replace-client-id": true } }, ... ] } In the DHCPv4 case, the value derived from the flexible identifier is formed by prepending one byte with a value of zero to the flexible identifier. In the DHCPv6 case, it is formed by prepending two zero bytes before the flexible identifier. Note that for this mechanism to take effect, the DHCPv4 server must be configured to respect the client identifier option value during lease allocation, i.e. ``match-client-id`` must be set to "true". See :ref:`dhcp4-match-client-id` for details. No additional settings are required for DHCPv6. If the ``replace-client-id`` option is set to "true", the value of the ``echo-client-id`` parameter (which governs whether to send back a client-id option) is ignored. The :ref:`lease-cmds` section describes commands used to retrieve, update, and delete leases using various identifiers, such as "hw-address" and "client-id". The lease_cmds library does not natively support querying for leases by flexible identifier. However, when ``replace-client-id`` is set to "true", it makes it possible to query for leases using a value derived from the flexible identifier. In the DHCPv4 case, the query will look similar to this: :: { "command": "lease4-get", "arguments": { "identifier-type": "client-id", "identifier": "00:54:64:45:66", "subnet-id": 44 } } where the hexadecimal value of "54:64:45:66" is a flexible identifier computed for the client. In the DHCPv6 case, the corresponding query will look similar to this: :: { "command": "lease6-get", "arguments": { "identifier-type": "duid", "identifier": "00:00:54:64:45:66", "subnet-id": 10 } } .. _flex-option: flex_option Flexible Option for Option value settings ===================================================== This library allows you to define an action to take, for a given option, based upon on the result of an expression. These actions are carried out during the final stages of constructing a query response packet, just before it is sent to the client. The three actions currently supported are ``add``, ``supersede``, and ``remove``. The syntax used for the action expressions is the same syntax used for client classification and the Flex Identifier hook library (See either :ref:`classification-using-expressions` or :ref:`flex-id` for detailed description of the syntax). The ``add`` and ``supersede`` actions use an expression returning a string, doing nothing when it evaluates to the empty string. The ``remove`` application uses an expression returning true or false, doing nothing on false. When it is necessary to set an option to the empty value this mechanism does not work but a client class can be used instead. The ``add`` action adds an option only when the option does not already exist and the expression does not evaluate to the empty string. The ``supersede`` action does the same but it overwrites the option value if it already exists. The ``remove`` action removes the option from the response packet if it already exists and the expression evaluates to true. The option to which an action applies may be specified by either its numeric code or its name.. At least the code or the name must be specified. The option space is the DHCPv4 or DHCPv6 spaces depending on the server where the hook library is loaded. Other spaces as vendor spaces could be supported in a further version. The library is available since Kea 1.7.1 and can be loaded in a similar way as other hook libraries by the ``kea-dhcp4`` or `kea-dhcp6`` process.. It takes a mandatory ``options`` parameter holding a list of per option parameter maps with code, name, add, supersede and remove actions. Action entries take a string value representing an expression. :: "Dhcp4": { "hooks-libraries": [ { "library": "/usr/local/lib/libdhcp_flex_option.so", "parameters": { "options": [ { "code": 67, "add": "ifelse(option[host-name].exists,concat(option[host-name].text,'.boot'),'')" } ] } }, ... ] } If (and only if) the query includes a host-name option (code 12), a boot-file-name option (code 67) is added to the response with the host name followed by .boot for content. The flexible option library supports both DHCPv4 and DHCPv6. Since Kea 1.9.0, the add and supersede actions take an optional csv-format boolean parameter. If not specified or configured to false, the option data is set using the raw value of the evaluated expression. When it is configured to true, this value is parsed using the option definition from the option data specified in the configuration file. This eases option setting for options using complex record formats or fully qualified domain names. For instance if the expression evaluation returns "example.com" and the option is defined with the fqdn type the domain name will be encoded into DNS binary format. .. _host-cmds: host_cmds: Host Commands ======================== This section describes a hook application that offers a number of new commands used to query and manipulate host reservations. Kea provides a way to store host reservations in a database. In many larger deployments it is useful to be able to manage that information while the server is running. This library provides management commands for adding, querying, and deleting host reservations in a safe way without restarting the server. In particular, it validates the parameters, so an attempt to insert incorrect data - such as adding a host with a conflicting identifier in the same subnet - will be rejected. Those commands are exposed via the command channel (JSON over UNIX sockets) and the Control Agent (JSON over a RESTful interface). Additional commands and capabilities related to host reservations will be added in the future. Currently this library is only available to ISC customers with a paid support contract. .. note:: This library may only be loaded by the ``kea-dhcp4`` or ``kea-dhcp6`` process. Currently, six commands are supported: reservation-add (which adds a new host reservation), reservation-get (which returns an existing reservation if specified criteria are matched), reservation-get-all (which returns all reservations in a specified subnet), reservation-get-page (a variant of reservation-get-all which returns all reservations in a specified subnet by pages and since Kea version 1.9.0 all reservations), reservation-get-by-hostname (which returns all reservations with a specified hostname and optionally in a subnet) since Kea version 1.7.1, reservation-get-by-id (which returns all reservations with a specified identifier) since Kea version 1.9.0, and reservation-del (which attempts to delete a reservation matching specified criteria). To use commands that change the reservation information (currently these are reservation-add and reservation-del, but this rule applies to other commands that may be implemented in the future), the hosts database must be specified and it must not operate in read-only mode (see the hosts-databases descriptions in :ref:`hosts-databases-configuration4` and :ref:`hosts-databases-configuration6`). If the hosts-databases are not specified or are running in read-only mode, the host_cmds library will load, but any attempts to use reservation-add or reservation-del will fail. Additional host reservation commands are planned in future releases of Kea. For a description of envisaged commands, see the `Control API Requirements `__ document. All commands use JSON syntax. They can be issued either using the control channel (see :ref:`ctrl-channel`) or via the Control Agent (see :ref:`kea-ctrl-agent`). The library can be loaded similarly to other hook libraries. It does not take any parameters, and it supports both DHCPv4 and DHCPv6 servers. :: "Dhcp6": { "hooks-libraries": [ { "library": "/path/libdhcp_host_cmds.so" } ... ] } The subnet-id Parameter ~~~~~~~~~~~~~~~~~~~~~~~ Prior to diving into the individual commands, it is worth discussing the parameter, ``subnet-id``. Currently this parameter is mandatory for all of the commands supplied by this library with the exception of reservation-get-by-hostname where it is optional, and since Kea 1.9.0 reservation-get-page where it is optional and reservation-get-by-id where it is forbidden. In previous versions of Kea, reservations had to belong to a specific subnet; as of Kea 1.5.0, reservations may be specified globally. In other words, they are not specific to any subnet. When reservations are supplied via the configuration file, the ID of the containing subnet (or lack thereof) is implicit in the configuration structure. However, when managing reservations using host commands, it is necessary to explicitly identify the scope to which the reservation belongs. This is done via the ``subnet-id`` parameter. For global reservations, use a value of zero (0). For reservations scoped to a specific subnet, use that subnet's ID. On the other hand when the subnet id is not specified in the command parameters it is added to each host in responses. If the subnet id has the unused special value this means the host entry belongs only to the other IP version (i.e. IPv6 in DHCPv4 server or IPv4 in DHCPv6 server) and this entry is ignored. .. _command-reservation-add: The reservation-add Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``reservation-add`` allows for the insertion of a new host. It takes a set of arguments that vary depending on the nature of the host reservation. Any parameters allowed in the configuration file that pertain to host reservation are permitted here. For details regarding IPv4 reservations, see :ref:`host-reservation-v4`; for IPv6 reservations, see :ref:`host-reservation-v6`. The ``subnet-id`` is mandatory. Use a value of zero (0) to add a global reservation, or the id of the subnet to which the reservation should be added. An example command can be as simple as: .. code-block:: json { "command": "reservation-add", "arguments": { "reservation": { "subnet-id": 1, "hw-address": "1a:1b:1c:1d:1e:1f", "ip-address": "192.0.2.202" } } } but it can also take many more parameters, for example: .. code-block:: json { "command": "reservation-add", "arguments": { "reservation": { "subnet-id": 1, "client-id": "01:0a:0b:0c:0d:0e:0f", "ip-address": "192.0.2.205", "next-server": "192.0.2.1", "server-hostname": "hal9000", "boot-file-name": "/dev/null", "option-data": [ { "name": "domain-name-servers", "data": "10.1.1.202,10.1.1.203" } ], "client-classes": [ "special_snowflake", "office" ] } } } Here is an example of a complex IPv6 reservation: .. code-block:: json { "command": "reservation-add", "arguments": { "reservation": { "subnet-id": 1, "duid": "01:02:03:04:05:06:07:08:09:0A", "ip-addresses": [ "2001:db8:1:cafe::1" ], "prefixes": [ "2001:db8:2:abcd::/64" ], "hostname": "foo.example.com", "option-data": [ { "name": "vendor-opts", "data": "4491" }, { "name": "tftp-servers", "space": "vendor-4491", "data": "3000:1::234" } ] } } } The command returns a status that indicates either a success (result 0) or a failure (result 1). A failed command always includes a text parameter that explains the cause of the failure. Example results: :: { "result": 0, "text": "Host added." } Example failure: :: { "result": 1, "text": "Mandatory 'subnet-id' parameter missing." } As ``reservation-add`` is expected to store the host, the hosts-databases parameter must be specified in the configuration and databases must not run in read-only mode. In future versions of Kea, it will be possible to modify the reservations read from a configuration file. Interested parties are encouraged to contact ISC for more information on developing this functionality. .. _command-reservation-get: The reservation-get Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``reservation-get`` can be used to query the host database and retrieve existing reservations. There are two types of parameters this command supports: (subnet-id, address) or (subnet-id, identifier-type, identifier). The first type of query is used when the address (either IPv4 or IPv6) is known, but the details of the reservation are not. One common use case of this type of query is to find out whether a given address is reserved. The second query uses identifiers. For maximum flexibility, Kea stores the host identifying information as a pair of values: the type and the actual identifier. Currently supported identifiers are "hw-address", "duid", "circuit-id", "client-id", and "flex-id", but additional types may be added in the future. If any new identifier types are defined in the future, the reservation-get command will support them automatically. The ``subnet-id`` is mandatory. Use a value of zero (0) to fetch a global reservation, or the id of the subnet to which the reservation belongs. An example command for getting a host reservation by a (subnet-id, address) pair looks as follows: :: { "command": "reservation-get", "arguments": { "subnet-id": 1, "ip-address": "192.0.2.202" } } An example query by (subnet-id, identifier-type, identifier) looks as follows: :: { "command": "reservation-get", "arguments": { "subnet-id": 4, "identifier-type": "hw-address", "identifier": "01:02:03:04:05:06" } } ``reservation-get`` typically returns the result 0 when the query was conducted properly. In particular, 0 is returned when the host was not found. If the query was successful, a number of host parameters will be returned. An example of a query that did not find the host looks as follows: :: { "result": 0, "text": "Host not found." } An example result returned when the host was found looks like this: :: { "arguments": { "boot-file-name": "bootfile.efi", "client-classes": [ ], "hostname": "somehost.example.org", "hw-address": "01:02:03:04:05:06", "ip-address": "192.0.2.100", "next-server": "192.0.0.2", "option-data": [ ], "server-hostname": "server-hostname.example.org" }, "result": 0, "text": "Host found." } An example result returned when the query was malformed might look like this: :: { "result": 1, "text": "No 'ip-address' provided and 'identifier-type' is either missing or not a string." } .. _command-reservation-get-all: The reservation-get-all Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``reservation-get-all`` can be used to query the host database and retrieve all reservations in a specified subnet. This command uses parameters providing the mandatory subnet-id. Global host reservations can be retrieved by using a subnet-id value of zero (0). For instance, retrieving host reservations for the subnet 1: :: { "command": "reservation-get-all", "arguments": { "subnet-id": 1 } } returns some IPv4 hosts: :: { "arguments": { "hosts": [ { "boot-file-name": "bootfile.efi", "client-classes": [ ], "hostname": "somehost.example.org", "hw-address": "01:02:03:04:05:06", "ip-address": "192.0.2.100", "next-server": "192.0.0.2", "option-data": [ ], "server-hostname": "server-hostname.example.org" }, ... { "boot-file-name": "bootfile.efi", "client-classes": [ ], "hostname": "otherhost.example.org", "hw-address": "01:02:03:04:05:ff", "ip-address": "192.0.2.200", "next-server": "192.0.0.2", "option-data": [ ], "server-hostname": "server-hostname.example.org" } ] }, "result": 0, "text": "72 IPv4 host(s) found." } The response returned by ``reservation-get-all`` can be very long. The DHCP server does not handle DHCP traffic when preparing a response to reservation-get-all, so if there are many reservations in a subnet, this may be disruptive. Use with caution. For larger deployments, please consider using ``reservation-get-page`` instead (see :ref:`command-reservation-get-page`). For a reference, see :ref:`command-reservation-get-all`. .. _command-reservation-get-page: The reservation-get-page command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``reservation-get-page`` can be used to query the host database and retrieve all reservations in a specified subnet by pages. This command uses parameters providing the mandatory subnet-id. Use a value of zero (0) to fetch global reservations. The second mandatory parameter is the page size limit. Optional source-index and from host id, both defaulting to 0, are used to chain page queries. Since Kea version 1.9.0 the subnet id parameter is optional. The usage of from and source-index parameters requires additional explanation. For the first call, those parameters should not be specified (or specified as zeros). For any follow-up calls, they should be set to the values returned in previous calls in a next map holding from and source-index values. Subsequent calls should be issued until all reservations are returned. The end is reached once the returned list is empty, the count is 0, no next map is present, and result status 3 (empty) is returned. .. note:: The from and source-index parameters are reflecting the internal state of the search. There is no need to understand what they represent; it is simply a value that is supposed to be copied from one response to the next query. However, for those who are curious, the from field represents a 64-bit representation of the host identifier used by a host backend. The source-index is an internal representation of multiple host backends: 0 is used to represent hosts defined in a configuration file, and 1 represents the first database backend. In some uncommon cases there may be more than one database backend configured, so potentially there may be a 2. In any case, Kea will iterate over all backends configured. For instance, retrieving host reservations for the subnet 1 and requesting the first page can be done by: :: { "command": "reservation-get-page", "arguments": { "subnet-id": 1, "limit": 10 } } Since this is the first call, source-index and from should not be specified. They will default to their zero default values. Some hosts are returned with information to get the next page: :: { "arguments": { "count": 72, "hosts": [ { "boot-file-name": "bootfile.efi", "client-classes": [ ], "hostname": "somehost.example.org", "hw-address": "01:02:03:04:05:06", "ip-address": "192.0.2.100", "next-server": "192.0.0.2", "option-data": [ ], "server-hostname": "server-hostname.example.org" }, ... { "boot-file-name": "bootfile.efi", "client-classes": [ ], "hostname": "otherhost.example.org", "hw-address": "01:02:03:04:05:ff", "ip-address": "192.0.2.200", "next-server": "192.0.0.2", "option-data": [ ], "server-hostname": "server-hostname.example.org" } ], "next": { "from": 1234567, "source-index": 1 } }, "result": 0, "text": "72 IPv4 host(s) found." } Note that the "from" and "source-index" fields were specified in the response in the next map. Those two must be copied to the next command, so Kea continues from the place where the last command finished. To get the next page the following command can be sent: :: { "command": "reservation-get-page", "arguments": { "subnet-id": 1, "source-index": 1, "from": 1234567, "limit": 10 } } The response will contain a list of hosts with updated source-index and from fields. Continue calling the command until the last page is received. Its response will look like this: :: { "arguments": { "count": 0, "hosts": [ ], }, "result": 3, "0 IPv4 host(s) found." } This command is more complex than ``reservation-get-all``, but lets users retrieve larger host reservations lists in smaller chunks. For small deployments with few reservations, it is easier to use ``reservation-get-all`` (see :ref:`command-reservation-get-all`). .. note:: Currently ``reservation-get-page`` is not supported by the Cassandra host backend. .. _command-reservation-get-by-hostname: The reservation-get-by-hostname Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``reservation-get-by-hostname`` can be used to query the host database and retrieve all reservations with a specified hostname and optionally in a specified subnet. This command uses parameters providing the mandatory hostname and the optional subnet-id. Global host reservations can be retrieved by using a subnet-id value of zero (0). Hostname matching is case-insensitive. This command is available since Kea version 1.7.1. For instance, retrieving host reservations for "foobar" in the subnet 1: :: { "command": "reservation-get-by-hostname", "arguments": { "hostname": "foobar.example.org", "subnet-id": 1 } } returns some IPv4 hosts: :: { "arguments": { "hosts": [ { "boot-file-name": "bootfile.efi", "client-classes": [ ], "hostname": "foobar.example.org", "hw-address": "01:02:03:04:05:06", "ip-address": "192.0.2.100", "next-server": "192.0.0.2", "option-data": [ ], "server-hostname": "server-hostname.example.org" }, ... { "boot-file-name": "bootfile.efi", "client-classes": [ ], "hostname": "foobar.example.org", "hw-address": "01:02:03:04:05:ff", "ip-address": "192.0.2.200", "next-server": "192.0.0.2", "option-data": [ ], "server-hostname": "server-hostname.example.org" } ] }, "result": 0, "text": "5 IPv4 host(s) found." } The response returned by ``reservation-get-by-hostname`` can be long in particular when responses are not limited to a subnet. For a reference, see :ref:`command-reservation-get-by-hostname`. .. note:: When the host backend is MySQL this commands relies on the fact the hostname column in the hosts table uses a case-insensitive collation as explained in the :ref:`mysql-database` section of :ref:`admin`. .. _command-reservation-get-by-id: The reservation-get-by-id Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``reservation-get-by-id`` can be used to query the host database and retrieve all reservations with a specified identifier (identifier-type and identifier parameters) independently of subnets. The syntax for parameters is the same as for ref:`command-reservation-get`. The subnet-id parameter is forbidden to avoid confusion. This command is available since Kea version 1.9.0. For instance, retrieving host reservations for the 01:02:03:04:05:06 MAC address: :: { "command": "reservation-get-by-id", "arguments": { "identifier-type": "hw-address", "identifier": "01:02:03:04:05:06" } } returns some IPv4 hosts: :: { "arguments": { "hosts": [ { "boot-file-name": "bootfile.efi", "client-classes": [ ], "hostname": "foo.example.org", "hw-address": "01:02:03:04:05:06", "ip-address": "192.0.2.100", "next-server": "192.0.0.2", "option-data": [ ], "server-hostname": "server-hostname.example.org", "subnet-id": 123 }, ... { "boot-file-name": "bootfile.efi", "client-classes": [ ], "hostname": "bar.example.org", "hw-address": "01:02:03:04:05:06", "ip-address": "192.0.2.200", "next-server": "192.0.0.2", "option-data": [ ], "server-hostname": "server-hostname.example.org", "subnet-id": 345 } ] }, "result": 0, "text": "5 IPv4 host(s) found." } The response returned by ``reservation-get-by-id`` can be long in particular when responses are not limited to a subnet. For a reference, see :ref:`command-reservation-get-by-id`. .. _command-reservation-del: The reservation-del Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``reservation-del`` can be used to delete a reservation from the host database. There are two types of parameters this command supports: (subnet-id, address) or (subnet-id, identifier-type, identifier). The first type of query is used when the address (either IPv4 or IPv6) is known, but the details of the reservation are not. One common use case of this type of query is to remove a reservation (e.g. a specific address should no longer be reserved). The second query uses identifiers. For maximum flexibility, Kea stores the host identifying information as a pair of values: the type and the actual identifier. Currently supported identifiers are "hw-address", "duid", "circuit-id", "client-id", and "flex-id", but additional types may be added in the future. If any new identifier types are defined in the future, the reservation-get command will support them automatically. The ``subnet-id`` is mandatory. Use a value of zero (0) to delete a global reservation, or the id of the subnet from which the reservation should be deleted. An example command for deleting a host reservation by (subnet-id, address) pair looks as follows: :: { "command": "reservation-del", "arguments": { "subnet-id": 1, "ip-address": "192.0.2.202" } } An example deletion by (subnet-id, identifier-type, identifier) looks as follows: :: { "command": "reservation-del", "arguments": "subnet-id": 4, "identifier-type": "hw-address", "identifier": "01:02:03:04:05:06" } } ``reservation-del`` returns a result 0 when the host deletion was successful or 1 if it was not. Descriptive text is provided in the event of an error. Example results look as follows: :: { "result": 1, "text": "Host not deleted (not found)." } :: { "result": 0, "text": "Host deleted." } :: { "result": 1, "text": "Unable to delete a host because there is no hosts-database configured." } .. include:: hooks-lease-cmds.rst .. _subnet-cmds: subnet_cmds: Subnet Commands ============================ This section describes a hook application that offers some new commands used to query and manipulate subnet and shared network configurations in Kea. This application is very useful in deployments with a large number of subnets being managed by the DHCP servers, when those subnets are frequently updated. The commands offer a lightweight approach for manipulating subnets without a need to fully reconfigure the server and without affecting existing servers' configurations. An ability to manage shared networks (listing, retrieving details, adding new ones, removing existing ones, and adding subnets to and removing them from shared networks) is also provided. Currently this library is only available to ISC customers with a paid support contract. .. note:: This library may only be loaded by the ``kea-dhcp4`` or ``kea-dhcp6`` process. The following commands are currently supported: - ``subnet4-list/subnet6-list`` - lists all configured subnets. - ``subnet4-get/subnet6-get`` - retrieves detailed information about a specified subnet. - ``subnet4-add/subnet6-add`` - adds a new subnet into the server's configuration. - ``subnet4-update/subnet6-update`` - updates a subnet in the server's configuration. - ``subnet4-del/subnet6-del`` - removes a subnet from the server's configuration. - ``network4-list/network6-list`` - lists all configured shared networks. - ``network4-get/network6-get`` - retrieves detailed information about a specified shared network. - ``network4-add/network6-add`` - adds a new shared network to the server's configuration. - ``network4-del/network6-del`` - removes a shared network from the server's configuration. - ``network4-subnet-add/network6-subnet-add`` - adds an existing subnet to an existing shared network. - ``network4-subnet-del/network6-subnet-del`` - removes a subnet from an existing shared network and demotes it to a plain subnet. .. _command-subnet4-list: The subnet4-list Command ~~~~~~~~~~~~~~~~~~~~~~~~ This command is used to list all currently configured subnets. Each subnet is returned with a subnet identifier and subnet prefix. To retrieve detailed information about the subnet, use the ``subnet4-get`` command. This command has the simple structure: :: { "command": "subnet4-list" } The list of subnets is returned in the following format: :: { "result": 0, "text": "2 IPv4 subnets found", "arguments": { "subnets": [ { "id": 10, "subnet": "10.0.0.0/8" }, { "id": 100, "subnet": "192.0.2.0/24" } ] } If no IPv4 subnets are found, an error code is returned along with the error description. .. _command-subnet6-list: The subnet6-list Command ~~~~~~~~~~~~~~~~~~~~~~~~ This command is used to list all currently configured subnets. Each subnet is returned with a subnet identifier and subnet prefix. To retrieve detailed information about the subnet, use the ``subnet6-get`` command. This command has the simple structure: :: { "command": "subnet6-list" } The list of subnets is returned in the following format: :: { "result": 0, "text": "2 IPv6 subnets found", "arguments": { "subnets": [ { "id": 11, "subnet": "2001:db8:1::/64" }, { "id": 233, "subnet": "3000::/16" } ] } If no IPv6 subnets are found, an error code is returned along with the error description. .. _command-subnet4-get: The subnet4-get Command ~~~~~~~~~~~~~~~~~~~~~~~ This command is used to retrieve detailed information about the specified subnet. This command usually follows ``subnet4-list``, which is used to discover available subnets with their respective subnet identifiers and prefixes. Any of those parameters can be then used in ``subnet4-get`` to fetch subnet information: :: { "command": "subnet4-get", "arguments": { "id": 10 } } or :: { "command": "subnet4-get", "arguments": { "subnet": "10.0.0.0/8" } } If the subnet exists the response will be similar to this: :: { "result": 0, "text": "Info about IPv4 subnet 10.0.0.0/8 (id 10) returned", "arguments": { "subnets": [ { "subnet": "10.0.0.0/8", "id": 1, "option-data": [ .... ] ... } ] } } .. _command-subnet6-get: The subnet6-get Command ~~~~~~~~~~~~~~~~~~~~~~~ This command is used to retrieve detailed information about the specified subnet. This command usually follows ``subnet6-list``, which is used to discover available subnets with their respective subnet identifiers and prefixes. Any of those parameters can be then used in ``subnet6-get`` to fetch subnet information: :: { "command": "subnet6-get", "arguments": { "id": 11 } } or :: { "command": "subnet6-get", "arguments": { "subnet": "2001:db8:1::/64" } } If the subnet exists the response will be similar to this: :: { "result": 0, "text": "Info about IPv6 subnet 2001:db8:1::/64 (id 11) returned", "arguments": { "subnets": [ { "subnet": "2001:db8:1::/64", "id": 1, "option-data": [ ... ] .... } ] } } .. _command-subnet4-add: The subnet4-add Command ~~~~~~~~~~~~~~~~~~~~~~~ This command is used to create and add a new subnet to the existing server configuration. This operation has no impact on other subnets. The subnet identifier must be specified and must be unique among all subnets. If the identifier or a subnet prefix is not unique, an error is reported and the subnet is not added. The subnet information within this command has the same structure as the subnet information in the server configuration file, with the exception that static host reservations must not be specified within ``subnet4-add``. The commands described in :ref:`host-cmds` should be used to add, remove, and modify static reservations. :: { "command": "subnet4-add", "arguments": { "subnet4": [ { "id": 123, "subnet": "10.20.30.0/24", ... } ] } } The response to this command has the following structure: :: { "result": 0, "text": "IPv4 subnet added", "arguments": { "subnet4": [ { "id": 123, "subnet": "10.20.30.0/24" } ] } } .. _command-subnet6-add: The subnet6-add Command ~~~~~~~~~~~~~~~~~~~~~~~ This command is used to create and add a new subnet to the existing server configuration. This operation has no impact on other subnets. The subnet identifier must be specified and must be unique among all subnets. If the identifier or a subnet prefix is not unique, an error is reported and the subnet is not added. The subnet information within this command has the same structure as the subnet information in the server configuration file, with the exception that static host reservations must not be specified within ``subnet6-add``. The commands described in :ref:`host-cmds` should be used to add, remove, and modify static reservations. :: { "command": "subnet6-add", "arguments": { "subnet6": [ { "id": 234, "subnet": "2001:db8:1::/64", ... } ] } } The response to this command has the following structure: :: { "result": 0, "text": "IPv6 subnet added", "arguments": { "subnet6": [ { "id": 234, "subnet": "2001:db8:1::/64" } ] } } It is recommended, but not mandatory, to specify the subnet ID. If not specified, Kea will try to assign the next subnet-id value. This automatic ID value generator is simple; it returns a previously automatically assigned value, increased by 1. This works well, unless a subnet is manually created with a value bigger than one previously used. For example, if subnet4-add is called five times, each without an ID, Kea will assign IDs 1, 2, 3, 4, and 5 and it will work just fine. However, if subnet4-add is called five times, with the first subnet having the subnet-id of value 3 and the remaining ones having no subnet-id, the operation will fail. The first command (with the explicit value) will use subnet-id 3; the second command will create a subnet with id of 1; the third will use a value of 2; and finally the fourth will have the subnet-id value auto-generated as 3. However, since there is already a subnet with that ID, the process will fail. The general recommendation is either never use explicit values, so the auto-generated values will always work; or always use explicit values, so the auto-generation is never used. The two approaches can be mixed only if the administrator understands how internal automatic subnet-id generation works in Kea. .. note:: Subnet IDs must be greater than zero and less than 4294967295. .. _command-subnet4-update: The subnet4-update Command ~~~~~~~~~~~~~~~~~~~~~~~~~~ This command is used to update a subnet in the existing server configuration. This operation has no impact on other subnets. The subnet identifier is used to identify the subnet to replace; it must be specified and must be unique among all subnets. The subnet prefix should not be updated. The subnet information within this command has the same structure as the subnet information in the server configuration file, with the exception that static host reservations must not be specified within ``subnet4-update``. The commands described in :ref:`host-cmds` should be used to update, remove, and modify static reservations. :: { "command": "subnet4-update", "arguments": { "subnet4": [ { "id": 123, "subnet": "10.20.30.0/24", ... } ] } } The response to this command has the following structure: :: { "result": 0, "text": "IPv4 subnet updated", "arguments": { "subnet4": [ { "id": 123, "subnet": "10.20.30.0/24" } ] } } .. _command-subnet6-update: The subnet6-update Command ~~~~~~~~~~~~~~~~~~~~~~~~~~ This command is used to update a subnet in the existing server configuration. This operation has no impact on other subnets. The subnet identifier is used to identify the subnet to replace; it must be specified and must be unique among all subnets. The subnet prefix should not be updated. The subnet information within this command has the same structure as the subnet information in the server configuration file, with the exception that static host reservations must not be specified within ``subnet6-update``. The commands described in :ref:`host-cmds` should be used to update, remove, and modify static reservations. :: { "command": "subnet6-update", "arguments": { "subnet6": [ { "id": 234, "subnet": "2001:db8:1::/64", ... } ] } } The response to this command has the following structure: :: { "result": 0, "text": "IPv6 subnet updated", "arguments": { "subnet6": [ { "id": 234, "subnet": "2001:db8:1::/64" } ] } } .. _command-subnet4-del: The subnet4-del Command ~~~~~~~~~~~~~~~~~~~~~~~ This command is used to remove a subnet from the server's configuration. This command has no effect on other configured subnets, but removing a subnet has certain implications which the server's administrator should be aware of. In most cases the server has assigned some leases to the clients belonging to the subnet. The server may also be configured with static host reservations which are associated with this subnet. The current implementation of the ``subnet4-del`` command removes neither the leases nor the host reservations associated with a subnet. This is the safest approach because the server does not lose track of leases assigned to the clients from this subnet. However, removal of the subnet may still cause configuration errors and conflicts. For example: after removal of the subnet, the server administrator may update a new subnet with the ID used previously for the removed subnet. This means that the existing leases and static reservations will be in conflict with this new subnet. Thus, we recommend that this command be used with extreme caution. This command can also be used to completely delete an IPv4 subnet that is part of a shared network. To simply remove the subnet from a shared network and keep the subnet configuration, use the ``network4-subnet-del`` command instead. The command has the following structure: :: { "command": "subnet4-del", "arguments": { "id": 123 } } The example successful response may look like this: :: { "result": 0, "text": "IPv4 subnet 192.0.2.0/24 (id 123) deleted", "arguments": { "subnets": [ { "id": 123, "subnet": "192.0.2.0/24" } ] } } .. _command-subnet6-del: The subnet6-del Command ~~~~~~~~~~~~~~~~~~~~~~~ This command is used to remove a subnet from the server's configuration. This command has no effect on other configured subnets, but removing a subnet has certain implications which the server's administrator should be aware of. In most cases the server has assigned some leases to the clients belonging to the subnet. The server may also be configured with static host reservations which are associated with this subnet. The current implementation of the ``subnet6-del`` command removes neither the leases nor the host reservations associated with a subnet. This is the safest approach because the server does not lose track of leases assigned to the clients from this subnet. However, removal of the subnet may still cause configuration errors and conflicts. For example: after removal of the subnet, the server administrator may add a new subnet with the ID used previously for the removed subnet. This means that the existing leases and static reservations will be in conflict with this new subnet. Thus, we recommend that this command be used with extreme caution. This command can also be used to completely delete an IPv6 subnet that is part of a shared network. To simply remove the subnet from a shared network and keep the subnet configuration, use the ``network6-subnet-del`` command instead. The command has the following structure: :: { "command": "subnet6-del", "arguments": { "id": 234 } } The example successful response may look like this: :: { "result": 0, "text": "IPv6 subnet 2001:db8:1::/64 (id 234) deleted", "subnets": [ { "id": 234, "subnet": "2001:db8:1::/64" } ] } .. _command-network4-list: .. _command-network6-list: The network4-list, network6-list Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to retrieve the full list of currently configured shared networks. The list contains only very basic information about each shared network. If more details are needed, please use ``network4-get`` or ``network6-get`` to retrieve all information available. This command does not require any parameters and its invocation is very simple: :: { "command": "network4-list" } An example response for ``network4-list`` looks as follows: :: { "arguments": { "shared-networks": [ { "name": "floor1" }, { "name": "office" } ] }, "result": 0, "text": "2 IPv4 network(s) found" } ``network6-list`` follows exactly the same syntax for both the query and the response. .. _command-network4-get: .. _command-network6-get: The network4-get, network6-get Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to retrieve detailed information about shared networks, including subnets that are currently part of a given network. Both commands take one mandatory parameter, ``name``, which specifies the name of the shared network. An example command to retrieve details about an IPv4 shared network with the name "floor13" looks as follows: :: { "command": "network4-get", "arguments": { "name": "floor13" } } An example response could look as follows: :: { "result": 0, "text": "Info about IPv4 shared network 'floor13' returned", "arguments": { "shared-networks": [ { "match-client-id": true, "name": "floor13", "option-data": [ ], "rebind-timer": 90, "relay": { "ip-address": "0.0.0.0" }, "renew-timer": 60, # "reservation-mode": "all", # It is replaced by the "reservations-global" # "reservations-in-subnet" and "reservations-out-of-pool" # parameters. # Specify if the server should lookup global reservations. "reservations-global": false, # Specify if the server should lookup in-subnet reservations. "reservations-in-subnet": true, # Specify if the server can assume that all reserved addresses # are out-of-pool. "reservations-out-of-pool": false, "subnet4": [ { "subnet": "192.0.2.0/24", "id": 5, # many other subnet-specific details here }, { "id": 6, "subnet": "192.0.3.0/31", # many other subnet-specific details here } ], "valid-lifetime": 120 } ] } } Note that the actual response contains many additional fields that are omitted here for clarity. The response format is exactly the same as used in ``config-get``, just limited to returning the shared network's information. .. _command-network4-add: .. _command-network6-add: The network4-add, network6-add Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to add a new shared network, which must have a unique name. This command requires one parameter, ``shared-networks``, which is a list and should contain exactly one entry that defines the network. The only mandatory element for a network is its name. Although it does not make operational sense, it is possible to add an empty shared network that does not have any subnets in it. That is allowed for testing purposes, but having empty networks (or with only one subnet) is discouraged in production environments. For details regarding syntax, see :ref:`shared-network4` and :ref:`shared-network6`. .. note:: As opposed to parameter inheritance during the processing of a full new configuration, this command does not fully handle parameter inheritance. Any missing parameters will be filled with default values, rather than inherited from the global scope. An example that showcases how to add a new IPv4 shared network looks as follows: :: { "command": "network4-add", "arguments": { "shared-networks": [ { "name": "floor13", "subnet4": [ { "id": 100, "pools": [ { "pool": "192.0.2.2-192.0.2.99" } ], "subnet": "192.0.2.0/24", "option-data": [ { "name": "routers", "data": "192.0.2.1" } ] }, { "id": 101, "pools": [ { "pool": "192.0.3.2-192.0.3.99" } ], "subnet": "192.0.3.0/24", "option-data": [ { "name": "routers", "data": "192.0.3.1" } ] } ] } ] } } Assuming there was no shared network with a name "floor13" and no subnets with IDs 100 and 101 previously configured, the command will be successful and will return the following response: :: { "arguments": { "shared-networks": [ { "name": "floor13" } ] }, "result": 0, "text": "A new IPv4 shared network 'floor13' added" } The ``network6-add`` command uses the same syntax for both the query and the response. However, there are some parameters that are IPv4-only (e.g. match-client-id) and some that are IPv6-only (e.g. interface-id). The same applies to subnets within the network. .. _command-network4-del: .. _command-network6-del: The network4-del, network6-del Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to delete existing shared networks. Both commands take exactly one parameter, ``name``, that specifies the name of the network to be removed. An example invocation of the ``network4-del`` command looks as follows: :: { "command": "network4-del", "arguments": { "name": "floor13" } } Assuming there was such a network configured, the response will look similar to the following: :: { "arguments": { "shared-networks": [ { "name": "floor13" } ] }, "result": 0, "text": "IPv4 shared network 'floor13' deleted" } The ``network6-del`` command uses exactly the same syntax for both the command and the response. If there are any subnets belonging to the shared network being deleted, they will be demoted to a plain subnet. There is an optional parameter called ``subnets-action`` that, if specified, takes one of two possible values: ``keep`` (which is the default) and ``delete``. It controls whether the subnets are demoted to plain subnets or removed. An example usage in the ``network6-del`` command that deletes the shared network and all subnets in it could look as follows: :: { "command": "network4-del", "arguments": { "name": "floor13", "subnets-action": "delete" } } Alternatively, to completely remove the subnets, it is possible to use the ``subnet4-del`` or ``subnet6-del`` commands. .. _command-network4-subnet-add: .. _command-network6-subnet-add: The network4-subnet-add, network6-subnet-add Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to add existing subnets to existing shared networks. There are several ways to add a new shared network. The system administrator can add the whole shared network at once, either by editing a configuration file or by calling the ``network4-add`` or ``network6-add`` command with the desired subnets in it. This approach works better for completely new shared subnets. However, there may be cases when an existing subnet is running out of addresses and needs to be extended with additional address space; in other words, another subnet needs to be added on top of it. For this scenario, a system administrator can use ``network4-add`` or ``network6-add``, and then add an existing subnet to this newly created shared network using ``network4-subnet-add`` or ``network6-subnet-add``. The ``network4-subnet-add`` and ``network6-subnet-add`` commands take two parameters: ``id``, which is an integer and specifies the subnet-id of an existing subnet to be added to a shared network; and ``name``, which specifies the name of the shared network to which the subnet will be added. The subnet must not belong to any existing network; to reassign a subnet from one shared network to another, please use the ``network4-subnet-del`` or ``network6-subnet-del`` commands first. An example invocation of the ``network4-subnet-add`` command looks as follows: :: { "command": "network4-subnet-add", "arguments": { "name": "floor13", "id": 5 } } Assuming there is a network named "floor13", and there is a subnet with subnet-id 5 that is not a part of existing network, the command will return a response similar to the following: :: { "result": 0, "text": "IPv4 subnet 10.0.0.0/8 (id 5) is now part of shared network 'floor13'" } The ``network6-subnet-add`` command uses exactly the same syntax for both the command and the response. .. note:: As opposed to parameter inheritance during the processing of a full new configuration or when adding a new shared network with new subnets, this command does not fully handle parameter inheritance. Any missing parameters will be filled with default values, rather than inherited from the global scope or from the shared network. .. _command-network4-subnet-del: .. _command-network6-subnet-del: The network4-subnet-del, network6-subnet-del Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to remove a subnet that is part of an existing shared network and demote it to a plain, stand-alone subnet. To remove a subnet completely, use the ``subnet4-del`` or ``subnet6-del`` commands instead. The ``network4-subnet-del`` and ``network6-subnet-del`` commands take two parameters: ``id``, which is an integer and specifies the subnet-id of an existing subnet to be removed from a shared network; and ``name``, which specifies the name of the shared network from which the subnet will be removed. An example invocation of the ``network4-subnet-del`` command looks as follows: :: { "command": "network4-subnet-del", "arguments": { "name": "floor13", "id": 5 } } Assuming there was a subnet with subnet-id equal to 5, that was part of a shared network named "floor13", the response would look similar to the following: :: { "result": 0, "text": "IPv4 subnet 10.0.0.0/8 (id 5) is now removed from shared network 'floor13'" } The ``network6-subnet-del`` command uses exactly the same syntax for both the command and the response. .. include:: hooks-bootp.rst .. include:: hooks-class-cmds.rst .. include:: hooks-cb-cmds.rst .. include:: hooks-ha.rst .. include:: hooks-stat-cmds.rst .. include:: hooks-radius.rst .. include:: hooks-host-cache.rst .. include:: hooks-lease-query.rst .. include:: hooks-run-script.rst .. _user-context-hooks: User Contexts in Hooks ====================== Hooks libraries can have their own configuration parameters, which is convenient if the parameter applies to the whole library. However, sometimes it is very useful to extend certain configuration entities with additional configuration data. This is where the concept of user contexts comes in. A system administrator can define an arbitrary set of data and attach it to Kea structures, as long as the data are specified as a JSON map. In particular, it is possible to define fields that are integers, strings, boolean, lists, or maps. It is possible to define nested structures of arbitrary complexity. Kea does not use that data on its own; it simply stores it and makes it available for the hooks libraries. Another use case for user contexts may be storing comments and other information that will be retained by Kea. Regular comments are discarded when the configuration is loaded, but user contexts are retained. This is useful if administrators want their comments to survive config-set or config-get operations, for example. If user context is supported in a given context, the parser translates "comment" entries into user context with a "comment" entry. The pretty print of a configuration did the opposite operation and put "comment" entries at the beginning of maps, but this was withdrawn in 1.7.9. As of Kea 1.3, the structures that allow user contexts are pools of all types (addresses and prefixes) and subnets. Kea 1.4 extended user context support to the global scope, interfaces configuration, shared networks, subnets, client classes, option data and definitions, host reservations, control socket, dhcp ddns, loggers and server ID. These are supported in both DHCPv4 and DHCPv6, with the exception of server ID which is DHCPv6 only. kea-2.0.2/doc/sphinx/arm/quickstart.rst0000644000175000017500000001227714206773363015001 00000000000000.. _quickstart: *********** Quick Start *********** This section describes the basic steps needed to get Kea up and running. For further details, full customizations, and troubleshooting, see the respective chapters elsewhere in this Kea Administrator Reference Manual (ARM). .. _quick-start-tarball: Quick Start Guide Using tarball =============================== 1. Install required run-time and build dependencies. See :ref:`build-requirements` for details. 2. Download the Kea source tarball from the `ISC.org downloads page `__ or the `ISC downloads site `__. 3. Extract the tarball. For example: .. parsed-literal:: $ tar -xvzf kea-|release|.tar.gz 4. Go into the source directory and run the configure script: .. parsed-literal:: $ cd kea-|release| $ ./configure [your extra parameters] 5. Build it: .. code-block:: console $ make 6. Install it (by default it will be placed in ``/usr/local/``, so root privileges are likely required for this step): .. code-block:: console $ make install .. _quick-start-repo: Quick Start Guide Using Native Packages ======================================= As of Kea 1.6.0, ISC provides native RPM, deb, and APK packages, which make Kea installation much easier. Unless specific compilation options are desired, it is usually easier to install Kea using native packages. 1. Go to `Kea on cloudsmith.io `__, choose the Kea version, and enter the repository. 2. Use ``Set Me Up`` and follow instructions to add the repository to the local system. 3. Update system repositories. For example: .. code-block:: console $ apt-get update 4. Kea is split into various packages. The entire list is available on `cloudsmith.io `__ or using apt/yum/dnf. For example: .. code-block:: console $ apt-cache search isc-kea 5. Install specific packages: .. code-block:: console $ sudo apt-get install isc-kea-dhcp6-server or all packages: .. code-block:: console $ sudo apt-get install isc-kea* or all packages with a specified version number: .. code-block:: console $ sudo apt-get install isc-kea*=1.8.1-isc0000920201106154401 6. All installed packages should be now available directly; for example: .. code-block:: console # kea-dhcp6 -c /path/to/your/kea6/config/file.json or using systemd: .. code-block:: console # systemctl restart isc-kea-dhcp6-server ``keactrl`` is not available in packages as similar functionality is provided by the native systemctl scripts. .. _quick-start-services: Quick Start Guide for DHCPv4 and DHCPv6 Services ================================================ 1. Edit the Kea configuration files, which by default are installed in the ``[kea-install-dir]/etc/kea/`` directory. These are: ``kea-dhcp4.conf``, ``kea-dhcp6.conf``, ``kea-dhcp-ddns.conf`` and ``kea-ctrl-agent.conf``, ``keactrl.conf`` for DHCPv4 server, DHCPv6 server, D2, Control Agent, and the keactrl script, respectively. 2. To start the DHCPv4 server in the background, run the following command (as root): .. code-block:: console # keactrl start -s dhcp4 Or run the following command to start the DHCPv6 server: .. code-block:: console # keactrl start -s dhcp6 Note that it is also possible to start all servers simultaneously: .. code-block:: console # keactrl start 3. Verify that the Kea server(s) is/are running: .. code-block:: console # keactrl status A server status of "inactive" may indicate a configuration error. Please check the log file (by default named ``[kea-install-dir]/var/log/kea-dhcp4.log``, ``[kea-install-dir]/var/log/kea-dhcp6.log``, ``[kea-install-dir]/var/log/kea-ddns.log``, or ``[kea-install-dir]/var/log/kea-ctrl-agent.log``) for the details of any errors. 4. If the server has started successfully, test that it is responding to DHCP queries and that the client receives a configuration from the server; for example, use the `ISC DHCP client `__. 5. To stop running the server(s): .. code-block:: console # keactrl stop For system-specific instructions, please read the `system-specific notes `__, available in the Kea section of `ISC's Knowledgebase `__. The details of ``keactrl`` script usage can be found in :ref:`keactrl`. Once Kea services are up and running, consider deploying a dashboard solution to monitor running services. For more details, see :ref:`stork`. .. _quick-start-direct-run: Running the Kea Servers Directly ================================ The Kea servers can be started directly, without the need to use ``keactrl`` or ``systemctl``. To start the DHCPv4 server run the following command: .. code-block:: console # kea-dhcp4 -c /path/to/your/kea4/config/file.json Similarly, to start the DHCPv6 server, run the following command: .. code-block:: console # kea-dhcp6 -c /path/to/your/kea6/config/file.json kea-2.0.2/doc/sphinx/arm/lfc.rst0000644000175000017500000000543114206773363013345 00000000000000.. _kea-lfc: *************** The LFC Process *************** .. _kea-lfc-overview: Overview ======== ``kea-lfc`` is a service process that removes redundant information from the files used to provide persistent storage for the Memfile database backend. This service is written to run as a standalone process. While ``kea-lfc`` can be started externally, there is usually no need to do so. ``kea-lfc`` is run on a periodic basis by the Kea DHCP servers. The process operates on a set of files, using them to receive input and output of the lease entries and to indicate what stage the process is in, in the event of an interruption. Currently the caller must supply names for all of the files. .. _kea-lfc-usage: Command-Line Options ==================== ``kea-lfc`` is run as follows: :: kea-lfc [-4 | -6] -c config-file -p pid-file -x previous-file -i copy-file -o output-file -f finish-file The argument ``-4`` or ``-6`` selects the protocol version of the lease files. The ``-c`` argument specifies the configuration file. This is required, but is not currently used by the process. The ``-p`` argument specifies the PID file. When the ``kea-lfc`` process starts, it attempts to determine whether another instance of the process is already running by examining the PID file. If one is already running, the new process is terminated; if one is not running, Kea writes its PID into the PID file. The other filenames specify where the ``kea-lfc`` process should look for input, write its output, and perform its bookkeeping: - ``previous`` — when ``kea-lfc`` starts, this is the result of any previous run of ``kea-lfc``. When ``kea-lfc`` finishes, it is the result of this run. If ``kea-lfc`` is interrupted before completing, this file may not exist. - ``input`` — before the DHCP server invokes ``kea-lfc``, it moves the current lease file here and then calls ``kea-lfc`` with this file. - ``output`` — This is the temporary file where ``kea-lfc`` writes the leases. Once the file has finished writing, it will be moved to the finish file (see below). - ``finish`` — This is another temporary file ``kea-lfc`` uses for bookkeeping. When ``kea-lfc`` completes writing the output file, it moves the output to this file name. After ``kea-lfc`` finishes deleting the other files (previous and input), it moves this file to the previous lease file. By moving the files in this fashion, the ``kea-lfc`` and the DHCP server processes can determine the correct file to use even if one of the processes were interrupted before completing its task. There are several additional arguments, mostly for debugging purposes. ``-d`` sets the logging level to debug. ``-v`` and ``-V`` print out version stamps, with ``-V`` providing a longer form. ``-h`` prints out the usage string. kea-2.0.2/doc/sphinx/arm/hooks-cb-cmds.rst0000644000175000017500000020503014206773363015227 00000000000000.. _cb-cmds-library: cb_cmds: Configuration Backend Commands ======================================= This section describes the ``cb_cmds`` hooks library, used to manage Kea servers' configurations in the Configuration Backends. This library must be used in conjunction with the available CB hooks libraries implementing the common APIs to create, read, update, and delete (CRUD) the configuration information in the respective databases. For example: the ``mysql_cb`` hooks library implements this API for MySQL. In order to manage the configuration information in the MySQL database, both the ``mysql_cb`` and ``cb_cmds`` libraries must be loaded by the server used for the configuration management. The ``cb_cmds`` library is only available to ISC customers with a paid support contract. .. note:: This library may only be loaded by the ``kea-dhcp4`` or ``kea-dhcp6`` process. .. note:: Please read about :ref:`cb-limitations` before using the commands described in this section. Commands Structure ~~~~~~~~~~~~~~~~~~ There are 5 types of commands supported by this library: - ``del`` - delete the selected object from the database, e.g. ``remote-global-parameter4-del``. - ``get`` - fetch the selected object from the database, e.g. ``remote-subnet4-get``. - ``get-all`` - fetch all objects of the particular type from the database, e.g. ``remote-option-def4-get-all``. - ``list`` - list all objects of the particular type in the database, e.g. ``remote-network4-list``; this class of commands returns brief information about each object compared to the output of ``get-all``. - ``set`` - creates or replaces an object of the given type in the database, e.g. ``remote-option4-global-set``. All types of commands accept an optional ``remote`` map which selects the database instance to which the command refers. For example: .. code-block:: json { "command": "remote-subnet4-list", "arguments": { "remote": { "type": "mysql", "host": "192.0.2.33", "port": 3302 } } } selects the MySQL database, running on host 192.0.2.33 and port 3302, to fetch the list of subnets from. All parameters in the ``remote`` argument are optional. The ``port`` parameter can be only specified in conjunction with the ``host``. If no options in the ``remote`` parameter are to be specified, the parameter should be omitted. In this case, the server will use the first backend listed in the ``config-control`` map within the configuration of the server receiving the command. .. note:: In the present Kea release, it is possible to configure the Kea server to use only one configuration backend. Strictly speaking, it is possible to point the Kea server to at most one MySQL database using the ``config-control`` parameter. That's why the ``remote`` parameter may be omitted in the commands and the cb_cmds hooks library will use the sole backend by default. .. _cb-cmds-dhcp: Control Commands for DHCP Servers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This section describes and gives some examples of the control commands implemented by the ``cb_cmds`` hooks library, to manage the configuration information of the DHCPv4 and DHCPv6 servers. Many of the commands are almost identical between DHCPv4 and DHCPv6; they only differ by the command name. Other commands differ slightly by the structure of the inserted data; for example, the structure of the IPv4 subnet information is different than that of the IPv6 subnet. Nevertheless, they still share the structure of their command arguments and thus it makes sense to describe them together. In the following sections, various commands are described and some usage examples are provided. In the sections jointly describing the DHCPv4 and DHCPv6 variants of the particular command, we sometimes use the following notation: the ``remote-subnet[46]-set`` is the wildcard name for the two commands: ``remote-subnet4-set`` and ``remote-subnet6-set``. In addition, whenever the text in the subsequent sections refers to a DHCP command or DHCP parameter, it refers to both DHCPv4 and DHCPv6 variants. The text specific to the particular server type refers to them as: DHCPv4 command, DHCPv4 parameter, DHCPv6 command, DHCPv6 parameter, etc. .. _cb-cmds-metadata: Metadata ~~~~~~~~ The typical response to the ``get`` or ``list`` command includes a list of returned objects (e.g. subnets), and each such object contains the ``metadata`` map with some database-specific information describing this object. In other words, the metadata contains any information about the fetched object which may be useful for an administrator but which is not part of the object specification from the DHCP server standpoint. In the present Kea release, the metadata is limited to the ``server-tag``. It describes the association of the object with a particular server or all servers. The following is the example response to the ``remote-network4-list`` command, which includes the metadata: .. code-block:: json { "result": 0, "text": "1 IPv4 shared network(s) found.", "arguments": { "shared-networks": [ { "name": "level3", "metadata": { "server-tags": [ "all" ] } } ], "count": 1 } } Client implementations must not assume that the metadata contains only the ``server-tags`` parameter. In future releases, this map will be extended with additional information, e.g. object modification time, log message created during the last modification, etc. .. _command-remote-server4-del: .. _command-remote-server6-del: remote-server4-del, remote-server6-del commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command is used to delete the information about a selected DHCP server from the configuration database. The server is identified by a unique case insensitive server tag. For example: .. code-block:: json { "command": "remote-server4-del", "arguments": { "servers": [ { "server-tag": "server1" } ], "remote": { "type": "mysql" } } } As a result of this command, the user defined server called `server1` is removed from the database. All associations of the configuration information with this server are automatically removed from the database. The non-shareable configuration information, such as: global parameters, option definitions and global options associated with the server are removed from the database. The shareable configuration information, i.e. the configuration elements which may be associated with more than one server, is preserved. In particular, the subnets and shared networks associated with the deleted servers are preserved. If any of the shareable configuration elements was associated only with the deleted server, this object becomes unassigned (orphaned). For example: if a subnet has been created and associated with the `server1` using the `remote-subnet4-set` command and the server1 is subsequently deleted, the subnet remains in the database but none of the servers can use this subnet. The subnet can be updated using the `remote-subnet4-set` and associated with some other server or with all servers using the special server tag "all". Such subnet can be also deleted from the database using the `remote-subnet4-del-by-id` or `remote-subnet4-del-by-prefix`, if it is no longer needed. The following is the successful response to the `remote-server4-del` command: .. code-block:: json { "result": 0, "text": "1 DHCPv4 server(s) deleted." "arguments": { "count": 1 } } .. note:: The `remote-server4-del` and `remote-server6-del` commands must be used with care, because an accidental deletion of the server causes some parts of the existing configurations to be lost permanently from the database. This operation is not reversible. Re-creation of the accidentally deleted server does not revert the lost configuration for that server and such configuration must be re-created manually by the user. .. _command-remote-server4-get: .. _command-remote-server6-get: remote-server4-get, remote-server6-get commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command is used to fetch the information about the selected DHCP server from the configuration database. For example: .. code-block:: json { "command": "remote-server6-get" "arguments": { "servers": [ { "server-tag": "server1" } ], "remote": { "type": "mysql" } } } This command fetches the information about the DHCPv6 server identified by the server tag `server1`. The server tag is case insensitive. A successful response returns basic information about the server, such as server tag and the user's description of the server: .. code-block:: json { "result": 0, "text": "DHCP server server1 found.", "arguments": { "servers": [ { "server-tag": "server1", "description": "A DHCPv6 server located on the first floor." } ], "count": 1 } } .. _command-remote-server4-get-all: .. _command-remote-server6-get-all: remote-server4-get-all, remote-server6-get-all commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command is used to fetch all user defined DHCPv4 or DHCPv6 servers from the database. The command structure is very simple: .. code-block:: json { "command": "remote-server4-get-all" "arguments": { "remote": { "type": "mysql" } } } The response includes basic information about each server, such as its server tag and description: .. code-block:: json { "result": 0, "text": "DHCPv4 servers found.", "arguments": { "servers": [ { "server-tag": "server1", "description": "A DHCP server located on the first floor." }, { "server-tag": "server2", "description": "An old DHCP server to be soon replaced." } ], "count": 2 } } .. _command-remote-server4-set: .. _command-remote-server6-set: remote-server4-set, remote-server6-set commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command is used to create or replace an information about a DHCP server in the database. The information about the server must be created when there is a need to differentiate the configurations used by various Kea instances connecting to the same database. Various configuration elements, e.g. global parameters, subnets etc. may be explicitly associated with the selected servers (using server tags as identifiers), allowing only these servers to use the respective configuration elements. Using the particular server tag to make such associations is only possible when the server information has been stored in the database via the `remote-server4-set` or `remote-server6-set` commands. The following command creates a new (or updates an existing) DHCPv6 server in the database: .. code-block:: json { "command": "remote-server6-set" "arguments": { "servers": [ { "server-tag": "server1", "description": "A DHCP server on the ground floor." } ], "remote": { "type": "mysql" } } } The server tag must be unique across all servers in the database. When the server information under the given server tag already exists, it is replaced with the new information. The specified server tag is case-insensitive, and the maximum length of the server tag is 256 characters. The following keywords are reserved and must not be used as server tags: "all" and "any". The following is the example response to the above command: .. code-block:: json { "result": 0, "text": "DHCPv6 server successfully set.", "arguments": { "servers": [ { "server-tag": "server1", "description": "A DHCP server on the ground floor." } ] } } .. _command-remote-global-parameter4-del: .. _command-remote-global-parameter6-del: The remote-global-parameter4-del, remote-global-parameter6-del Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to delete a global DHCP parameter from the configuration database. When the parameter is deleted from the database, the server will use the value specified in the configuration file for this parameter, or a default value if the parameter is not specified in the configuration file. The following command attempts to delete the DHCPv4 ``renew-timer`` parameter common for all servers from the database: .. code-block:: json { "command": "remote-global-parameter4-del", "arguments": { "parameters": [ "renew-timer" ], "remote": { "type": "mysql" }, "server-tags": [ "all" ] } } If the server specific parameter is to be deleted, the `server-tags` list must contain the tag of the appropriate server. There must be exactly one server tag specified in this list. .. _command-remote-global-parameter4-get: .. _command-remote-global-parameter6-get: The remote-global-parameter4-get, remote-global-parameter6-get Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to fetch a scalar global DHCP parameter from the configuration database. The following command attempts to fetch the ``boot-file-name`` parameter for the "server1": .. code-block:: json { "command": "remote-global-parameter4-get", "arguments": { "parameters": [ "boot-file-name" ], "remote": { "type": "mysql" }, "server-tags": [ "server1" ] } } The returned value has one of the four scalar types: string, integer, real, or boolean. Non-scalar global configuration parameters, such as map or list, are not returned by this command. In the case of the example above, the string value is returned, e.g.: .. code-block:: json { "result": 0, "text": "1 DHCPv4 global parameter found.", "arguments": { "parameters": { "boot-file-name": "/dev/null", "metadata": { "server-tags": [ "all" ] } }, "count": 1 } } Note that the response above indicates that the returned parameter is associated with "all" servers rather than "server1" used in the command. This indicates that there is no server1 specific value in the database. Therefore, the value shared by all servers is returned. If there was the server1 specific value in the database this value would be returned instead. The example response for the integer value is: .. code-block:: json { "result": 0, "text": "1 DHCPv4 global parameter found.", "arguments": { "parameters": { "renew-timer": 2000, "metadata": { "server-tags": [ "server1" ] } }, "count": 1 } } The real value: .. code-block:: json { "result": 0, "text": "1 DHCPv4 global parameter found.", "arguments": { "parameters": { "t1-percent": 0.85, "metadata": { "server-tags": [ "all" ] } }, "count": 1 } } Finally, the boolean value: .. code-block:: json { "result": 0, "text": "1 DHCPv4 global parameter found.", "arguments": { "parameters": { "match-client-id": true, "metadata": { "server-tags": [ "server2" ] } }, "count": 1 } } .. _command-remote-global-parameter4-get-all: .. _command-remote-global-parameter6-get-all: The remote-global-parameter4-get-all, remote-global-parameter6-get-all Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to fetch all global DHCP parameters from the database for the specified server. The following example demonstrates how to fetch all global parameters to be used by the server "server1": .. code-block:: json { "command": "remote-global-parameter4-get-all", "arguments": { "remote": { "type": "mysql" }, "server-tags": [ "server1" ] } } The example response may look as follows: .. code-block:: json { "result": 0, "text": "DHCPv4 global parameters found.", "arguments": { "parameters": [ { "boot-file-name": "/dev/null", "metadata": { "server-tags": [ "server1" ] } }, { "match-client-id": true, "metadata": { "server-tags": [ "all" ] } } ], "count": 2 } } The example response contains two parameters, one string parameter and one boolean parameter. The metadata returned for each parameter indicates if this parameter is specific to the "server1" or all servers. Since the `match-client-id` value is associated with "all" servers it indicates that there is no server1 specific setting for this parameter. Each parameter always has exactly one server tag associated with it, because the global parameters are non-shareable configuration elements. .. note:: If the server tag is set to "all" in the command, the response will contain only the global parameters associated with the logical server "all". When the server tag points to the specific server (as in the example above), the returned list combines parameters associated with this server and all servers, but the former take precedence. .. _command-remote-global-parameter4-set: .. _command-remote-global-parameter6-set: The remote-global-parameter4-set, remote-global-parameter6-set Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command is used to create scalar global DHCP parameters in the database. If any of the parameters already exists, its value is replaced as a result of this command. It is possible to set multiple parameters within a single command, each having one of the four types: string, integer, real, or boolean. For example: .. code-block:: json { "command": "remote-global-parameter4-set" "arguments": { "parameters": { "boot-file-name": "/dev/null", "renew-timer": 2000, "t1-percent": 0.85, "match-client-id": true }, "remote": { "type": "mysql" }, "server-tags": [ "server1" ] } } An error is returned if any of the parameters is not supported by the DHCP server or its type does not match. Care should be taken when multiple parameters are specified in a single command, because it is possible that only some of the parameters are stored successfully and some fail. If an error occurs when processing this command, it is recommended to use ``remote-global-parameter[46]-get-all`` to check which of the parameters have been stored/updated successfully and which have failed. The `server-tags` list is mandatory and it must contain a single server tag or the keyword "all". In the example above, all specified parameters are associated with the "server1" server. .. _command-remote-network4-del: .. _command-remote-network6-del: The remote-network4-del, remote-network6-del Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to delete an IPv4 or IPv6 shared network from the database. The optional parameter ``subnets-action`` determines whether the subnets belonging to the deleted shared network should also be deleted or preserved. The ``subnets-action`` parameter defaults to ``keep``, which preserves the subnets. If it is set to ``delete``, the subnets are deleted along with the shared network. The following command: .. code-block:: json { "command": "remote-network6-del", "arguments": { "shared-networks": [ { "name": "level3" } ], "subnets-action": "keep", "remote": { "type": "mysql" } } } deletes the "level3" IPv6 shared network. The subnets are preserved, but they are disassociated from the deleted shared network and become global. This behavior corresponds to the behavior of the ``network[46]-del`` commands with respect to the ``subnets-action`` parameter. Note that the `server-tags` parameter must not be used for this command. .. _command-remote-network4-get: .. _command-remote-network6-get: The remote-network4-get, remote-network6-get Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to retrieve information about an IPv4 or IPv6 shared network. The optional parameter ``subnets-include`` denotes whether the subnets belonging to the shared network should also be returned. This parameter defaults to ``no``, in which case the subnets are not returned. If this parameter is set to ``full``, the subnets are returned together with the shared network. The following command fetches the "level3" IPv6 shared network along with the full information about the subnets belonging to it: .. code-block:: json { "command": "remote-network6-get", "arguments": { "shared-networks": [ { "name": "level3" } ], "subnets-include": "full", "remote": { "type": "mysql" } } } Note that the `server-tags` parameter must not be used for this command. .. _command-remote-network4-list: .. _command-remote-network6-list: The remote-network4-list, remote-network6-list Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to list all IPv4 or IPv6 shared networks for a server. The following command retrieves all shared networks to be used by the "server1" and "server2": .. code-block:: json { "command": "remote-network4-list" "arguments": { "remote": { "type": "mysql" }, "server-tags": [ "server1", "server2" ] } } The `server-tags` parameter is mandatory and it contains one or more server tags. It may contain the keyword "all" to fetch the shared networks associated with all servers. When the `server-tags` list contains the `null` value the returned response contains a list of unassigned shared networks, i.e. the networks which are associated with no servers. For example: .. code-block:: json { "command": "remote-network4-list" "arguments": { "remote": { "type": "mysql" }, "server-tags": [ null ] } } The example response to this command when non-null server tags are specified looks similar to this: .. code-block:: json { "result": 0, "text": "3 IPv4 shared network(s) found.", "arguments": { "shared-networks": [ { "name": "ground floor", "metadata": { "server-tags": [ "all" ] } }, { "name": "floor2", "metadata": { "server-tags": [ "server1" ] } }, { "name": "floor3", "metadata": { "server-tags": [ "server2" ] } } ], "count": 3 } } The returned information about each shared network merely contains the shared network name and the metadata. In order to fetch the detailed information about the selected shared network, use the `remote-network[46]-get` command. The example response above contains three shared networks. One of the shared networks is associated with all servers, so it is included in the list of shared networks to be used by "server1" and "server2". The remaining two shared networks are returned because one of them is associated with the "server1" and another one is associated with the "server2". When listing unassigned shared networks, the response will look similar to this: .. code-block:: json { "result": 0, "text": "1 IPv4 shared network(s) found.", "arguments": { "shared-networks": [ { "name": "fancy", "metadata": { "server-tags": [ null ] } } ], "count": 1 } } The `null` value in the metadata indicates that the returned shared network is unassigned. .. _command-remote-network4-set: .. _command-remote-network6-set: The remote-network4-set, remote-network6-set Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands create a new or replace an existing IPv4 or IPv6 shared network in the database. The structure of the shared network information is the same as in the Kea configuration file (see :ref:`shared-network4` and :ref:`shared-network6` for details), except that specifying subnets along with the shared network information is not allowed. Including the ``subnet4`` or ``subnet6`` parameter within the shared network information will result in an error. These commands are intended to be used for managing the shared network-specific information and DHCP options. In order to associate and disassociate the subnets with the shared networks, the ``remote-subnet[46]-set`` commands should be used. The following command adds the IPv6 shared network "level3" to the database: .. code-block:: json { "command": "remote-network6-set", "arguments": { "shared-networks": [ { "name": "level3", "interface": "eth0", "option-data": [ { "name": "sntp-servers", "data": "2001:db8:1::1" } ], } ], "remote": { "type": "mysql" }, "server-tags": [ "all" ] } } This command includes the ``interface`` parameter, which sets the shared network-level interface name. Any remaining shared network-level parameters, which are not specified with the command, will be marked as "unspecified" in the database. The DHCP server will use the global values for unspecified parameters or, if the global values are not specified, the default values will be used. The `server-tags` list is mandatory for this command and it must include one or more server tags. As a result the shared network is associated with all listed servers. The shared network may be associated with all servers connecting to the database when the keyword "all" is included. .. note:: As with other "set" commands, this command replaces all the information about the given shared network in the database, if the shared network already exists. Therefore, when sending this command, make sure to always include all parameters that must be specified for the updated shared-network instance. Any unspecified parameter will be marked unspecified in the database, even if its value was present prior to sending the command. .. _command-remote-option-def4-del: .. _command-remote-option-def6-del: The remote-option-def4-del, remote-option-def6-del Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to delete a DHCP option definition from the database. The option definition is identified by an option code and option space. For example: .. code-block:: json { "command": "remote-option-def6-del", "arguments": { "option-defs": [ { "code": 1, "space": "isc" } ], "remote": { "type": "mysql" }, "server-tags": [ "server1" ] } } deletes the definition of the option associated with the "server1", having the code of 1 and belonging to the option space "isc". The default option spaces are "dhcp4" and "dhcp6" for the DHCPv4 and DHCPv6 top level options respectively. If there is no such option explicitly associated with the server1, no option is deleted. In order to delete an option belonging to "all" servers, the keyword "all" must be used as the server tag. The `server-tags` list must contain exactly one tag. It must not include the `null` value. .. _command-remote-option-def4-get: .. _command-remote-option-def6-get: The remote-option-def4-get, remote-option-def6-get Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to fetch a specified DHCP option definition from the database. The option definition is identified by the option code and option space. The default option spaces are "dhcp4" and "dhcp6" for the DHCPv4 and DHCPv6 top-level options, respectively. The following command retrieves a DHCPv4 option definition associated with all servers, having the code of 1 and belonging to the option space "isc": .. code-block:: json { "command": "remote-option-def4-get" "arguments": { "option-defs": [ { "code": 1, "space": "isc" } ], "remote": { "type": "mysql" }, "server-tags": [ "all" ] } } The `server-tags` list must include exactly one server tag or the keyword "all". It must not contain the `null` value. .. _command-remote-option-def4-get-all: .. _command-remote-option-def6-get-all: The remote-option-def4-get-all, remote-option-def6-get-all Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to fetch all DHCP option definitions from the database for the particular server or all servers. For example: .. code-block:: json { "command": "remote-option-def6-get-all" "arguments": { "remote": { "type": "mysql" }, "server-tags": [ "all" ] } } This command attempts to fetch all DHCPv6 option definitions associated with "all" servers. The `server-tags` list is mandatory for this command and it must include exactly one server tag or the keyword "all". It must not include the `null` value. The following is the example response to this command: .. code-block:: json { "result": 0, "text": "1 DHCPv6 option definition(s) found.", "arguments": { "option-defs": [ { "name": "bar", "code": 1012, "space": "dhcp6", "type": "record", "array": true, "record-types": "ipv6-address, uint16", "encapsulate": "", "metadata": { "server-tags": [ "all" ] } } ], "count": 1 } } The response contains an option definition associated with all servers as indicated by the metadata. .. _command-remote-option-def4-set: .. _command-remote-option-def6-set: The remote-option-def4-set, remote-option-def6-set Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands create a new DHCP option definition or replace an existing option definition in the database. The structure of the option definition information is the same as in the Kea configuration file (see :ref:`dhcp4-custom-options` and :ref:`dhcp6-custom-options`). The following command creates the DHCPv4 option definition at the top-level "dhcp4" option space and associates it with the "server1": .. code-block:: json { "command": "remote-option-def4-set", "arguments": { "option-defs": [ { "name": "foo", "code": 222, "type": "uint32", "array": false, "record-types": "", "space": "dhcp4", "encapsulate": "" } ], "remote": { "type": "mysql" }, "server-tags": [ "server1" ] } } The `server-tags` list must include exactly one server tag or the keyword "all". It must not contain the `null` value. .. _command-remote-option4-global-del: .. _command-remote-option6-global-del: The remote-option4-global-del, remote-option6-global-del Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to delete a global DHCP option from the database. The option is identified by an option code and option space. For example: .. code-block:: json { "command": "remote-option4-global-del", "arguments": { "options": [ { "code": 5 "space": "dhcp4" } ], "remote": { "type": "mysql" }, "server-tags": [ "server1" ] } } The "dhcp4" is the top-level option space where the standard DHCPv4 options belong. The `server-tags` is mandatory and it must include a single option tag or the keyword "all". If the explicit server tag is specified then this command attempts to delete a global option associated with this server. If there is no such option associated with the given server, no option is deleted. In order to delete the option associated with all servers, the keyword "all" must be specified. .. _command-remote-option4-global-get: .. _command-remote-option6-global-get: The remote-option4-global-get, remote-option6-global-get Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to fetch a global DHCP option from the database. The option is identified by the code and option space. The top-level option spaces where DHCP standard options belong are called "dhcp4" and "dhcp6" for the DHCPv4 and DHCPv6 servers, respectively. The following command retrieves the IPv6 "DNS Servers" (code 23) option associated with all servers: .. code-block:: json { "command": "remote-option6-global-get", "arguments": { "options": [ { "code": 23, "space": "dhcp6" } ], "remote": { "type": "mysql" }, "server-tags": [ "all" ] } } The `server-tags` is mandatory and it must include exactly one server tag or the keyword "all". It must not contain the `null` value. .. _command-remote-option4-global-get-all: .. _command-remote-option6-global-get-all: The remote-option4-global-get-all, remote-option6-global-get-all Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to fetch all global DHCP options from the configuration database for the particular server or for all servers. The following command fetches all global DHCPv4 options for the "server1": .. code-block:: json { "command": "remote-option6-global-get-all", "arguments": { "remote": { "type": "mysql" }, "server-tags": [ "server1" ] } } The `server-tags` list is mandatory for this command and it must contain exactly one server tag or a keyword "all". It must not contain the `null` value. The following is the example response to this command with a single option being associated with the "server1" returned: .. code-block:: json { "result": 0, "text": "DHCPv4 options found.", "arguments": { "options": [ { "name": "domain-name-servers", "code": 6, "space": "dhcp4", "csv-format": false, "data": "192.0.2.3", "metadata": { "server-tags": [ "server1" ] } } ], "count": 1 } } .. _command-remote-option4-global-set: .. _command-remote-option6-global-set: The remote-option4-global-set, remote-option6-global-set Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands create a new global DHCP option or replace an existing option in the database. The structure of the option information is the same as in the Kea configuration file (see :ref:`dhcp4-std-options` and :ref:`dhcp6-std-options`). For example: .. code-block:: json { "command": "remote-option6-global-set", "arguments": { "options": [ { "name": "dns-servers", "data": "2001:db8:1::1" } ], "remote": { "type": "mysql" }, "server-tags": [ "server1" ] } } The `server-tags` list is mandatory for this command and it must include exactly one server tag or the keyword "all". It must not include the `null` value. The command above associates the option with the "server1" server. Note that specifying an option name instead of the option code only works reliably for the standard DHCP options. When specifying a value for the user-defined DHCP option, the option code should be specified instead of the name. For example: .. code-block:: json { "command": "remote-option6-global-set", "arguments": { "options": [ { "code": 1, "space": "isc", "data": "2001:db8:1::1" } ], "server-tags": [ "server1" ] } } .. _command-remote-option4-network-del: .. _command-remote-option6-network-del: The remote-option4-network-del, remote-option6-network-del Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to delete a shared network specific DHCP option from the database. The option is identified by an option code and option space and these two parameters are passed within the `options` list. Another list, `shared-networks`, contains a map with the name of the shared network from which the option is to be deleted. If the option is not explicitly specified for this shared network, no option is deleted. In particular, the given option may be present for a subnet belonging to the shared network. Such an option instance is not affected by this command as this command merely deletes the shared network level option. In order to delete a subnet level option the `remote-option[46]-subnet-del` command must be used instead. The following command attempts to delete an option having the option code 5 in the top-level option space from the shared network "fancy". .. code-block:: json { "command": "remote-option4-network-del", "arguments": { "shared-networks": [ { "name": "fancy" } ], "options": [ { "code": 5, "space": "dhcp4" } ], "remote": { "type": "mysql" } } } The "dhcp4" is the top-level option space where the standard DHCPv4 options belong. The `server-tags` parameter must not be specified for this command. .. _command-remote-option4-network-set: .. _command-remote-option6-network-set: The remote-option4-network-set, remote-option6-network-set Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands create a new shared network specific DHCP option or replace an existing option in the database. The structure of the option information is the same as in the Kea configuration file (see :ref:`dhcp4-std-options` and :ref:`dhcp6-std-options`). The option information is carried in the `options` list. Another list, `shared-networks`, contains a map with the name of the shared network for which the option is to be set. If such an option already exists for the shared network, it is replaced with the new instance. .. code-block:: json { "command": "remote-option6-network-set", "arguments": { "shared-networks": [ { "name": "fancy" } ], "options": [ { "name": "dns-servers", "data": "2001:db8:1::1" } ], "remote": { "type": "mysql" } } } The `sever-tags` parameter must not be specified for this command. Specifying an option name instead of the option code only works reliably for the standard DHCP options. When specifying a value for the user-defined DHCP option, the option code should be specified instead of the name. .. _command-remote-option6-pd-pool-del: The remote-option6-pd-pool-del Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command is used to delete a prefix delegation pool specific DHCPv6 option from the database. The option is identified by an option code and option space and these two parameters are passed within the `options` list. Another list, `pd-pools`, contains a map with the prefix delegation pool prefix and length identifying the pool. If the option is not explicitly specified for this pool, no option is deleted. In particular, the given option may exist for a subnet containing the specified pool. Such an option instance is not affected by this command as this command merely deletes a prefix delegation pool level option. In order to delete a subnet level option the `remote-option6-subnet-del` command must be used instead. .. code-block:: json { "command": "remote-option6-pd-pool-del", "arguments": { "pd-pools": [ { "prefix": "3000::", "prefix-len": 64 } ], "options": [ { "code": 23, "space": "dhcp6" } ], "remote": { "type": "mysql" } } } The "dhcp6" is the top-level option space where the standard DHCPv6 options belong. The `server-tags` parameter must not be specified for this command. .. _command-remote-option6-pd-pool-set: The remote-option6-pd-pool-set Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This command creates a new prefix delegation pool-specific DHCPv6 option or replaces an existing option in the database. The structure of the option information is the same as in the Kea configuration file (see :ref:`dhcp4-std-options` and :ref:`dhcp6-std-options`). The option information is carried in the `options` list. Another list, `pd-pools`, contains a map with the prefix delegation pool prefix and the prefix length identifying the pool. If such an option already exists for the prefix delegation pool, it is replaced with the new instance. For example: .. code-block:: json { "command": "remote-option6-pd-pool-set", "arguments": { "pd-pools": [ { "prefix": "3001:1::", "length": 64 } ], "options": [ { "name": "dns-servers", "data": "2001:db8:1::1" } ], "remote": { "type": "mysql" } } } The `sever-tags` parameter must not be specified for this command. Specifying an option name instead of the option code only works reliably for the standard DHCP options. When specifying a value for the user-defined DHCP option, the option code should be specified instead of the name. .. _command-remote-option4-pool-del: .. _command-remote-option6-pool-del: The remote-option4-pool-del, remote-option6-pool-del Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to delete an address pool specific DHCP option from the database. The option is identified by an option code and option space and these two parameters are passed within the `options` list. Another list, `pools`, contains a map with the IP address range or prefix identifying the pool. If the option is not explicitly specified for this pool, no option is deleted. In particular, the given option may exist for a subnet containing the specified pool. Such option instance is not affected by this command as this command merely deletes a pool level option. In order to delete subnet level option the `remote-option[46]-subnet-del` command must be used instead. The following command attempts to delete an option having the option code 5 in the top-level option space from an IPv4 address pool: .. code-block:: json { "command": "remote-option4-pool-del", "arguments": { "pools": [ { "pool": "192.0.2.10 - 192.0.2.100" } ], "options": [ { "code": 5, "space": "dhcp4" } ], "remote": { "type": "mysql" } } } The "dhcp4" is the top-level option space where the standard DHCPv4 options belong. The `server-tags` parameter must not be specified for this command. .. _command-remote-option4-pool-set: .. _command-remote-option6-pool-set: The remote-option4-pool-set, remote-option6-pool-set Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands create a new address pool specific DHCP option or replace an existing option in the database. The structure of the option information is the same as in the Kea configuration file (see :ref:`dhcp4-std-options` and :ref:`dhcp6-std-options`). The option information is carried in the `options` list. Another list, `pools`, contains a map with the IP address range or prefix identifying the pool. If such an option already exists for the pool, it is replaced with the new instance. For example: .. code-block:: json { "command": "remote-option4-pool-set", "arguments": { "pools": [ { "pool": "192.0.2.10 - 192.0.2.100" } ], "options": [ { "name": "domain-name-servers", "data": "10.0.0.1" } ], "remote": { "type": "mysql" } } } The `sever-tags` parameter must not be specified for this command. Specifying an option name instead of the option code only works reliably for the standard DHCP options. When specifying a value for the user-defined DHCP option, the option code should be specified instead of the name. .. _command-remote-option4-subnet-del: .. _command-remote-option6-subnet-del: The remote-option4-subnet-del, remote-option6-subnet-del Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to delete a subnet specific DHCP option from the database. The option is identified by an option code and option space and these two parameters are passed within the `options` list. Another list, `subnets`, contains a map with the identifier of the subnet from which the option is to be deleted. If the option is not explicitly specified for this subnet, no option is deleted. The following command attempts to delete an option having the option code 5 in the top-level option space from the subnet having an identifier of 123. .. code-block:: json { "command": "remote-option4-subnet-del", "arguments": { "subnets": [ { "id": 123 } ], "options": [ { "code": 5, "space": "dhcp4" } ], "remote": { "type": "mysql" } } } The "dhcp4" is the top-level option space where the standard DHCPv4 options belong. The `server-tags` parameter must not be specified for this command. .. _command-remote-option4-subnet-set: .. _command-remote-option6-subnet-set: The remote-option4-subnet-set, remote-option6-subnet-set Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands create a new subnet specific DHCP option or replace an existing option in the database. The structure of the option information is the same as in the Kea configuration file (see :ref:`dhcp4-std-options` and :ref:`dhcp6-std-options`). The option information is carried in the `options` list. Another list, `subnets`, contains a map with the identifier of the subnet for which the option is to be set. If such an option already exists for the subnet, it is replaced with the new instance. .. code-block:: json { "command": "remote-option6-subnet-set", "arguments": { "subnets": [ { "id": 123 } ], "options": [ { "name": "dns-servers", "data": "2001:db8:1::1" } ], "remote": { "type": "mysql" } } } The `sever-tags` parameter must not be specified for this command. Specifying an option name instead of the option code only works reliably for the standard DHCP options. When specifying a value for the user-defined DHCP option, the option code should be specified instead of the name. .. _command-remote-subnet4-del-by-id: .. _command-remote-subnet6-del-by-id: The remote-subnet4-del-by-id, remote-subnet6-del-by-id Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is the first variant of the commands used to delete an IPv4 or IPv6 subnet from the database. It uses the subnet ID to identify the subnet. For example, to delete the IPv4 subnet with an ID of 5: .. code-block:: json { "command": "remote-subnet4-del-by-id", "arguments": { "subnets": [ { "id": 5 } ], "remote": { "type": "mysql" } } } The `server-tags` parameter must not be used with this command. .. _command-remote-subnet4-del-by-prefix: .. _command-remote-subnet6-del-by-prefix: The remote-subnet4-del-by-prefix, remote-subnet6-del-by-prefix Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is the second variant of the commands used to delete an IPv4 or IPv6 subnet from the database. It uses the subnet prefix to identify the subnet. For example: .. code-block:: json { "command": "remote-subnet6-del-by-prefix", "arguments": { "subnets": [ { "subnet": "2001:db8:1::/64" } ], "remote": { "type": "mysql" } } } The `server-tags` parameter must not be used with this command. .. _command-remote-subnet4-get-by-id: .. _command-remote-subnet6-get-by-id: The remote-subnet4-get-by-id, remote-subnet6-get-by-id Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is the first variant of the commands used to fetch an IPv4 or IPv6 subnet from the database. It uses a subnet ID to identify the subnet. For example: .. code-block:: json { "command": "remote-subnet4-get-by-id", "arguments": { "subnets": [ { "id": 5 } ], "remote": { "type": "mysql" } } } The `server-tags` parameter must not be used with this command. .. _command-remote-subnet4-get-by-prefix: .. _command-remote-subnet6-get-by-prefix: The remote-subnet4-get-by-prefix, remote-subnet6-get-by-prefix Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is the second variant of the commands used to fetch an IPv4 or IPv6 subnet from the database. It uses a subnet prefix to identify the subnet. For example: .. code-block:: json { "command": "remote-subnet6-get-by-prefix", "arguments": { "subnets": [ { "subnet": "2001:db8:1::/64" } ], "remote": { "type": "mysql" } } } The `server-tags` parameter must not be used with this command. .. _command-remote-subnet4-list: .. _command-remote-subnet6-list: The remote-subnet4-list, remote-subnet6-list Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to list all IPv4 or IPv6 subnets from the database for selected servers or all servers. The following command retrieves all servers to be used by the "server1" and "server2": .. code-block:: json { "command": "remote-subnet4-list" "arguments": { "remote": { "type": "mysql" }, "server-tags": [ "server1", "server2" ] } } The `server-tags` parameter is mandatory and contains one or more server tags. It may contain the keyword "all", to fetch the subnets associated with all servers. When the `server-tags` list contains the `null` value, the returned response contains a list of unassigned subnets, i.e. the subnets which are associated with no servers. For example: .. code-block:: json { "command": "remote-subnet4-list" "arguments": { "remote": { "type": "mysql" }, "server-tags": [ null ] } } The example response to this command when non-null server tags are specified looks similar to this: .. code-block:: json { "result": 0, "text": "2 IPv4 subnet(s) found.", "arguments": { "subnets": [ { "id": 1, "subnet": "192.0.2.0/24", "shared-network-name": null, "metadata": { "server-tags": [ "server1", "server2" ] } }, { "id": 2, "subnet": "192.0.3.0/24", "shared-network-name": null, "metadata": { "server-tags": [ "all" ] } } ], "count": 2 } } The returned information about each subnet is limited to subnet identifier, prefix and associated shared network name. In order to retrieve full information about the selected subnet use the `remote-subnet[46]-get-by-id` or `remote-subnet[46]-get-by-prefix`. The example response above contains two subnets. One of the subnets is associated with both servers: "server1" and "server2". The second subnet is associated with all servers, thus it is also present in the configuration for the "server1" and "server2". When listing unassigned subnets, the response will look similar to this: .. code-block:: json { "result": 0, "text": "1 IPv4 subnet(s) found.", "arguments": { "subnets": [ { "id": 3, "subnet": "192.0.4.0/24", "shared-network-name": null, "metadata": { "server-tags": [ null ] } } ], "count": 1 } } The `null` value in the metadata indicates that the returned subnet is unassigned. .. _command-remote-subnet4-set: .. _command-remote-subnet6-set: The remote-subnet4-set, remote-subnet6-set Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands are used to create a new IPv4 or IPv6 subnet or replace an existing subnet in the database. Setting the subnet also associates or disassociates the subnet with a shared network. The structure of the subnet information is similar to the structure used in the configuration file (see :ref:`dhcp4-configuration` and :ref:`dhcp6-configuration`). The subnet information conveyed in the ``remote-subnet[46]-set`` command must include the additional parameter ``shared-network-name``, which denotes whether the subnet belongs to a shared network. Consider the following example: .. code-block:: json { "command": "remote-subnet4-set", "arguments": { "subnets": [ { "id": 5, "subnet": "192.0.2.0/24", "shared-network-name": "level3", "pools": [ { "pool": "192.0.2.100-192.0.2.200" } ], "option-data": [ { "name": "routers", "data": "192.0.2.1" } ] } ], "remote": { "type": "mysql" }, "server-tags": [ "all" ] } } It creates the subnet and associates it with the "level3" shared network. The "level3" shared network must be created with the ``remote-network4-set`` command prior to creating the subnet. If the created subnet must be global - that is, not associated with any shared network - the ``shared-network-name`` must be explicitly set to ``null``: .. code-block:: json { "command": "remote-subnet4-set", "arguments": { "subnets": [ { "id": 5, "subnet": "192.0.2.0/24", "shared-network-name": null, "pools": [ { "pool": "192.0.2.100-192.0.2.200" } ], "option-data": [ { "name": "routers", "data": "192.0.2.1" } ] } ], "server-tags": [ "all" ] } } The subnet created in the previous example is replaced with the new subnet having the same parameters, but it becomes global. The ``shared-network-name`` parameter is mandatory for the ``remote-subnet4-set`` command. The `server-tags` list is mandatory and it must include one or more server tags. As a result, the subnet is associated with all of the listed servers. It may also be associated with "all" servers connecting to the database when the keyword "all" is used as the server tag. .. note:: As with other "set" commands, this command replaces all the information about the particular subnet in the database, if the subnet information is already present. Therefore, when sending this command, make sure to always include all parameters that must be specified for the updated subnet instance. Any unspecified parameter will be marked as unspecified in the database, even if its value was present prior to sending the command. .. _command-remote-class4-del: .. _command-remote-class6-del: The remote-class4-del, remote-class6-del Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands delete a DHCPv4 or DHCPv6 client class by name. If any client classes in the database depend on the deleted class, an error is returned in response to this command. In this case, to successfully delete the class, the dependent client classes must be deleted first. Use the ``remote-class4-get-all`` command to fetch all client classes and find the dependent ones. .. code-block:: json { "command": "remote-class4-del", "arguments": { "client-classes": [ { "name": "foo" } ], "remote": { "type": "mysql" } } } The `server-tags` parameter must not be used for this command because client classes are uniquely identified by name. .. _command-remote-class4-get: .. _command-remote-class6-get: The remote-class4-get, remote-class6-get Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands retrieve DHCPv4 or DHCPv6 client class information by a client class name. .. code-block:: json { "command": "remote-class4-get", "arguments": { "client-classes": [ { "name": "foo" } ], "remote": { "type": "mysql" } } } The `server-tags` parameter must not be used for this command because client classes are uniquely identified by name. A response to the command looks similar to this: .. code-block:: json { "result": 0, "text": "DHCPv4 client class 'foo' found.", "arguments": { "client-classes": [ { "name": "foo", "metadata": { "server-tags": [ "all" ] } } ], "count": 1 } } .. _command-remote-class4-get-all: .. _command-remote-class6-get-all: The remote-class4-get-all, remote-class6-get-all Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands retrieve all DHCPv4 or DHCPv6 client classes for a particular server, multiple explicitly listed servers, or all servers. For example, the following command retrieves all client classes defined for a server having the server tag of `server1` and all servers. In other words, it returns all client classes used by that server. .. code-block:: json { "command": "remote-class4-get-all", "arguments": { "remote": { "type": "mysql" }, "server-tags": [ "server1" ] } } The `server-tags` parameter is mandatory and it contains one or more server tags. It may contain the keyword "all" to fetch the client classes associated with all servers. When the `server-tags` list contains the `null` value the returned response contains a list of unassigned client classes, i.e. the networks which are associated with no servers. A response to the command looks similar to this: .. code-block:: json { "result": 0, "text": "2 DHCPv4 client class(es) found.", "arguments": { "client-classes": [ { "name": "foo", "metadata": { "server-tags": [ "all" ] } }, { "name": "bar", "test": "member('foo')", "metadata": { "server-tags": [ "all" ] } } ], "count": 2 } } .. _command-remote-class4-set: .. _command-remote-class6-set: The remote-class4-set, remote-class6-set Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These commands insert a new or replace an existing DHCPv4 or DHCPv6 client class in the database. The client class information structure is the same as in the Kea configuration file (see :ref:`dhcp4-client-classifier` and :ref:`dhcp6-client-classifier` for details). .. code-block:: json { "command": "remote-class4-set", "arguments": { "client-classes": [ { "name": "foo", "test": "member('KNOWN') or member('bar')", "option-def": [ { "name": "configfile", "code": 224, "type": "string" } ], "option-data": [ { "name": "configfile", "data": "1APC" } ] } ], "remote": { "type": "mysql" }, "server-tags": [ "all" ] } } Client class ordering rules described in :ref:`classification-using-expressions` apply to the classes inserted into the database. It implies that the class `bar` referenced in the test expression must exist in the database when issuing the above command. By default, a new client class is inserted at the end of the class hierarchy in the database and can reference any class associated with the same server tag or with the special server tag `all`. If an existing class is updated, it remains at its current position within the class hierarchy. However, the class commands allow for specifying a position of the inserted or updated client class. The optional `follow-class-name` parameter can be included in the command to specify the name of the existing class after which the managed class should be placed. Suppose there are two DHCPv6 classes in the database: `first-class` and `second-class`. To add a new class, `third-class`, between these two, use the command similar to the following: .. code-block:: json { "command": "remote-class6-set", "arguments": { "client-classes": [ { "name": "third-class", "test": "member('first-class')" } ], "follow-class-name": "first-class", "remote": { "type": "mysql" }, "server-tags": [ "all" ] } } Note that the `third-class` can depend on the `first-class` because it is placed after the `first-class`. The `third-class` must not depend on the `second-class` because it is placed before it. However, the `second-class` could now be updated to depend on the `third-class`. The `follow-class-name` parameter can be explicitly set to `null`, e.g.: .. code-block:: json { "command": "remote-class6-set", "arguments": { "client-classes": [ { "name": "third-class", "test": "member('first-class')" } ], "follow-class-name": null, "remote": { "type": "mysql" }, "server-tags": [ "all" ] } } It yields the same behavior as if the `follow-class-name` parameter is not included, i.e. the new class is appended at the end of the class hierarchy, and the updated class remains at the current position. kea-2.0.2/doc/sphinx/arm/hooks-lease-query.rst0000644000175000017500000004260314206773363016160 00000000000000.. _lease-query: lease_query: Leasequery ======================= This library provides support for DHCPv4 Leasequery as described in `RFC 4388 `__; and for DHCPv6 Lease Query (`RFC 5007 `__). .. note:: This library may only be loaded by the ``kea-dhcp4`` or ``kea-dhcp6`` process. The Leasequery library is only available to ISC customers with a paid support contract. .. _lease-query-dhcpv4: DHCPv4 Leasequery ~~~~~~~~~~~~~~~~~ DHCPv4 simple Leasequery provides a requester the ability to query for active lease information for either a single IP address or a single client. RFC 4388 calls for three such queries: - Query by IP address The IP address of interest is contained within the ``ciaddr`` field of the query. - Query by hardware address The hardware address of interest is contained with the ``chaddr`` field of the query. - Query by client identifier The client identifier of interest is sent in the dhcp-client-identifier option (61) of the query. The inbound DHCPLEASEQUERY packet must supply only one of the three values above. Queries which supply more than one of these values are dropped. In addition, the query must contain the IP address of the requester in ``giaddr``. This value will be used not only as the destination for the query response but also to validate the requester against a known list of IP addresses which are permitted to query. This list of valid requester addresses is specified as part of the Leasequery hook library's configuration (See the section on configuration below). In response to a valid query, the server will return one of three message types: - DHCPLEASEUNKNOWN Returned when the IP address of interest is not one the server knows about (query by IP address); or there are no active leases for the client of interest (query by hardware address or client id). - DHCPLEASEUNASSIGNED Returned when the IP address is one the server knows of but for which there are no active leases (applies only to query by IP address). - DHCPLEASEACTIVE Returned when there is at least one active lease found matching the criteria. For both DHCPLEASEUNKNOWN and DHCPLEASEUNASSIGNED responses, the only information sent back to the requester in response is the query parameter itself (i.e. one of: IP address, hardware address, or client identifier). For DHCPLEASEACTIVE the server will provide the following information for the newest active lease that matches the criteria, in the response: - ciaddr - set to the lease's IP address - chaddr - set to the lease's hardware address In addition, one or more of the following options will be included: .. tabularcolumns:: |p{0.2\linewidth}|p{0.1\linewidth}|p{0.7\linewidth}| .. table:: DHCPLEASEACTIVE Options :class: longtable :widths: 30 10 70 +------------------------------+-------+-----------------------------------------------+ | Option | Code | Content | +==============================+=======+===============================================+ | dhcp-client-identifier | 61 | copied from the lease (if appropriate) | +------------------------------+-------+-----------------------------------------------+ | client-last-transaction-time | 91 | the amount of time that has elapsed since the | | | | lease's client-last-transaction-time (CLTT). | | | | This value is also used by the server to | | | | adjust lifetime and timer values. | +------------------------------+-------+-----------------------------------------------+ | dhcp-lease-time | 51 | lease's lifetime reduced by CLTT | +------------------------------+-------+-----------------------------------------------+ | dhcp-renewal-time | 58 | as controlled by kea-dhcp4 configuration and | | | | then reduced by CLTT | +------------------------------+-------+-----------------------------------------------+ | dhcp-rebind-time | 59 | as dictated by kea-dhcp4 configuration and | | | | then reduced by CLTT | +------------------------------+-------+-----------------------------------------------+ | dhcp-agent-options | 82 | if stored on the lease. (See | | | | :ref:`dhcp4-store-extended-info`) | +------------------------------+-------+-----------------------------------------------+ | associated-ip | 92 | a list of all other IP addresses for which | | | | the client has active leases. (Does not apply | | | | to query by IP address) | +------------------------------+-------+-----------------------------------------------+ The dhcp-server-identifier option (54) will be returned in all responses in keeping with RFC 2131 section 4.3.1. RFC 4388 allows requesters to ask for specific options via the dhcp-parameter-request-list (PRL, option 55). This is not currently supported but may be added at a future date. .. _lease-query-dhcpv4-config: DHCPv4 Leasequery Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Configuring the Leasequery hook library for use is straight forward. It currently supports a single parameter, ``requesters``, which is a list of IP addresses from which DHCPLEASEQUERY packets will be accepted. In other words, it is a list of known requesters. The following shows an example configuration with two requester addresses: :: : "hooks-libraries": [ { "library": "lib/kea/hooks/libdhcp_lease_query.so", "parameters": { "requesters": [ "192.0.1.1", "10.0.0.2" ] } } ], : .. note:: For security purposes, there is no way to specify wildcards. Each requester address must be explicitly listed. .. _lease-query-dhcpv6: DHCPv6 Leasequery ~~~~~~~~~~~~~~~~~ DHCPv6 simple Leasequery provides a requester the ability to query for active lease information for either a single IP address or a single client DUID. The query type and parameters are conveyed in an ``lq-query`` option (44) attached to a DHCPV6_LEASEQUERY message. Briefly, - query-type This is either ``query-by-address`` (1) or ``query-by-clientid`` (2) - link-address Global link address, when not empty, instructs the query to be limited to leases within that "link". Kea uses this value to select only leases that belong to subnets whose prefix matches this value. Note that active leases for prefix delegations for a matched subnet will be included in the query reply, even if the delegated prefix itself falls outside the subnet prefix. - query-options A single ``iaaddr`` option (12) must be supplied when querying by address. When querying by client ID, a single ``clientid`` option (1) must be supplied. RFC 5007 also calls for an optional, ``oro`` option (6), to request specific options be returned for matched leases. This is not currently implemented. .. note:: `RFC 5007, Section 3.3 `__ states that querying by IP address should return either a lease (e.g. binding) for the address itself or a lease for a delegated prefix that contains the address. The latter is not currently implemented. Leases for delegated prefixes may only be returned when querying by client ID. See `gitlab issue #1275 `__ DHCPV6_LEASEQUERY queries will only be honored if the source address of the query matches an entry in a list of known IP addresses which are permitted to query. This list of valid requester addresses is specified as part of the Leasequery hook library’s configuration (See the section on configuration below). Queries received from unknown requesters will be logged and dropped. In response to a valid query, the server will carry out the requisite activities and return a DHCPV6_LEASEQUERY_REPLY. All replies will contain at least a ``status-code`` option (13) that indicates the outcome of the query as detailed in the following table: .. tabularcolumns:: |p{0.5\linewidth}|p{0.3\linewidth}|p{0.1\linewidth}|p{0.3\linewidth}| .. table:: DHCPV6_LEASEQUERY_REPLY Status Option Values per Query Outcome :class: longtable :widths: 50 30 10 30 +--------------------------------------+-------------------------+--------+------------------------------+ | | Status | Status | Status | | Query Outcome | Label | Code | Text | +======================================+=========================+========+==============================+ | Invalid query type field | STATUS_UnknownQueryType | 7 | "unknown query-type" | +--------------------------------------+-------------------------+--------+------------------------------+ | Query by IP address that does not | STATUS_Malformed | 10 | "missing D6O_IAADDR" | | contain an address option | | | | +--------------------------------------+-------------------------+--------+------------------------------+ | Query by IP address for an address | STATUS_NotConfigured | 9 | "address not in a configured | | that does fall within any configured | | | pool" | | pools | | | | +--------------------------------------+-------------------------+--------+------------------------------+ | Query by IP address which found only | STATUS_Success | 0 | "inactive lease exists" | | an inactive lease (e.g. expired, | | | | | declined, reclaimed-expired) | | | | +--------------------------------------+-------------------------+--------+------------------------------+ | Query by IP address that found no | STATUS_Success | 0 | "no active lease" | | leases (active or otherwise) | | | | +--------------------------------------+-------------------------+--------+------------------------------+ | Query by IP address that found an | STATUS_Success | 0 | "active lease found" | | active lease for the address | | | | +--------------------------------------+-------------------------+--------+------------------------------+ | Query by Client ID that does not | STATUS_Malformed | 10 | "missing D6O_CLIENTID" | | contain a client ID option | | | | +--------------------------------------+-------------------------+--------+------------------------------+ | Query by Client ID with a link | STATUS_NotConfigured | 9 | "not a configured link" | | address that does not match any | | | | | configured subnets | | | | +--------------------------------------+-------------------------+--------+------------------------------+ | Query by client id which found no | STATUS_Success | 0 | "no active leases" | | matching leases | | | | +--------------------------------------+-------------------------+--------+------------------------------+ | Query by client id which found one | STATUS_Success | 0 | "active lease(s) found" | | or more active leases | | | | +--------------------------------------+-------------------------+--------+------------------------------+ For those scenarios where the query was either invalid or no matching, active leases were found the DHCPV6_LEASEQUERY_REPLY will only contain the status-code option (12) per the above table. When a query finds active leases in more than one subnet and query's link-address is empty, then in addition to the status-code, the DHCPV6_LEASEQUERY_REPLY will contain an ``lq-client-link`` option (48). The lq-client-link will contain a list of IPv6 addresses, one for each subnet in which a lease was found (see `RFC 5007, Section 4.1.2.5 `__) If, however, the query's link-address is not empty, the list of queries will be pruned to contain only leases that belong to that subnet. When the query results in one or more active leases which all belong to a single subnet, in addition to the status-code, the DHCPV6_LEASEQUERY_REPLY will contain a client-data option (45) (see `RFC 5007, Section 4.1.2.2 `__) The client-data option will encapsulate the following options: .. tabularcolumns:: |p{0.2\linewidth}|p{0.1\linewidth}|p{0.7\linewidth}| .. table:: OPTION_CLIENT_DATA returned when active lease(s) are found :class: longtable :widths: 30 10 70 +------------------------------+-------+-----------------------------------------------+ | Option | Code | Content | +==============================+=======+===============================================+ | clientid | 1 | copied from the lease (if one) | +------------------------------+-------+-----------------------------------------------+ | clt-time | 46 | amount of time that has elapsed since the | | | | lease's client-last-transaction-time (CLTT) | | | | This value will also be used by the server to | | | | adjust life time and timer values. | +------------------------------+-------+-----------------------------------------------+ | iaaddr | 5 | One option per matched address, fields in | | | | each option: | | | | - lease address | | | | - valid life time reduced by CLTT | | | | - preferred life time reduced by CLTT | +------------------------------+-------+-----------------------------------------------+ | iaprefix | 26 | One option per matched prefix, fields in | | | | each option: | | | | - prefix | | | | - prefix length | | | | - valid life time reduced by CLTT | | | | - preferred life time reduced by CLTT | +------------------------------+-------+-----------------------------------------------+ If the lease with the most recent client-last-transaction-time (CLTT) value has relay information in its user-context (see :ref:`store-extended-info-v6`), then an OPTION_LQ_RELAY_DATA option is added to the reply (see `RFC 5007, Section 4.1.2.4 `__). The relay information on the lease is a list with an entry for each relay layer the client packet (e.g. DHCPV6_REQUEST) traversed, with the first entry in list being the outermost layer (closest to the server). The ``peer-address`` field of the lq-rely-option is set to the peer address of this relay. The list of relays is then used to construct a DHCPV6_RELAY_FORW message equivalent to that which contained the client packet, minus the client packet. This message is stored in the ``DHCP-relay-message`` field of the lq-relay-data option. .. _lease-query-dhcpv6-config: DHCPv6 Leasequery Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Configuring the Leasequery hook library for use is straight forward. It currently supports a single parameter, ``requesters``, which is a list of IP addresses from which DHCPV6_LEASEQUERY packets will be accepted. In other words, it is a list of known requesters. The following shows an example configuration with two requester addresses: :: : "hooks-libraries": [ { "library": "lib/kea/hooks/libdhcp_lease_query.so", "parameters": { "requesters": [ "2001:db8:1::1", "2001:db8:2::1" ] } } ], : .. note:: For security purposes, there is no way to specify wildcards. Each requester address must be explicitly listed. kea-2.0.2/doc/sphinx/arm/keactrl.rst0000644000175000017500000003277614206773363014242 00000000000000.. _keactrl: ***************************** Managing Kea with ``keactrl`` ***************************** .. _keactrl-overview: Overview ======== ``keactrl`` is a shell script which controls the startup, shutdown, and reconfiguration of the Kea servers (``kea-dhcp4``, ``kea-dhcp6``, ``kea-dhcp-ddns``, ``kea-ctrl-agent``, and ``kea-netconf``). It also provides the means for checking the current status of the servers and determining the configuration files in use. ``keactrl`` is available only when Kea is built from sources. When installing Kea using native packages, the native ``systemd`` scripts are provided. See :ref:`systemd` Section for details. .. _keactrl-usage: Command Line Options ==================== ``keactrl`` is run as follows: .. code-block:: console # keactrl [-c keactrl-config-file] [-s server[,server,...]] ```` is one of the commands described in :ref:`keactrl-commands`. The optional ``-c keactrl-config-file`` switch allows specification of an alternate ``keactrl`` configuration file. (``--ctrl-config`` is a synonym for ``-c``.) In the absence of ``-c``, ``keactrl`` uses the default configuration file ``[kea-install-dir]/etc/kea/keactrl.conf``. The optional ``-s server[,server,...]`` switch selects the servers to which the command is issued. (``--server`` is a synonym for ``-s``.) If absent, the command is sent to all servers enabled in the ``keactrl`` configuration file. If multiple servers are specified, they should be separated by commas with no intervening spaces. .. _keactrl-config-file: The ``keactrl`` Configuration File ================================== Depending on the administrator's requirements, it may not be necessary to run all of the available servers. The ``keactrl`` configuration file sets which servers are enabled and which are disabled. The default configuration file is ``[kea-install-dir]/etc/kea/keactrl.conf``, but this can be overridden on a per-command basis using the ``-c`` switch. The contents of ``keactrl.conf`` are: .. code-block:: bash # This is a configuration file for keactrl script which controls # the startup, shutdown, reconfiguration and gathering the status # of the Kea processes. # prefix holds the location where the Kea is installed. prefix=@prefix@ # Location of Kea configuration file. kea_dhcp4_config_file=@sysconfdir@/@PACKAGE@/kea-dhcp4.conf kea_dhcp6_config_file=@sysconfdir@/@PACKAGE@/kea-dhcp6.conf kea_dhcp_ddns_config_file=@sysconfdir@/@PACKAGE@/kea-dhcp-ddns.conf kea_ctrl_agent_config_file=@sysconfdir@/@PACKAGE@/kea-ctrl-agent.conf kea_netconf_config_file=@sysconfdir@/@PACKAGE@/kea-netconf.conf # Location of Kea binaries. exec_prefix=@exec_prefix@ dhcp4_srv=@sbindir@/kea-dhcp4 dhcp6_srv=@sbindir@/kea-dhcp6 dhcp_ddns_srv=@sbindir@/kea-dhcp-ddns ctrl_agent_srv=@sbindir@/kea-ctrl-agent netconf_srv=@sbindir@/kea-netconf # Start DHCPv4 server? dhcp4=yes # Start DHCPv6 server? dhcp6=yes # Start DHCP DDNS server? dhcp_ddns=no # Start Control Agent? ctrl_agent=yes # Start Netconf? netconf=no # Be verbose? kea_verbose=no .. .. note:: In the example above, strings of the form @something@ are replaced by the appropriate values when Kea is installed. Setting the ``dhcp4``, ``dhcp6``, ``dhcp_ddns``, ``ctrl_agent``, and ``netconf`` parameters set to "yes" configures ``keactrl`` to manage (start, reconfigure) all servers, i.e. ``kea-dhcp4``, ``kea-dhcp6``, ``kea-dhcp-ddns``, ``kea-ctrl-agent``, and ``kea-netconf``. When any of these parameters is set to "no", ``keactrl`` ignores the corresponding server when starting or reconfiguring Kea. Some daemons (dhcp_ddns and netconf) are disabled by default. By default, Kea servers managed by ``keactrl`` are located in ``[kea-install-dir]/sbin``. This should work for most installations. If the default location needs to be altered, the paths specified with the ``dhcp4_srv``, ``dhcp6_srv``, ``dhcp_ddns_srv``, ``ctrl_agent_srv``, and ``netconf_srv`` parameters should be modified. The ``kea_verbose`` parameter specifies the verbosity of the servers being started. When ``kea_verbose`` is set to "yes," the logging level of the server is set to DEBUG. Modification of the logging severity in a configuration file, as described in :ref:`logging`, will have no effect as long as ``kea_verbose`` is set to "yes." Setting it to "no" causes the server to use the logging levels specified in the Kea configuration file. If no logging configuration is specified, the default settings are used. .. note:: The verbosity for the server is set when it is started. Once started, the verbosity can only be changed by stopping the server and starting it again with the new value of the ``kea_verbose`` parameter. .. _keactrl-commands: Commands ======== The following commands are supported by ``keactrl``: - ``start`` - starts the selected servers. - ``stop`` - stops all running servers. - ``reload`` - triggers reconfiguration of the selected servers by sending the SIGHUP signal to them. - ``status`` - returns the status of the servers (active or inactive) and the names of the configuration files in use. - ``version`` - prints out the version of the ``keactrl`` tool itself, together with the versions of the Kea daemons. Typical output from ``keactrl`` when starting the servers looks similar to the following: .. code-block:: console $ keactrl start INFO/keactrl: Starting kea-dhcp4 -c /usr/local/etc/kea/kea-dhcp4.conf -d INFO/keactrl: Starting kea-dhcp6 -c /usr/local/etc/kea/kea-dhcp6.conf -d INFO/keactrl: Starting kea-dhcp-ddns -c /usr/local/etc/kea/kea-dhcp-ddns.conf -d INFO/keactrl: Starting kea-ctrl-agent -c /usr/local/etc/kea/kea-ctrl-agent.conf -d INFO/keactrl: Starting kea-netconf -c /usr/local/etc/kea/kea-netconf.conf -d Kea's servers create PID files upon startup. These files are used by ``keactrl`` to determine whether a given server is running. If one or more servers are running when the start command is issued, the output looks similar to the following: .. code-block:: console $ keactrl start INFO/keactrl: kea-dhcp4 appears to be running, see: PID 10918, PID file: /usr/local/var/run/kea/kea.kea-dhcp4.pid. INFO/keactrl: kea-dhcp6 appears to be running, see: PID 10924, PID file: /usr/local/var/run/kea/kea.kea-dhcp6.pid. INFO/keactrl: kea-dhcp-ddns appears to be running, see: PID 10930, PID file: /usr/local/var/run/kea/kea.kea-dhcp-ddns.pid. INFO/keactrl: kea-ctrl-agent appears to be running, see: PID 10931, PID file: /usr/local/var/run/kea/kea.kea-ctrl-agent.pid. INFO/keactrl: kea-netconf appears to be running, see: PID 10123, PID file: /usr/local/var/run/kea/kea.kea-netconf.pid. During normal shutdowns, these PID files are deleted; they may, however, be left over as remnants following a system crash. It is possible, though highly unlikely, that upon system restart the PIDs they contain may actually refer to processes unrelated to Kea. This condition will cause ``keactrl`` to decide that the servers are running, when in fact they are not. In such a case the PID files listed in the ``keactrl`` output must be manually deleted. The following command stops all servers: .. code-block:: console $ keactrl stop INFO/keactrl: Stopping kea-dhcp4... INFO/keactrl: Stopping kea-dhcp6... INFO/keactrl: Stopping kea-dhcp-ddns... INFO/keactrl: Stopping kea-ctrl-agent... INFO/keactrl: Stopping kea-netconf... Note that the ``stop`` command attempts to stop all servers regardless of whether they are "enabled" in ``keactrl.conf``. If any of the servers are not running, an informational message is displayed as in the ``stop`` command output below. .. code-block:: console $ keactrl stop INFO/keactrl: kea-dhcp4 isn't running. INFO/keactrl: kea-dhcp6 isn't running. INFO/keactrl: kea-dhcp-ddns isn't running. INFO/keactrl: kea-ctrl-agent isn't running. INFO/keactrl: kea-netconf isn't running. As already mentioned, the reconfiguration of each Kea server is triggered by the SIGHUP signal. The ``reload`` command sends the SIGHUP signal to any servers that are enabled in the ``keactrl`` configuration file and that are currently running. When a server receives the SIGHUP signal it rereads its configuration file and, if the new configuration is valid, uses the new configuration. A reload is executed as follows: .. code-block:: console $ keactrl reload INFO/keactrl: Reloading kea-dhcp4... INFO/keactrl: Reloading kea-dhcp6... INFO/keactrl: Reloading kea-dhcp-ddns... INFO/keactrl: Reloading kea-ctrl-agent... If any of the servers are not running, an informational message is displayed as in the ``reload`` command output below. As of version 1.5.0, ``kea-netconf`` does not support the SIGHUP signal. If its configuration has changed, please stop and restart it for the change to take effect. .. code-block:: console $ keactrl stop INFO/keactrl: kea-dhcp4 isn't running. INFO/keactrl: kea-dhcp6 isn't running. INFO/keactrl: kea-dhcp-ddns isn't running. INFO/keactrl: kea-ctrl-agent isn't running. INFO/keactrl: kea-netconf isn't running. .. .. note:: NETCONF is an optional feature that is disabled by default and can be enabled during compilation. If Kea was compiled without NETCONF support, ``keactrl`` does not provide information about it. The NETCONF entries are still present in the ``keactrl.conf`` file, but NETCONF status is not shown and other commands ignore it. .. note:: Currently ``keactrl`` does not report configuration failures when the server is started or reconfigured. To check if the server's configuration succeeded, the Kea log must be examined for errors. By default, the log is written to the `syslog` file. Sometimes it is useful to check which servers are running. The ``status`` command reports this, with typical output that looks like: .. code-block:: console $ keactrl status DHCPv4 server: active DHCPv6 server: inactive DHCP DDNS: active Control Agent: active Netconf agent: inactive Kea configuration file: /usr/local/etc/kea/kea.conf Kea DHCPv4 configuration file: /usr/local/etc/kea/kea-dhcp4.conf Kea DHCPv6 configuration file: /usr/local/etc/kea/kea-dhcp6.conf Kea DHCP DDNS configuration file: /usr/local/etc/kea/kea-dhcp-ddns.conf Kea Control Agent configuration file: /usr/local/etc/kea/kea-ctrl-agent.conf Kea Netconf configuration file: /usr/local/etc/kea/kea-netconf.conf keactrl configuration file: /usr/local/etc/kea/keactrl.conf ``keactrl status`` offers basic reporting capabilities. For more extensive insight into Kea's health and status, consider deploying Stork. For details, see :ref:`stork`. .. _keactrl-overriding-servers: Overriding the Server Selection =============================== The optional ``-s`` switch allows the selection of the server(s) to which the ``keactrl`` command is issued. For example, the following instructs ``keactrl`` to stop the ``kea-dhcp4`` and ``kea-dhcp6`` servers and leave the ``kea-dhcp-ddns`` and ``kea-ctrl-agent`` running: .. code-block:: console $ keactrl stop -s dhcp4,dhcp6 Similarly, the following starts only the ``kea-dhcp4`` and ``kea-dhcp-ddns`` servers, but not ``kea-dhcp6`` or ``kea-ctrl-agent``. .. code-block:: console $ keactrl start -s dhcp4,dhcp_ddns Note that the behavior of the ``-s`` switch with the ``start`` and ``reload`` commands is different from its behavior with the ``stop`` command. On ``start`` and ``reload``, ``keactrl`` checks whether the servers given as parameters to the ``-s`` switch are enabled in the ``keactrl`` configuration file; if not, the server is ignored. For ``stop``, however, this check is not made; the command is applied to all listed servers, regardless of whether they have been enabled in the file. The following keywords can be used with the ``-s`` command-line option: - ``dhcp4`` for ``kea-dhcp4``. - ``dhcp6`` for ``kea-dhcp6``. - ``dhcp_ddns`` for ``kea-dhcp-ddns``. - ``ctrl_agent`` for ``kea-ctrl-agent``. - ``netconf`` for ``kea-netconf``. - ``all`` for all servers (default). .. _systemd: Native Packages and ``systemd`` =============================== ``keactrl`` is a script that was developed to assist in managing Kea processes. However, all modern operating systems have their own process-management scripts, such as ``systemd``. In general, these native scripts should be used, as they have several advantages. ``systemd`` scripts handle processes in a uniform way, so Kea is handled in a similar fashion to HTTP or a mail server. Second and more importantly, ``systemd`` allows dependencies to be defined between services. For example, it is easy to specify that the Kea server should not start until the network interfaces are operational. Using native scripts also has other benefits, such as the ability to enable or disable services using commands, and the ability to temporarily start a disabled service. Thus, it is recommended to use ``systemctl`` commands if they are available. Native Kea packages do not provide ``keactrl``; ``systemctl`` service definitions are provided instead. Consult the system documentation for details. Briefly, here are example commands to check status, start, stop, and restart various Kea daemons: .. code-block:: console # systemctl status isc-kea-ctrl-agent # systemctl start isc-kea-dhcp4-server # systemctl stop isc-kea-dhcp6-server # systemctl restart isc-kea-dhcp-ddns-server Note that the service names may be slightly different between Linux distributions; in general, we have followed the naming conventions in third-party packages. In particular, some systems may not have the `isc-` prefix. kea-2.0.2/doc/sphinx/arm/ctrl-channel.rst0000644000175000017500000006062414206773363015160 00000000000000.. _ctrl-channel: ************** Management API ************** A classic approach to daemon configuration assumes that the server's configuration is stored in configuration files and, when the configuration is changed, the daemon is restarted. This approach has the significant disadvantage of introducing periods of downtime when client traffic is not handled. Another risk is that if the new configuration is invalid for any reason, the server may refuse to start, which will further extend the downtime period until the issue is resolved. To avoid such problems, the DHCPv4, DHCPv6, and D2 servers in Kea include support for a mechanism that allows online reconfiguration without requiring server shutdown. Both servers can be instructed to open control sockets, which is a communications channel. The server is able to receive commands on that channel, act on them, and report back status. The DHCPv4, DHCPv6, and D2 servers receive commands over the UNIX domain sockets. For details on how to configure these sockets, see :ref:`dhcp4-ctrl-channel` and :ref:`dhcp6-ctrl-channel`. While it is possible to control the servers directly using UNIX domain sockets, that requires that the controlling client be running on the same machine as the server. SSH is usually used to connect remotely to the controlled machine. Network administrators usually prefer using some form of a RESTful API to control the servers, rather than using UNIX domain sockets directly. Therefore, Kea includes a component called the Control Agent (or CA), which exposes a RESTful API to the controlling clients and can forward commands to the respective Kea services over the UNIX domain sockets. The CA configuration is described in :ref:`agent-configuration`. The HTTP requests received by the CA contain the control commands encapsulated within HTTP requests. Simply speaking, the CA is responsible for stripping the HTTP layer from the received commands and forwarding the commands in a JSON format over the UNIX domain sockets to the respective services. Because the CA receives commands for all services, it requires additional "forwarding" information to be included in the client's messages. This forwarding information is carried within the ``service`` parameter of the received command. If the ``service`` parameter is not included, or if the parameter is a blank list, the CA will assume that the control command is targeted at the CA itself and will try to handle it on its own. Control connections over both HTTP and UNIX domain sockets are guarded with timeouts. The default timeout value is set to 10 seconds and is not configurable. This API can be used by external tools to manage and monitor Kea operation. An example of such a monitoring tool is ISC Stork. For details, see :ref:`stork`. .. _ctrl-channel-syntax: Data Syntax =========== Communication over the control channel is conducted using JSON structures. If configured, Kea will open a socket and listen for incoming connections. A process connecting to this socket is expected to send JSON commands structured as follows: :: { "command": "foo", "service": [ "dhcp4" ] "arguments": { "param1": "value1", "param2": "value2", ... } } The same command sent over the RESTful interface to the CA will have the following structure: :: POST / HTTP/1.1\r\n Content-Type: application/json\r\n Content-Length: 147\r\n\r\n { "command": "foo", "service": [ "dhcp4" ] "arguments": { "param1": "value1", "param2": "value2", ... } } ``command`` is the name of the command to execute and is mandatory. ``arguments`` is a map of the parameters required to carry out the given command. The exact content and format of the map are command-specific. ``service`` is a list of the servers at which the control command is targeted. In the example above, the control command is targeted at the DHCPv4 server. In most cases, the CA will simply forward this command to the DHCPv4 server for processing via a UNIX domain socket. Sometimes, the command including a service value may also be processed by the CA, if the CA is running a hooks library which handles such a command for the given server. As an example, the hooks library loaded by the CA may perform some operations on the database, such as adding host reservations, modifying leases, etc. An advantage of performing DHCPv4-specific administrative operations in the CA, rather than forwarding it to the DHCPv4 server, is the ability to perform these operations without disrupting the DHCPv4 service, since the DHCPv4 server doesn't have to stop processing DHCP messages to apply changes to the database. Nevertheless, these situations are rather rare and, in most cases, when the ``service`` parameter contains a name of the service the commands are simply forwarded by the CA. The forwarded command includes the ``service`` parameter but this parameter is ignored by the receiving server. This parameter is only meaningful to the CA. If the command received by the CA does not include a ``service`` parameter or this list is empty, the CA simply processes this message on its own. For example, a ``config-get`` command which includes no service parameter returns the Control Agent's own configuration. The ``config-get`` command with a service value "dhcp4" is forwarded to the DHCPv4 server and returns the DHCPv4 server's configuration. The following list shows the mapping of the values carried within the ``service`` parameter to the servers to which the commands are forwarded: - ``dhcp4`` - the command is forwarded to the ``kea-dhcp4`` server. - ``dhcp6`` - the command is forwarded to the ``kea-dhcp6`` server. - ``d2`` - the command is forwarded to the ``kea-d2`` server. The server processing the incoming command will send a response of the form: :: { "result": 0|1|2|3, "text": "textual description", "arguments": { "argument1": "value1", "argument2": "value2", ... } } ``result`` indicates the outcome of the command. A value of 0 means success, while any non-zero value designates an error or a failure to complete the requested action. Currently 1 indicates a generic error, 2 means that a command is not supported, and 3 means that the requested operation was completed, but the requested object was not found. For example, a well-formed command that requests a subnet that exists in a server's configuration returns the result 0. If the server encounters an error condition, it returns 1. If the command asks for the IPv6 subnet, but was sent to a DHCPv4 server, it returns 2. If the query asks for a subnet-id and there is no subnet with such an id, the result is 3. The ``text`` field typically appears when the result is non-zero and contains a description of the error encountered, but it often also appears for successful outcomes. The exact text is command-specific, but in general uses plain English to describe the outcome of the command. ``arguments`` is a map of additional data values returned by the server which are specific to the command issued. The map may be present, but that depends on the specific command. .. note:: When sending commands via the Control Agent, it is possible to specify multiple services at which the command is targeted. CA forwards this command to each service individually. Thus, the CA response to the controlling client contains an array of individual responses. .. note:: Since Kea 1.9.7 it is possible to put comments in the commands as in the configuration file, for instance: :: { "command": "foo", // service is a list "service": [ "dhcp4" ] # command arguments are here. "arguments": { "param1": "value1"/*, "param2": "value2", ...*/ } } .. _ctrl-channel-client: Using the Control Channel ========================= The easiest way to start interacting with the control API is to use common UNIX/Linux tools such as ``socat`` and ``curl``. In order to control the given Kea service via a UNIX domain socket, use ``socat`` in interactive mode as follows: .. code-block:: console $ socat UNIX:/path/to/the/kea/socket - or in batch mode, include the "ignoreeof" option as shown below to ensure ``socat`` waits long enough for the server to respond: .. code-block:: console $ echo "{ some command...}" | socat UNIX:/path/to/the/kea/socket -,ignoreeof where ``/path/to/the/kea/socket`` is the path specified in the ``Dhcp4/control-socket/socket-name`` parameter in the Kea configuration file. Text passed to ``socat`` is sent to Kea and the responses received from Kea are printed to standard output. This approach communicates with the specific server directly and bypasses the Control Agent. It is also easy to open a UNIX socket programmatically. An example of a simple client written in C is available in the Kea Developer's Guide, in the Control Channel Overview chapter, in the `Using Control Channel `__ section. To use Kea's RESTful API with ``curl``, use the following: .. code-block:: console $ curl -X POST -H "Content-Type: application/json" -d '{ "command": "config-get", "service": [ "dhcp4" ] }' http://ca.example.org:8000/ This assumes that the Control Agent is running on host ``ca.example.org`` and is running the RESTful service on port 8000. .. _commands-common: Commands Supported by Both the DHCPv4 and DHCPv6 Servers ======================================================== .. _command-build-report: The build-report Command ------------------------ The ``build-report`` command returns on the control channel what the command line ``-W`` argument displays, i.e. the embedded content of the ``config.report`` file. This command does not take any parameters. :: { "command": "build-report" } .. _command-config-get: The config-get Command ---------------------- The ``config-get`` command retrieves the current configuration used by the server. This command does not take any parameters. The configuration returned is roughly equal to the configuration that was loaded using the -c command line option during server start-up or later set using the ``config-set`` command. However, there may be certain differences, as comments are not retained. If the original configuration used file inclusion, the returned configuration will include all parameters from all the included files. Note that the returned configuration is not redacted, i.e. it will contain database passwords in plain text if those were specified in the original configuration. Care should be taken not to expose the command channel to unprivileged users. An example command invocation looks like this: :: { "command": "config-get" } .. _command-config-reload: The config-reload Command ------------------------- The ``config-reload`` command instructs Kea to load again the configuration file that was used previously. This operation is useful if the configuration file has been changed by some external source; for example, a sysadmin can tweak the configuration file and use this command to force Kea pick up the changes. Caution should be taken when mixing this with ``config-set`` commands. Kea remembers the location of the configuration file it was started with, and this configuration can be significantly changed using the ``config-set`` command. When ``config-reload`` is issued after ``config-set``, Kea will attempt to reload its original configuration from the file, possibly losing all changes introduced using ``config-set`` or other commands. ``config-reload`` does not take any parameters. An example command invocation looks like this: :: { "command": "config-reload" } If the configuration file is incorrect reloading it can raise an error which leaves the server in an unusable state. Look at :ref:`command-config-set` what to do to recover a working server. .. _command-config-test: The config-test Command ----------------------- The ``config-test`` command instructs the server to check whether the new configuration supplied in the command's arguments can be loaded. The supplied configuration is expected to be the full configuration for the target server, along with an optional Logger configuration. As for the ``-t`` command, some sanity checks are not performed, so it is possible a configuration which successfully passes this command will still fail in the ``config-set`` command or at launch time. The structure of the command is as follows: :: { "command": "config-test", "arguments": { "": { } } } where is the configuration element name for a given server such as "Dhcp4" or "Dhcp6". For example: :: { "command": "config-test", "arguments": { "Dhcp6": { : } } } The server's response will contain a numeric code, "result" (0 for success, non-zero on failure), and a string, "text", describing the outcome: :: {"result": 0, "text": "Configuration seems sane..." } or {"result": 1, "text": "unsupported parameter: BOGUS (:16:26)" } .. _command-config-write: The config-write Command ------------------------ The ``config-write`` command instructs the Kea server to write its current configuration to a file on disk. It takes one optional argument, called "filename", that specifies the name of the file to write the configuration to. If not specified, the name used when starting Kea (passed as a -c argument) will be used. If a relative path is specified, Kea will write its files only in the directory it is running. An example command invocation looks like this: :: { "command": "config-write", "arguments": { "filename": "config-modified-2017-03-15.json" } } .. _command-leases-reclaim: The leases-reclaim Command -------------------------- The ``leases-reclaim`` command instructs the server to reclaim all expired leases immediately. The command has the following JSON syntax: :: { "command": "leases-reclaim", "arguments": { "remove": true } } The ``remove`` boolean parameter is mandatory and indicates whether the reclaimed leases should be removed from the lease database (if true), or left in the "expired-reclaimed" state (if false). The latter facilitates lease affinity, i.e. the ability to re-assign an expired lease to the same client that used this lease before. See :ref:`lease-affinity` for the details. Also, see :ref:`lease-reclamation` for general information about the processing of expired leases (lease reclamation). .. _command-libreload: The libreload Command --------------------- The ``libreload`` command first unloads and then loads all currently loaded hooks libraries. This is primarily intended to allow one or more hooks libraries to be replaced with newer versions without requiring Kea servers to be reconfigured or restarted. Note that the hooks libraries are passed the same parameter values (if any) that were passed when they originally loaded. :: { "command": "libreload", "arguments": { } } The server will respond with a result of either 0, indicating success, or 1, indicating failure. .. _command-list-commands: The list-commands Command ------------------------- The ``list-commands`` command retrieves a list of all commands supported by the server. It does not take any arguments. An example command may look like this: :: { "command": "list-commands", "arguments": { } } The server responds with a list of all supported commands. The arguments element is a list of strings, each of which conveys one supported command. .. _command-config-set: The config-set Command ---------------------- The ``config-set`` command instructs the server to replace its current configuration with the new configuration supplied in the command's arguments. The supplied configuration is expected to be the full configuration for the target server, along with an optional Logger configuration. While optional, the Logger configuration is highly recommended, as without it the server will revert to its default logging configuration. The structure of the command is as follows: :: { "command": "config-set", "arguments": { "": { } } } where is the configuration element name for a given server such as "Dhcp4" or "Dhcp6". For example: :: { "command": "config-set", "arguments": { "Dhcp6": { : } } } If the new configuration proves to be invalid, the server retains its current configuration but in some cases a fatal error message is logged indicating that the server no longer provides any service: a working configuration must be loaded as soon as possible. If the control channel is dead the configuration file can still be reloaded using the SIGHUP signal. Of course a last chance solution is to restart the server. Please note that the new configuration is retained in memory only; if the server is restarted or a configuration reload is triggered via a signal, the server uses the configuration stored in its configuration file. The server's response contains a numeric code, "result" (0 for success, non-zero on failure), and a string, "text", describing the outcome: :: {"result": 0, "text": "Configuration successful." } or {"result": 1, "text": "unsupported parameter: BOGUS (:16:26)" } .. _command-shutdown: The shutdown Command -------------------- The ``shutdown`` command instructs the server to initiate its shutdown procedure. It is the equivalent of sending a SIGTERM signal to the process. This command does not take any arguments. An example command may look like this: :: { "command": "shutdown" "arguments": { "exit-value": 3 } } The server responds with a confirmation that the shutdown procedure has been initiated. The optional parameter, "exit-value", specifies the numeric value with which the server process will exit to the system. The default value is zero. The DDNS daemon supports an extra parameter "type" which controls the way the process cleans up on exit. The supported shutdown types are: - "normal" - Stops the queue manager and finishes all current transactions before exiting. This is the default. - "drain_first" - Stops the queue manager but continues processing requests from the queue until it is empty. - "now" - Exits immediately. An example command may look like this: :: { "command": "shutdown" "arguments": { "exit-value": 3, "type": "drain_first" } } .. _command-dhcp-disable: The dhcp-disable Command ------------------------ The ``dhcp-disable`` command globally disables the DHCP service. The server continues to operate, but it drops all received DHCP messages. This command is useful when the server's maintenance requires that the server temporarily stop allocating new leases and renew existing leases. It is also useful in failover-like configurations during a synchronization of the lease databases at startup, or recovery after a failure. The optional parameter "max-period" specifies the time in seconds after which the DHCP service should be automatically re-enabled, if the ``dhcp-enable`` command is not sent before this time elapses. Since Kea 1.9.4 there is an additional "origin" parameter that specifies the command source. A server administrator should typically omit this parameter because the default value "user" indicates that the administrator sent the command. This command can also be sent by the partner server running HA hooks library. In that case, the partner server sets the parameter to "ha-partner". This value is reserved for the communication between HA partners and should not be specified in the administrator's commands because it may interfere with the HA operation. The administrator should either omit this parameter or set it to "user". :: { "command": "dhcp-disable", "arguments": { "max-period": 20, "origin": "user" } } .. _command-dhcp-enable: The dhcp-enable Command ----------------------- The ``dhcp-enable`` command globally enables the DHCP service. Since Kea 1.9.4 there is an additional "origin" parameter that specifies the command source. A server administrator should typically omit this parameter because the default value "user" indicates that the administrator sent the command. This command can also be sent by the partner server running HA hooks library. In that case, the partner server sets the parameter to "ha-partner". This value is reserved for the communication between HA partners and should not be specified in the administrator's commands because it may interfere with the HA operation. The administrator should either omit this parameter or set it to "user". :: { "command": "dhcp-enable", "arguments": { "origin": "user" } } .. _command-status-get: The status-get Command ---------------------- The ``status-get`` command returns server's runtime information: - pid: process id. - uptime: number of seconds since the start of the server. - reload: number of seconds since the last configuration (re)load. - high-availability: HA specific status information about the DHCP servers configured to use HA hooks library: * local: for the local server the state, the role (primary, secondary, ...) and scopes (i.e. what the server is actually processing). * remote: for the remote server the last known state, served HA scopes and the role of the server in the HA relationship. - multi-threading-enabled: flag indicating if multi-threading is enabled. - thread-pool-size: number of dhcp service threads. - packet-queue-size: maximum size of the packet queue. There is one queue, regardless of how many threads are running. - packet-queue-statistics: average queue size for last 10, 100 and 1000 packets. Statistics using an approach similar to the Unix ``top`` command. The averaged queue size for the last 10 packets can be considered an instantaneous value, while the average for the last 1000 packets shows a longer term trend. The ``high-availability`` information is returned only when the command is sent to the DHCP servers being in the HA setup. This parameter is never returned when the ``status-get`` command is sent to the Control Agent or DDNS daemon. The ``thread-pool-size`` and ``packet-queue-size`` parameters are returned only when the command is sent to DHCP servers with multi-threading enabled. These two parameters and ``multi-threading-enabled`` are never returned when the ``status-get`` command is sent to the Control Agent or DDNS daemon. To learn more about the HA status information returned by the ``status-get`` command please refer to the :ref:`command-ha-status-get` section. .. _command-server-tag-get: The server-tag-get Command: --------------------------- The ``server-tag-get`` command returns the configured server tag of the DHCPv4 or DHCPv6 server (:ref:`cb-sharing` explains the server tag concept) .. _command-config-backend-pull: The config-backend-pull Command: -------------------------------- The ``config-backend-pull`` command triggers the polling of Config Backends (which should be configured for this command to do something) explained in :ref:`dhcp4-cb-json`. .. _command-version-get: The version-get Command ----------------------- The ``version-get`` command returns extended information about the Kea version. It is the same information available via the ``-V`` command-line argument. This command does not take any parameters. :: { "command": "version-get" } Commands Supported by the D2 Server =================================== The D2 server supports only a subset of DHCPv4 / DHCPv6 server commands: - build-report - config-get - config-reload - config-set - config-test - config-write - list-commands - shutdown - status-get - version-get .. _agent-commands: Commands Supported by the Control Agent ======================================= The following commands listed in :ref:`commands-common` are also supported by the Control Agent, i.e. when the ``service`` parameter is blank, the commands are handled by the CA and they relate to the CA process itself: - build-report - config-get - config-reload - config-set - config-test - config-write - list-commands - shutdown - status-get - version-get kea-2.0.2/doc/sphinx/arm/acknowledgments.rst0000644000175000017500000000262714206773363015772 00000000000000Acknowledgments =============== Kea is an open source project designed, developed, and maintained by Internet Systems Consortium, Inc, a 501(c)3 non-profit organization. ISC is primarily funded by revenues from support subscriptions for our open source, and we encourage all professional users to consider this option. To learn more, see \ https://www.isc.org/support/. We thank all the organizations and individuals who have helped to make Kea possible. `Comcast `__ and the Comcast Innovation Fund provided major support for the development of Kea's DHCPv4, DHCPv6, and DDNS modules. Mozilla funded initial work on the REST API via a MOSS award. Kea was initially implemented as a collection of applications within the BIND 10 framework. We thank the founding sponsors of the BIND 10 project: `Afilias `__, `IIS.SE `__, `Nominet `__, `SIDN `__, `JPRS `__, `CIRA `__; and additional sponsors `AFNIC `__, `CNNIC `__, `CZ.NIC `__, `DENIC eG `__, `Google `__, `RIPE NCC `__, `Registro.br `__, `.nz Registry Services `__, and `Technical Center of Internet `__. kea-2.0.2/doc/sphinx/arm/install.rst0000644000175000017500000006112414206773363014250 00000000000000.. _installation: ************ Installation ************ Packages ======== ISC publishes native RPM, deb, and APK packages, along with the tarballs with the source code. The packages are available on `Cloudsmith `_ at https://cloudsmith.io/~isc/repos. The native packages can be downloaded and installed using the system available in a specific distribution (such as dpkg or rpm). The Kea repository can also be added to the system, making it easier to install updates. For details, please go to https://cloudsmith.io/~isc/repos, choose the repository of interest, and then click the ``Set Me Up`` button for detailed instructions. .. _install-hierarchy: Installation Hierarchy ====================== The following is the directory layout of the complete Kea installation. (All directory paths are relative to the installation directory.) - ``etc/kea/`` — configuration files. - ``include/`` — C++ development header files. - ``lib/`` — libraries. - ``lib/kea/hooks`` — additional hooks libraries. - ``sbin/`` — server software and commands used by the system administrator. - ``share/doc/kea/`` — this guide, other supplementary documentation, and examples. - ``share/kea/`` — API command examples and database schema scripts. - ``share/man/`` — manual pages (online documentation). - ``var/lib/kea/`` — server identification and lease database files. - ``var/log/`` - log files. - ``var/run/kea`` - PID file and logger lock file. .. _build-requirements: Build Requirements ================== In addition to the run-time requirements (listed in :ref:`required-software`), building Kea from source code requires various development include headers and program development tools. .. note:: Some operating systems have split their distribution packages into a run-time and a development package. The development package versions, which include header files and libraries, must be installed to build Kea from the source code. Building from source code requires the following software installed on the system: - Boost C++ libraries (https://www.boost.org/). The oldest Boost version used for testing is 1.57 (although Kea may also work with older versions). The Boost system library must also be installed. Installing a header-only version of Boost is not recommended. - OpenSSL (at least version 1.0.2) or Botan (at least version 2). OpenSSL version 1.1.1 or later is strongly recommended. - log4cplus (at least version 1.0.3) development include headers. - A C++ compiler (with C++11 support) and standard development headers. The Kea build has been checked with GCC g++ 4.8.5 and some later versions, and Clang 800.0.38 and some later versions. - The development tools automake, libtool, and pkg-config. - The MySQL client and the client development libraries, when using the ``--with-mysql`` configuration flag to build the Kea MySQL database backend. In this case, an instance of the MySQL server running locally or on a machine reachable over a network is required. Note that running the unit tests requires a local MySQL server. - The PostgreSQL client and the client development libraries, when using the ``--with-pgsql`` configuration flag to build the Kea PostgreSQL database backend. In this case an instance of the PostgreSQL server running locally or on a machine reachable over a network is required. Note that running the unit tests requires a local PostgreSQL server. - The cpp-driver from DataStax is needed when using the ``--with-cql`` configuration flag to build Kea with the Cassandra database backend. In this case, an instance of the Cassandra server running locally or on a machine reachable over a network is required. Note that running the unit tests requires a local Cassandra server. - The FreeRADIUS client library is required to connect to a RADIUS server. This is specified using the ``--with-freeradius`` configuration switch. - Sysrepo v1.4.140 and libyang v1.0.240 are needed to connect to a Sysrepo datastore. Earlier versions are no longer supported. When compiling from sources, the configure switches that can be used are ``--with-libyang`` and ``--with-sysrepo`` without any parameters. If these dependencies were installed in custom paths, point the switches to them. - The MIT Kerberos 5 or Heimdal libraries are needed by Kea DDNS server to sign and verify DNS updates using GSS-TSIG. The configuration switch which enables this functionality is ``--with-gssapi`` without any parameters. If these dependencies were installed in custom paths, point the switch to them. - googletest (version 1.8 or later) is required when using the ``--with-gtest`` configuration option to build the unit tests. - The documentation generation tools `Sphinx `_, texlive with its extensions, and Doxygen, if using the ``--enable-generate-docs`` configuration option to create the documentation. Specifically, with Fedora, python3-sphinx, texlive, and texlive-collection-latexextra are necessary; with Ubuntu, python3-sphinx, python3-sphinx-rtd-theme, and texlive-binaries are needed. If LaTeX packages are missing, Kea skips PDF generation and produces only HTML documents. Visit ISC's Knowledgebase at https://kb.isc.org/docs/installing-kea for system-specific installation tips. .. _install: Installation From Source ======================== Although Kea may be available in pre-compiled, ready-to-use packages from operating system vendors, it is open source software written in C++. As such, it is freely available in source code form from ISC as a downloadable tar file. The source code can also be obtained from the Kea GitLab repository at https://gitlab.isc.org/isc-projects/kea. This section describes how to build Kea from the source code. Download Tar File ----------------- The Kea release tarballs may be downloaded from: https://downloads.isc.org/isc/kea/. Retrieve From Git ----------------- The latest development code is available on GitLab (see https://gitlab.isc.org/isc-projects/kea). The Kea source is public and development is done in the “master” branch. Downloading this "bleeding edge" code is recommended only for developers or advanced users. Using development code in a production environment is not recommended. .. note:: When building from source code retrieved via git, additional software is required: automake (v1.11 or later), libtoolize, and autoconf (v2.69 or later). These may need to be installed. The code can be checked out from ``https://gitlab.isc.org/isc-projects/kea.git``: .. code-block:: console $ git clone https://gitlab.isc.org/isc-projects/kea.git The code checked out from the git repository does not include the generated configure script or the Makefile.in files, nor their related build files. They can be created by running ``autoreconf`` with the ``--install`` switch. This will run ``autoconf``, ``aclocal``, ``libtoolize``, ``autoheader``, ``automake``, and related commands. Write access to the Kea repository is only granted to ISC staff. Developers planning to contribute to Kea should check our `Contributor's Guide `__. The `Kea Developer's Guide `__ contains more information about the process, and describes the requirements for contributed code to be accepted by ISC. .. _configure: Configure Before the Build -------------------------- Kea uses the GNU Build System to discover build environment details. To generate the makefiles using the defaults, simply run: .. code-block:: console $ ./configure Run ``./configure`` with the ``--help`` switch to view the different options. Some commonly used options are: - ``--prefix`` Define the installation location (the default is ``/usr/local``). - ``--with-mysql`` Build Kea with code to allow it to store leases and host reservations in a MySQL database. - ``--with-pgsql`` Build Kea with code to allow it to store leases and host reservations in a PostgreSQL database. - ``--with-cql`` Build Kea with code to allow it to store leases and host reservations in a Cassandra (CQL) database. Support for Cassandra is now deprecated. - ``--with-log4cplus`` Define the path to find the Log4cplus headers and libraries. Normally this is not necessary. - ``--with-boost-include`` Define the path to find the Boost headers. Normally this is not necessary. - ``--with-botan-config`` Specify the path to the botan-config script to build with Botan for cryptographic functions. It is preferable to use OpenSSL (see below). - ``--with-openssl`` Use the OpenSSL cryptographic library instead of Botan. By default ``configure`` searches for a valid Botan installation; if one is not found, Kea searches for OpenSSL. Normally this is not necessary. - ``--enable-shell`` Build the optional ``kea-shell`` tool (more in :ref:`kea-shell`). The default is to not build it. - ``--with-site-packages`` Only useful when ``kea-shell`` is enabled, this switch causes the kea-shell Python packages to be installed in the specified directory. This is mostly useful for Debian-related distributions. While most systems store Python packages in ``${prefix}/usr/lib/pythonX/site-packages``, Debian introduced a separate directory for packages installed from DEB. Such Python packages are expected to be installed in ``/usr/lib/python3/dist-packages``. - ``--enable-perfdhcp`` Build the optional ``perfdhcp`` DHCP benchmarking tool. The default is to not build it. - ``--with-freeradius`` Build the optional ``RADIUS`` hook. This option specifies the path to the patched version of the FreeRADIUS client. This feature is available in the subscriber-only version of Kea, and requires the subscription-only RADIUS hook. - ``--with-freeradius-dictionary`` Specify a non-standard location for a FreeRADIUS dictionary file, which contains a list of supported RADIUS attributes. This feature is available in the subscriber-only version of Kea, and requires the subscription-only RADIUS hook. If the RADIUS options are not available, ensure that the RADIUS hook sources are in the ``premium`` directory and rerun ``autoreconf -i``. .. note:: For instructions concerning the installation and configuration of database backends for Kea, see :ref:`dhcp-install-configure`. There are many options that are typically not necessary for regular users. However, they may be useful for package maintainers, developers, or people who want to extend Kea code or send patches: - ``--with-gtest``, ``--with-gtest-source`` Enable the building of C++ unit tests using the Google Test framework. This option specifies the path to the gtest source. (If the framework is not installed on the system, it can be downloaded from https://github.com/google/googletest.) - ``--enable-generate-docs`` Enable the rebuilding of Kea documentation. ISC publishes Kea documentation for each release; however, in some cases it may be desirable to rebuild it: for example, to change something in the docs, or to generate new ones from git sources that are not yet released. - ``--enable-generate-parser`` Enable the generation of parsers using flex or bison. Kea sources include .cc and .h parser files, pre-generated for users' convenience. By default Kea does not use flex or bison, to avoid requiring installation of unnecessary dependencies for users. However, if anything in the parsers is changed (such as adding a new parameter), flex and bison are required to regenerate parsers. This option permits that. - ``--enable-generate-messages`` Enable the regeneration of messages files from their messages source files, e.g. regenerate xxx_messages.h and xxx_messages.cc from xxx_messages.mes using the Kea message compiler. By default Kea is built using these .h and .cc files from the distribution. However, if anything in a .mes file is changed (such as adding a new message), the Kea message compiler needs to be built and used. This option permits that. - ``--with-benchmark``, ``--with-benchmark-source`` Enable the building of the database backend benchmarks using the Google Benchmark framework. This option specifies the path to the gtest source. (If the framework is not installed on the system, it can be downloaded from https://github.com/google/benchmark.) This support is experimental. As an example, the following command configures Kea to find the Boost headers in /usr/pkg/include, specifies that PostgreSQL support should be enabled, and sets the installation location to /opt/kea: .. code-block:: console $ ./configure \ --with-boost-include=/usr/pkg/include \ --with-pgsql=/usr/local/bin/pg_config \ --prefix=/opt/kea Users who have any problems with building Kea using the header-only Boost code, or who would like to use the Boost system library (assumed for the sake of this example to be located in /usr/pkg/lib), should issue these commands: .. code-block:: console $ ./configure \ --with-boost-libs=-lboost_system \ --with-boost-lib-dir=/usr/pkg/lib If ``configure`` fails, it may be due to missing or old dependencies. When ``configure`` succeeds, it displays a report with the parameters used to build the code. This report is saved into the file ``config.report`` and is also embedded into the executable binaries, e.g., ``kea-dhcp4``. Build ----- After the configure step is complete, build the executables from the C++ code and prepare the Python scripts by running the command: .. code-block:: console $ make Install ------- To install the Kea executables, support files, and documentation, issue the command: .. code-block:: console $ make install Do not use any form of parallel or job server options (such as GNU make's ``-j`` option) when performing this step; doing so may cause errors. .. note:: The install step may require superuser privileges. If required, run ``ldconfig`` as root with ``/usr/local/lib`` (or with prefix/lib if configured with ``--prefix``) in ``/etc/ld.so.conf`` (or the relevant linker cache configuration file for the OS): .. code-block:: console $ ldconfig .. .. note:: If ``ldconfig`` is not run where required, users may see errors like the following: :: program: error while loading shared libraries: libkea-something.so.1: cannot open shared object file: No such file or directory Cross-Building -------------- It is possible to cross-build Kea, i.e. to create binaries in a separate system (the ``build`` system) from the one where Kea runs (the ``host`` system). It is outside of the scope of common administrator operations and requires some developer skills, but the Developer Guide explains how to do that using an x86_64 Linux system to build Kea for a Raspberry Pi box running Raspbian: `Kea Cross-Compiling Example `__. .. _dhcp-install-configure: DHCP Database Installation and Configuration ============================================ Kea stores its leases in a lease database. The software has been written in a way that makes it possible to choose which database product should be used to store the lease information. Kea supports four database backends: MySQL, PostgreSQL, Cassandra[1], and memfile. To limit external dependencies, MySQL, PostgreSQL, and Cassandra support are disabled by default and only memfile is available. Support for the optional external database backend must be explicitly included when Kea is built. This section covers the building of Kea with one of the optional backends and the creation of the lease database. [1] As of Kea 1.9.9, support for Cassandra is deprecated. .. note:: When unit tests are built with Kea (i.e. the ``--with-gtest`` configuration option is specified), the databases must be manually pre-configured for the unit tests to run. The details of this configuration can be found in the `Kea Developer's Guide `__. Building with MySQL Support --------------------------- Install MySQL according to the instructions for the system. The client development libraries must be installed. Build and install Kea as described in :ref:`installation`, with the following modification. To enable the MySQL database code, at the "configure" step (see :ref:`configure`), the ``--with-mysql`` switch should be specified: .. code-block:: console $ ./configure [other-options] --with-mysql If MySQL was not installed in the default location, the location of the MySQL configuration program "mysql_config" should be included with the switch: .. code-block:: console $ ./configure [other-options] --with-mysql=path-to-mysql_config See :ref:`mysql-database-create` for details regarding MySQL database configuration. Building with PostgreSQL support -------------------------------- Install PostgreSQL according to the instructions for the system. The client development libraries must be installed. Client development libraries are often packaged as "libpq". Build and install Kea as described in :ref:`installation`, with the following modification. To enable the PostgreSQL database code, at the "configure" step (see :ref:`configure`), the ``--with-pgsql`` switch should be specified: .. code-block:: console $ ./configure [other-options] --with-pgsql If PostgreSQL was not installed in the default location, the location of the PostgreSQL configuration program "pg_config" should be included with the switch: .. code-block:: console $ ./configure [other-options] --with-pgsql=path-to-pg_config See :ref:`pgsql-database-create` for details regarding PostgreSQL database configuration. Building with CQL (Cassandra) Support ------------------------------------- As of Kea 1.9.9, support for Cassandra is deprecated. It is still available in current versions, but the support will be removed in a future version; new users are encouraged to choose an alternative. Install Cassandra according to the instructions for the system. The Cassandra project website contains useful pointers: https://cassandra.apache.org. If a cpp-driver package is available as binary or as source, simply install or build and install the package. Then build and install Kea as described in :ref:`installation`. To enable the Cassandra (CQL) database code, at the "configure" step (see :ref:`configure`), enter: .. code-block:: console $ ./configure [other-options] --with-cql=path-to-pkg-config If ``pkg-config`` is at its standard location (and thus in the shell path), the path does not need to be specified. If it does not work (e.g. no pkg-config, package not available in pkg-config with the cassandra name), the ``cql_config`` script in the tools/ directory can still be used as described below. Download and compile cpp-driver from DataStax. For details regarding dependencies for building cpp-driver, see the project homepage https://github.com/datastax/cpp-driver. .. code-block:: console $ git clone https://github.com/datastax/cpp-driver.git $ cd cpp-driver $ mkdir build $ cd build $ cmake .. $ make Kea's cpp-driver does not include the cql_config script. A cql_config script is present in the tools/ directory of the Kea sources. Before using it, please create a cql_config_defines.sh file in the same directory (there is an example available in cql_config_define.sh.sample; copy it over to cql_config_defines.sh and edit the path specified in it) and change the environment variable CPP_DRIVER_PATH to point to the directory where the cpp-driver sources are located. Make sure that appropriate access rights are set on this file; it should be executable by the system user building Kea. Build and install Kea as described in :ref:`installation`, with the following modification. To enable the Cassandra (CQL) database code, at the "configure" step (see :ref:`configure`), enter: .. code-block:: console $ ./configure [other-options] --with-cql=path-to-cql_config .. include:: hammer.rst .. _non-root: Running Kea From a Non-root Account on Linux ============================================ Both Kea DHCPv4 and DHCPv6 servers perform operations that in general require root access privileges. In particular, DHCPv4 opens raw sockets and both DHCPv4 and DHCPv6 open UDP sockets on privileged ports. However, with some extra system configuration, it is possible to run Kea from non-root accounts. First, a regular user account must be created: .. code-block:: console useradd admin Then, change the binaries' ownership and group to the new user. Note that the specific path may be different. Please refer to the ``--prefix`` parameter passed to the configure script: .. code-block:: console chown -R admin /opt/kea chgrp -R admin /opt/kea chown -R admin /var/log/kea-dhcp4.log chgrp -R admin /var/log/kea-dhcp4.log chown -R admin /var/log/kea-dhcp6.log chgrp -R admin /var/log/kea-dhcp6.log If using systemd, modify its service file (e.g. /etc/systemd/system/kea-dhcp6.service): .. code-block:: console User=admin Group=admin The most important step is to set the capabilities of the binaries. Refer to `man capabilities` to get more information. .. code-block:: console setcap 'cap_net_bind_service,cap_net_raw=+ep' /opt/kea/sbin/kea-dhcp4 setcap 'cap_net_bind_service=+ep' /opt/kea/sbin/kea-dhcp6 If using systemd, also add this to the service file (e.g. /etc/systemd/system/kea-dhcp6.service): .. code-block:: console ExecStartPre=setcap 'cap_net_bind_service=+ep' /opt/kea/sbin/kea-dhcp6 After this step is complete, the admin user should be able to run Kea. Note that the DHCPv4 server by default opens raw sockets. If the network is only using relayed traffic, Kea can be instructed to use regular UDP sockets (refer to ``dhcp-socket-type`` parameter in the :ref:`dhcp4-interface-configuration` section) and the ``cap_net_raw`` capability can be skipped. .. note:: It is possible to avoid running Kea with root privileges by instructing Kea to use non-privileged (greater than 1024) ports and redirecting traffic. This, however, only works for relayed traffic. This approach in general is considered experimental and has not been tested for deployment in production environments. Use with caution! To use this approach, configure the server to listen on other non-privileged ports (e.g. 1547 and 1548) by running the process with the ``-p`` option in ``/etc/systemd/system/kea-dhcp4.service``: .. code-block:: console ExecStart=/opt/kea/sbin/kea-dhcp4 -d -c /etc/kea/kea-dhcp4.conf -p 2067 and ``/etc/systemd/system/kea-dhcp4.service``: .. code-block:: console ExecStart=/opt/kea/sbin/kea-dhcp6 -d -c /etc/kea/kea-dhcp6.conf -p 1547 Then configure port redirection with iptables and ip6tables for new ports (e.g. 1547 and 1548). Be sure to replace ``ens4`` with the specific interface name. .. code-block:: console iptables -t nat -A PREROUTING -i ens4 -p udp --dport 67 -j REDIRECT --to-port 2067 iptables -t nat -A PREROUTING -i ens4 -p udp --dport 2068 -j REDIRECT --to-port 68 ip6tables -t nat -A PREROUTING -i ens4 -p udp --dport 547 -j REDIRECT --to-port 1547 ip6tables -t nat -A PREROUTING -i ens4 -p udp --dport 1548 -j REDIRECT --to-port 548 .. _deprecated: Deprecated Features =================== This section lists significant features that have been or will be removed. We try to deprecate features before removing them to signal to current users to plan a migration. New users should not rely on deprecated features. Cassandra (CQL) Support ----------------------- Cassandra is a non-relational NoSQL database. Kea added support for the CQL lease backend in Kea 1.1.0-beta1 and the CQL host backend in 1.4.0-beta1. This feature never gained much traction with users, particularly compared to the level of interest in and deployments of the alternatives, MySQL and PostgreSQL. The non-relational nature of Cassandra makes it exceedingly difficult to implement more complex DHCP features, such as the configuration backend. Cassandra also introduces performance degradation, is complicated to set up, and is an ongoing maintenance burden. Cassandra support is deprecated as of Kea 1.9.9. The feature will function as before in the Kea 2.0.x and 2.1.x series, but will print a warning. The feature will be removed entirely in a future release. Sysrepo 0.x ----------- Kea versions 1.9.9 and earlier required Sysrepo 0.7.x to run, when optional support for NETCONF was enabled. Kea versions 1.9.10 and later now require Sysrepo 1.4.x and the related libyang 1.x library to run. The earlier Sysrepo versions are no longer supported. The latest Sysrepo 2.x version does not provide C++ bindings, and as such, is not usable for Kea. kea-2.0.2/doc/sphinx/arm/hooks-stat-cmds.rst0000644000175000017500000002042514206773363015621 00000000000000.. _hooks-stat-cmds: stat_cmds: Supplemental Statistics Commands =========================================== This library provides additional commands for retrieving lease statistics from Kea DHCP servers. These commands were added to address an issue with obtaining accurate lease statistics in deployments running multiple Kea servers that use a shared lease backend. The in-memory statistics kept by individual servers only track lease changes made by that server; thus, in a deployment with multiple servers (e.g. two kea-dhcp6 servers using the same PostgreSQL database for lease storage), these statistics are incomplete. The MySQL and PostgreSQL backends in Kea track lease allocation changes as they occur via database triggers. Additionally, all four lease backends were extended to support retrieving lease statistics for all subnets, a single subnet, or a range of subnets. Finally, this library was constructed to provide commands for retrieving these statistics. .. note:: This library may only be loaded by the ``kea-dhcp4`` or ``kea-dhcp6`` process. The commands currently provided by this library are: - ``stat-lease4-get`` - fetches DHCPv4 lease statistics. - ``stat-lease6-get`` - fetches DHCPv6 lease statistics. The stat commands library is part of the open source code and is available to every Kea user. All commands use JSON syntax and can be issued directly to the servers via either the control channel (see :ref:`ctrl-channel`) or the Control Agent (see :ref:`kea-ctrl-agent`). This library may be loaded by both the kea-dhcp4 and kea-dhcp6 servers. It is loaded in the same way as other libraries and currently has no parameters: :: "Dhcp6": { "hooks-libraries": [ { "library": "/path/libdhcp_stat_cmds.so" } ... ] } In a deployment with multiple Kea DHCP servers sharing a common lease storage, this hooks library may be loaded by any or all of the servers. However, one thing to keep in mind is that a server's response to a stat-lease[46]-get command will only contain data for subnets known to that server. In other words, if a subnet does not appear in a server's configuration, Kea will not retrieve statistics for it. .. _command-stat-lease4-get: .. _command-stat-lease6-get: The stat-lease4-get, stat-lease6-get Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``stat-lease4-get`` and ``stat-lease6-get`` commands fetch lease statistics for a range of known subnets. The range of subnets is determined through the use of optional command input parameters: - ``subnet-id`` - the ID of the subnet for which lease statistics should be fetched. Use this to get statistics for a single subnet. If the subnet does not exist, the command result code is 3 (i.e. CONTROL_RESULT_EMPTY). - ``subnet-range`` - a pair of subnet IDs which describe an inclusive range of subnets for which statistics should be retrieved. The range may include one or more IDs that correspond to no subnet; in this case, the command will only output lease statistics for those that exist. However, if the range does not include any known subnets, the command result code is 3 (i.e. CONTROL_RESULT_EMPTY). - ``first-subnet-id`` - the ID of the first subnet in the range. - ``last-subnet-id`` - the ID of the last subnet in the range. The use of subnet-id and subnet-range are mutually exclusive. If no parameters are given, the result will contain data for all known subnets. Note that in configurations with large numbers of subnets, this can result in a large response. The following command fetches lease statistics for all known subnets from a kea-dhcp4 server: :: { "command": "stat-lease4-get" } The following command fetches lease statistics for subnet ID 10 from a kea-dhcp6 server: :: { "command": "stat-lease6-get", "arguments": { "subnet-id" : 10 } } The following command fetches lease statistics for all subnets with IDs in the range 10 through 50 from a kea-dhcp4 server: :: { "command": "stat-lease4-get", "arguments": { "subnet-range" { "first-subnet-id": 10, "last-subnet-id": 50, } } } The response to either command will contain three elements: - ``result`` - a numeric value indicating the outcome of the command where: - ``0`` - the command was successful; - ``1`` - an error occurred, and an explanation will be the "text" element; or - ``2`` - the fetch found no matching data. - ``text`` - an explanation of the command outcome. When the command succeeds it will contain the command name along with the number of rows returned. - ``arguments`` - a map containing the data returned by the command as the element "result-set", which is patterned after SQL statement responses: - ``columns`` - a list of text column labels. The columns returned for DHCPv4 are: - ``subnet-id`` - the ID of the subnet. - ``total-addresses`` - the total number of addresses available for DHCPv4 management in the subnet. In other words, this is the sum of all addresses in all the configured pools in the subnet. - ``cumulative-assigned-addresses`` - the cumulative number of addresses in the subnet that have been assigned to a client by the server since it started. - ``assigned-addresses`` - the number of addresses in the subnet that are currently assigned to a client. - ``declined-addresses`` - the number of addresses in the subnet that are currently declined and are thus unavailable for assignment. - The columns returned for DHCPv6 are: - ``subnet-id`` - the ID of the subnet. - ``total-nas`` - the number of NA addresses available for DHCPv6 management in the subnet. In other words, this is the sum of all the NA addresses in all the configured NA pools in the subnet. - ``cumulative-assigned-nas`` - the cumulative number of NA addresses in the subnet that have been assigned to a client by the server since it started. - ``assigned-nas`` - the number of NA addresses in the subnet that are currently assigned to a client. - ``declined-nas`` - the number of NA addresses that are currently declined and are thus unavailable for assignment. - ``total-pds`` - the total number of PD prefixes available of DHCPv6 management in the subnet. In other words, this is the sum of all prefixes in all the configured prefix pools in the subnet. - ``cumulative-assigned-pds`` - the cumulative number of PD prefixes in the subnet that have been assigned to a client by the server since it started. - ``assigned-pds`` - the number of PD prefixes in the subnet that are currently assigned to a client. - ``rows`` - a list of rows, one per subnet ID. Each row contains a data value corresponding to and in the same order as each column listed in "columns" for a given subnet. - ``timestamp`` - the textual date and time the data were fetched, expressed as GMT. The response to a DHCPv4 command might look as follows: :: { "result": 0, "text": "stat-lease4-get: 2 rows found", "arguments": { "result-set": { "columns": [ "subnet-id", "total-addresses", "cumulative-assigned-addresses", "assigned-addresses", "declined-addresses" ] "rows": [ [ 10, 256, 300, 111, 0 ], [ 20, 4098, 2034, 2034, 4 ] ], "timestamp": "2018-05-04 15:03:37.000000" } } } The response to a DHCPv6 command might look as follows (subnet 10 has no prefix pools, subnet 20 has no NA pools, and subnet 30 has both NA and PD pools): :: { "result": 0, "text": "stat-lease6-get: 2 rows found", "arguments": { "result-set": { "columns": [ "subnet-id", "total-nas", "cumulative-assigned-nas", "assigned-nas", "declined-nas", "total-pds", "cumulative-assigned-pds", "assigned-pds" ] "rows": [ [ 10, 4096, 5000, 2400, 3, 0, 0, 0], [ 20, 0, 0, 0, 0, 1048, 300, 233 ] [ 30, 256, 60, 60, 0, 1048, 15, 15 ] ], "timestamp": "2018-05-04 15:03:37.000000" } } } kea-2.0.2/doc/sphinx/arm/hammer.rst0000644000175000017500000000751414206773363014056 00000000000000.. _hammer: Hammer Building Tool ==================== An optional building tool called Hammer was introduced with Kea 1.6.0. It is a Python 3 script that lets users automate tasks related to building Kea, such as setting up virtual machines, installing Kea dependencies, compiling Kea with various options, running unit-tests and more. This tool was created primarily for internal QA purposes at ISC and it is not included in the Kea distribution. However, it is available in the Kea git repository. This tool was developed primarily for internal purposes and ISC cannot guarantee its proper operation. If you decide to use it, please do so with care. .. note:: Use of this tool is completely optional. Everything it does can be done manually. The first-time user is strongly encouraged to look at Hammer's built-in help: .. code-block:: console $ ./hammer.py --help It will list available parameters. Hammer is able to set up various operating systems running either in LXC or in VirtualBox. For a list of supported systems, use the ``supported-systems`` command: .. code-block:: console $ ./hammer.py supported-systems fedora: - 27: lxc, virtualbox - 28: lxc, virtualbox - 29: lxc, virtualbox centos: - 7: lxc, virtualbox rhel: - 8: virtualbox ubuntu: - 16.04: lxc, virtualbox - 18.04: lxc, virtualbox - 18.10: lxc, virtualbox debian: - 8: lxc, virtualbox - 9: lxc, virtualbox freebsd: - 11.2: virtualbox - 12.0: virtualbox It is also possible to run the build locally, in the current system (if the OS is supported). First, you must install the Hammer dependencies: Vagrant and either VirtualBox or LXC. To make life easier, Hammer can install Vagrant and the required Vagrant plugins using the command: .. code-block:: console $ ./hammer.py ensure-hammer-deps VirtualBox and LXC need to be installed manually. The basic functions provided by Hammer are to prepare the build environment and perform the actual build, and to run the unit tests locally in the current system. This can be achieved by running the command: .. code-block:: console $ ./hammer.py build -p local The scope of the process can be defined using --with (-w) and --without (-x) options. By default the build command will build Kea with documentation, install it locally, and run unit tests. To exclude the installation and generation of docs, type: .. code-block:: console $ ./hammer.py build -p local -x install docs The basic scope can be extended by: mysql, pgsql, cql, native-pkg, radius, shell, and forge. .. note:: To build Kea locally, Hammer dependencies like Vagrant are not needed. Hammer can be told to set up a new virtual machine with a specified operating system, without the build: .. code-block:: console $ ./hammer.py prepare-system -p virtualbox -s freebsd -r 12.0 This way we can prepare a system for our own use. To get to such a system using SSH, invoke: .. code-block:: console $ ./hammer.py ssh -p virtualbox -s freebsd -r 12.0 It is possible to speed up subsequent Hammer builds. To achieve this Hammer employs `ccache `__. During compilation, ccache stores objects in a shared folder. In subsequent runs, instead of doing an actual compilation, ccache returns the stored earlier objects. The cache with these objects for reuse needs to be stored outside of VM or LXC. To indicate the folder, you must indicate the --ccache-dir parameter for Hammer. In the indicated folder, there are separate stored objects for each target operating system. .. code-block:: console $ ./hammer.py build -p lxc -s ubuntu -r 18.04 --ccache-dir ~/kea-ccache .. .. note:: ccache is currently only supported for LXC in Hammer; support for VirtualBox may be added later. For more information check: .. code-block:: console $ ./hammer.py --help kea-2.0.2/doc/sphinx/arm/rst_arm_sources.mk0000644000175000017500000000273314206773363015614 00000000000000rst_arm_sources += arm/acknowledgments.rst rst_arm_sources += arm/admin.rst rst_arm_sources += arm/agent.rst rst_arm_sources += arm/classify.rst rst_arm_sources += arm/config-backend.rst rst_arm_sources += arm/config-templates.rst rst_arm_sources += arm/config.rst rst_arm_sources += arm/congestion-handling.rst rst_arm_sources += arm/ctrl-channel.rst rst_arm_sources += arm/database-connectivity.rst rst_arm_sources += arm/ddns.rst rst_arm_sources += arm/dhcp4-srv.rst rst_arm_sources += arm/dhcp6-srv.rst rst_arm_sources += arm/ext-gss-tsig.rst rst_arm_sources += arm/ext-netconf.rst rst_arm_sources += arm/hammer.rst rst_arm_sources += arm/hooks-bootp.rst rst_arm_sources += arm/hooks-cb-cmds.rst rst_arm_sources += arm/hooks-class-cmds.rst rst_arm_sources += arm/hooks-ha.rst rst_arm_sources += arm/hooks-host-cache.rst rst_arm_sources += arm/hooks-lease-cmds.rst rst_arm_sources += arm/hooks-lease-query.rst rst_arm_sources += arm/hooks-radius.rst rst_arm_sources += arm/hooks-run-script.rst rst_arm_sources += arm/hooks-stat-cmds.rst rst_arm_sources += arm/hooks.rst rst_arm_sources += arm/install.rst rst_arm_sources += arm/integrations.rst rst_arm_sources += arm/intro.rst rst_arm_sources += arm/keactrl.rst rst_arm_sources += arm/lease-expiration.rst rst_arm_sources += arm/lfc.rst rst_arm_sources += arm/logging.rst rst_arm_sources += arm/quickstart.rst rst_arm_sources += arm/security.rst rst_arm_sources += arm/shell.rst rst_arm_sources += arm/stats.rst rst_arm_sources += arm/stork.rst kea-2.0.2/doc/sphinx/arm/hooks-bootp.rst0000644000175000017500000000513114206773363015042 00000000000000.. _hooks-bootp: BOOTP Support ============= .. note:: This library is still in the experimental phase. Use with care! This hooks library adds support for BOOTP with vendor information extensions (`RFC 1497 `__). Received BOOTP requests are recognized, translated into DHCPREQUEST packets by adding a dhcp-message-type option and put into the "BOOTP" client class. Members of this class get infinite lifetime leases but the class can be used too for instance to guard a pool of addresses. The DHCP specific options, such as dhcp-message-type, are removed from the server's responses and responses shorter than the BOOTP minimum size (300 octets) are padded to this size. The library is available since Kea 1.7.2 and can be loaded in a similar way to other hook libraries by the ``kea-dhcp4`` process. It takes no parameter. :: "Dhcp4": { "hooks-libraries": [ { "library": "/usr/local/lib/libdhcp_bootp.so" }, ... ] } .. note:: This library is only meant to be loaded by the ``kea-dhcp4`` process as there is no BOOTP protocol for IPv6. .. note:: A host reservation for a BOOTP client should use the hardware address as the identifier (the client-id option is a DHCP specific option). .. _hooks-bootp-config: Incoming BOOTP packets are added to the BOOTP class. This can be used to segregate BOOTP clients to separate pools. For example you can do the following: :: "Dhcp4": { "client-classes": [ { // The DHCP class is the complement of the BOOTP class "name": "DHCP", "test": "not member('BOOTP')" } ], "subnet4": [ { "subnet": "192.0.2.0/24", "pools": [ { // BOOTP clients will be handled here "pool": "192.0.2.200 - 192.0.2.254", "client-class": "BOOTP" }, { // Regular DHCP clients will be handled here "pool": "192.0.2.1 - 192.0.2.199", "client-class": "DHCP" }], ... }, ... ], ... } .. _hooks-bootp-limitations: BOOTP Hooks Limitation ~~~~~~~~~~~~~~~~~~~~~~ Currently the BOOTP library has the following limitation: - A basic BOOTP as defined in `RFC 951 `__ is not supported. Kea only supports the BOOTP with vendor information extensions. Depending on the demand, this may or may not be implemented in the future. kea-2.0.2/doc/sphinx/arm/dhcp4-srv.rst0000644000175000017500000122756214206773363014427 00000000000000.. _dhcp4: ***************** The DHCPv4 Server ***************** .. _dhcp4-start-stop: Starting and Stopping the DHCPv4 Server ======================================= It is recommended that the Kea DHCPv4 server be started and stopped using ``keactrl`` (described in :ref:`keactrl`); however, it is also possible to run the server directly. It accepts the following command-line switches: - ``-c file`` - specifies the configuration file. This is the only mandatory switch. - ``-d`` - specifies whether the server logging should be switched to debug/verbose mode. In verbose mode, the logging severity and debuglevel specified in the configuration file are ignored; "debug" severity and the maximum debuglevel (99) are assumed. The flag is convenient for temporarily switching the server into maximum verbosity, e.g. when debugging. - ``-p server-port`` - specifies the local UDP port on which the server will listen. This is only useful during testing, as a DHCPv4 server listening on ports other than the standard ones is not able to handle regular DHCPv4 queries. - ``-P client-port`` - specifies the remote UDP port to which the server will send all responses. This is only useful during testing, as a DHCPv4 server sending responses to ports other than the standard ones is not able to handle regular DHCPv4 queries. - ``-t file`` - specifies a configuration file to be tested. ``kea-dhcp4`` loads it, checks it, and exits. During the test, log messages are printed to standard output and error messages to standard error. The result of the test is reported through the exit code (0 = configuration looks OK, 1 = error encountered). The check is not comprehensive; certain checks are possible only when running the server. - ``-v`` - displays the Kea version and exits. - ``-V`` - displays the Kea extended version with additional parameters and exits. The listing includes the versions of the libraries dynamically linked to Kea. - ``-W`` - displays the Kea configuration report and exits. The report is a copy of the ``config.report`` file produced by ``./configure``; it is embedded in the executable binary. On startup, the server detects available network interfaces and attempts to open UDP sockets on all interfaces mentioned in the configuration file. Since the DHCPv4 server opens privileged ports, this daemon must be run as root. During startup, the server attempts to create a PID file of the form ``[runstatedir]/kea/[conf name].kea-dhcp4.pid``, where: - ``runstatedir`` is the value as passed into the build configure script; it defaults to ``/usr/local/var/run``. Note that this value may be overridden at runtime by setting the environment variable ``KEA_PIDFILE_DIR``, although this is intended primarily for testing purposes. - ``conf name`` is the configuration file name used to start the server, minus all preceding paths and the file extension. For example, given a pathname of ``/usr/local/etc/kea/myconf.txt``, the portion used would be ``myconf``. If the file already exists and contains the PID of a live process, the server issues a DHCP4_ALREADY_RUNNING log message and exits. It is possible, though unlikely, that the file is a remnant of a system crash and the process to which the PID belongs is unrelated to Kea. In such a case, it would be necessary to manually delete the PID file. The server can be stopped using the ``kill`` command. When running in a console, the server can also be shut down by pressing ctrl-c. Kea detects the key combination and shuts down gracefully. .. _dhcp4-configuration: DHCPv4 Server Configuration =========================== Introduction ------------ This section explains how to configure the Kea DHCPv4 server using a configuration file. Before DHCPv4 is started, its configuration file must be created. The basic configuration is as follows: :: { # DHCPv4 configuration starts on the next line "Dhcp4": { # First we set up global values "valid-lifetime": 4000, "renew-timer": 1000, "rebind-timer": 2000, # Next we set up the interfaces to be used by the server. "interfaces-config": { "interfaces": [ "eth0" ] }, # And we specify the type of lease database "lease-database": { "type": "memfile", "persist": true, "name": "/var/lib/kea/dhcp4.leases" }, # Finally, we list the subnets from which we will be leasing addresses. "subnet4": [ { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ] } ] # DHCPv4 configuration ends with the next line } } The following paragraphs provide a brief overview of the parameters in the above example, along with their format. Subsequent sections of this chapter go into much greater detail for these and other parameters. The lines starting with a hash (#) are comments and are ignored by the server; they do not impact its operation in any way. The configuration starts in the first line with the initial opening curly bracket (or brace). Each configuration must contain an object specifying the configuration of the Kea module using it. In the example above, this object is called ``Dhcp4``. .. note:: In the current Kea release it is possible to specify configurations of multiple modules within a single configuration file, but this is not recommended. Support for this type of configuration was removed in the 1.7.10 release, including the ``Logging`` object; its previous content, the list of loggers, must now be inside the ``Dhcp4`` object. The Dhcp4 configuration starts with the ``"Dhcp4": {`` line and ends with the corresponding closing brace (in the above example, the brace after the last comment). Everything defined between those lines is considered to be the Dhcp4 configuration. In general, the order in which those parameters appear does not matter, but there are two caveats. The first one is that the configuration file must be well-formed JSON, meaning that the parameters for any given scope must be separated by a comma, and there must not be a comma after the last parameter. When reordering a configuration file, keep in mind that moving a parameter to or from the last position in a given scope may also require moving the comma. The second caveat is that it is uncommon — although legal JSON — to repeat the same parameter multiple times. If that happens, the last occurrence of a given parameter in a given scope is used, while all previous instances are ignored. This is unlikely to cause any confusion as there are no real-life reasons to keep multiple copies of the same parameter in the configuration file. The first few DHCPv4 configuration elements define some global parameters. ``valid-lifetime`` defines how long the addresses (leases) given out by the server are valid; the default is for a client to be allowed to use a given address for 4000 seconds. (Note that integer numbers are specified as is, without any quotes around them.) ``renew-timer`` and ``rebind-timer`` are values (also in seconds) that define the T1 and T2 timers that govern when the client begins the renewal and rebind processes. .. note:: Beginning with Kea 1.6.0, the lease valid-lifetime is extended from a single value to a triplet with minimum, default, and maximum values using ``min-valid-lifetime``, ``valid-lifetime``, and ``max-valid-lifetime``. As of Kea 1.9.5, these values may be specified in client classes. The procedure the server uses to select which lifetime value to use is as follows: If the client query is a BOOTP query, the server always uses the infinite lease time (e.g. 0xffffffff). Otherwise, the server must determine which configured triplet to use by first searching all classes assigned to the query, and then the subnet selected for the query. Classes are searched in the order they were assigned to the query; the server uses the triplet from the first class that specifies it. If no classes specify the triplet, the server uses the triplet specified by the subnet selected for the client. If the subnet does not explicitly specify it, the server next looks at the subnet's shared-network (if one exists), then for a global specification, and finally the global default. If the client requested a lifetime value via DHCP option 51, then the lifetime value used is the requested value bounded by the configured triplet. In other words, if the requested lifetime is less than the configured minimum, the configured minimum is used; if it is more than the configured maximum, the configured maximum is used. If the client did not provide a requested value, the lifetime value used is the triplet default value. .. note:: Both ``renew-timer`` and ``rebind-timer`` are optional. The server only sends ``rebind-timer`` to the client, via DHCPv4 option code 59, if it is less than ``valid-lifetime``; and it only sends ``renew-timer``, via DHCPv4 option code 58, if it is less than ``rebind-timer`` (or ``valid-lifetime`` if ``rebind-timer`` was not specified). In their absence, the client should select values for T1 and T2 timers according to `RFC 2131 `_. See section :ref:`dhcp4-t1-t2-times` for more details on generating T1 and T2. The ``interfaces-config`` map specifies the server configuration concerning the network interfaces on which the server should listen to the DHCP messages. The ``interfaces`` parameter specifies a list of network interfaces on which the server should listen. Lists are opened and closed with square brackets, with elements separated by commas. To listen on two interfaces, the ``interfaces-config`` command should look like this: :: "interfaces-config": { "interfaces": [ "eth0", "eth1" ] }, The next couple of lines define the lease database, the place where the server stores its lease information. This particular example tells the server to use ``memfile``, which is the simplest and fastest database backend. It uses an in-memory database and stores leases on disk in a CSV (comma-separated values) file. This is a very simple configuration example; usually the lease database configuration is more extensive and contains additional parameters. Note that ``lease-database`` is an object and opens up a new scope, using an opening brace. Its parameters (just one in this example: ``type``) follow. If there were more than one, they would be separated by commas. This scope is closed with a closing brace. As more parameters for the Dhcp4 definition follow, a trailing comma is present. Finally, we need to define a list of IPv4 subnets. This is the most important DHCPv4 configuration structure, as the server uses that information to process clients' requests. It defines all subnets from which the server is expected to receive DHCP requests. The subnets are specified with the ``subnet4`` parameter. It is a list, so it starts and ends with square brackets. Each subnet definition in the list has several attributes associated with it, so it is a structure and is opened and closed with braces. At a minimum, a subnet definition has to have at least two parameters: ``subnet``, which defines the whole subnet; and ``pools``, which is a list of dynamically allocated pools that are governed by the DHCP server. The example contains a single subnet. If more than one were defined, additional elements in the ``subnet4`` parameter would be specified and separated by commas. For example, to define three subnets, the following syntax would be used: :: "subnet4": [ { "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ], "subnet": "192.0.2.0/24" }, { "pools": [ { "pool": "192.0.3.100 - 192.0.3.200" } ], "subnet": "192.0.3.0/24" }, { "pools": [ { "pool": "192.0.4.1 - 192.0.4.254" } ], "subnet": "192.0.4.0/24" } ] Note that indentation is optional and is used for aesthetic purposes only. In some cases it may be preferable to use more compact notation. After all the parameters have been specified, there are two contexts open: global and Dhcp4; thus, two closing curly brackets are needed to close them. Lease Storage ------------- All leases issued by the server are stored in the lease database. Currently there are four database backends available: memfile (the default), MySQL, PostgreSQL, and Cassandra (deprecated). Memfile - Basic Storage for Leases ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The server is able to store lease data in different repositories. Larger deployments may elect to store leases in a database; :ref:`database-configuration4` describes this option. In typical smaller deployments, though, the server stores lease information in a CSV file rather than a database. As well as requiring less administration, an advantage of using a file for storage is that it eliminates a dependency on third-party database software. The configuration of the memfile backend is controlled through the Dhcp4/lease-database parameters. The ``type`` parameter is mandatory and specifies which storage for leases the server should use, through the ``memfile`` value. The following list gives additional optional parameters that can be used to configure the memfile backend. - ``persist``: controls whether the new leases and updates to existing leases are written to the file. It is strongly recommended that the value of this parameter be set to ``true`` at all times during the server's normal operation. Not writing leases to disk means that if a server is restarted (e.g. after a power failure), it will not know which addresses have been assigned. As a result, it may assign new clients addresses that are already in use. The value of ``false`` is mostly useful for performance-testing purposes. The default value of the ``persist`` parameter is ``true``, which enables writing lease updates to the lease file. - ``name``: specifies an absolute location of the lease file in which new leases and lease updates will be recorded. The default value for this parameter is ``"[kea-install-dir]/var/lib/kea/kea-leases4.csv"``. - ``lfc-interval``: specifies the interval, in seconds, at which the server will perform a lease file cleanup (LFC). This removes redundant (historical) information from the lease file and effectively reduces the lease file size. The cleanup process is described in more detail later in this section. The default value of the ``lfc-interval`` is ``3600``. A value of 0 disables the LFC. - ``max-row-errors``: specifies the number of row errors before the server stops attempting to load a lease file. When the server loads a lease file, it is processed row by row, each row containing a single lease. If a row is flawed and cannot be processed correctly the server logs it, discards the row, and goes on to the next row. This parameter can be used to set a limit on the number of such discards that can occur, after which the server abandons the effort and exits. The default value of 0 disables the limit and allows the server to process the entire file, regardless of how many rows are discarded. :: "Dhcp4": { "lease-database": { "type": "memfile", "persist": true, "name": "/tmp/kea-leases4.csv", "lfc-interval": 1800, "max-row-errors": 100 } } This configuration selects ``/tmp/kea-leases4.csv`` as the storage for lease information and enables persistence (writing lease updates to this file). It also configures the backend to perform a periodic cleanup of the lease file every 1800 seconds (30 minutes) and sets the maximum number of row errors to 100. Why Is Lease File Cleanup Necessary? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It is important to know how the lease file contents are organized to understand why the periodic lease file cleanup is needed. Every time the server updates a lease or creates a new lease for a client, the new lease information must be recorded in the lease file. For performance reasons, the server does not update the existing client's lease in the file, as this would potentially require rewriting the entire file. Instead, it simply appends the new lease information to the end of the file; the previous lease entries for the client are not removed. When the server loads leases from the lease file, e.g. at server startup, it assumes that the latest lease entry for the client is the valid one. Previous entries are discarded, meaning that the server can reconstruct accurate information about the leases even though there may be many lease entries for each client. However, storing many entries for each client results in a bloated lease file and impairs the performance of the server's startup and reconfiguration, as it needs to process a larger number of lease entries. Lease file cleanup (LFC) removes all previous entries for each client and leaves only the latest ones. The interval at which the cleanup is performed is configurable, and it should be selected according to the frequency of lease renewals initiated by the clients. The more frequent the renewals, the smaller the value of ``lfc-interval`` should be. Note, however, that the LFC takes time and thus it is possible (although unlikely) that, if the ``lfc-interval`` is too short, a new cleanup may be started while the previous one is still running. The server would recover from this by skipping the new cleanup when it detected that the previous cleanup was still in progress, but it implies that the actual cleanups will be triggered more rarely than the configured interval. Moreover, triggering a new cleanup adds overhead to the server, which is not able to respond to new requests for a short period of time when the new cleanup process is spawned. Therefore, it is recommended that the ``lfc-interval`` value be selected in a way that allows the LFC to complete the cleanup before a new cleanup is triggered. Lease file cleanup is performed by a separate process (in the background) to avoid a performance impact on the server process. To avoid conflicts between two processes using the same lease files, the LFC process starts with Kea opening a new lease file; the actual LFC process operates on the lease file that is no longer used by the server. There are also other files created as a side effect of the lease file cleanup. The detailed description of the LFC process is located later in this Kea Administrator's Reference Manual: :ref:`kea-lfc`. .. _database-configuration4: Lease Database Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. note:: Lease database access information must be configured for the DHCPv4 server, even if it has already been configured for the DHCPv6 server. The servers store their information independently, so each server can use a separate database or both servers can use the same database. .. note:: Kea requires the database timezone to match the system timezone. For more details, see :ref:`mysql-database-create` and :ref:`pgsql-database-create`. Lease database configuration is controlled through the Dhcp4/``lease-database`` parameters. The database type must be set to ``memfile``, ``mysql``, ``postgresql``, or `cql``, e.g.: :: "Dhcp4": { "lease-database": { "type": "mysql", ... }, ... } Next, the name of the database to hold the leases must be set; this is the name used when the database was created (see :ref:`mysql-database-create`, :ref:`pgsql-database-create`, or :ref:`cql-database-create`). For MySQL or PostgreSQL: :: "Dhcp4": { "lease-database": { "name": "database-name" , ... }, ... } For Cassandra: :: "Dhcp4": { "lease-database": { "keyspace": "database-name" , ... }, ... } If the database is located on a different system from the DHCPv4 server, the database host name must also be specified: :: "Dhcp4": { "lease-database": { "host": "remote-host-name", ... }, ... } (It should be noted that this configuration may have a severe impact on server performance.) Normally, the database is on the same machine as the DHCPv4 server. In this case, set the value to the empty string: :: "Dhcp4": { "lease-database": { "host" : "", ... }, ... } Should the database use a port other than the default, it may be specified as well: :: "Dhcp4": { "lease-database": { "port" : 12345, ... }, ... } Should the database be located on a different system, the administrator may need to specify a longer interval for the connection timeout: :: "Dhcp4": { "lease-database": { "connect-timeout" : timeout-in-seconds, ... }, ... } The default value of five seconds should be more than adequate for local connections. If a timeout is given, though, it should be an integer greater than zero. The maximum number of times the server automatically attempts to reconnect to the lease database after connectivity has been lost may be specified: :: "Dhcp4": { "lease-database": { "max-reconnect-tries" : number-of-tries, ... }, ... } If the server is unable to reconnect to the database after making the maximum number of attempts, the server will exit. A value of zero (the default) disables automatic recovery and the server will exit immediately upon detecting a loss of connectivity (MySQL and PostgreSQL only). For Cassandra, Kea uses an interface that connects to all nodes in a cluster at the same time. Any connectivity issues should be handled by internal Cassandra mechanisms. The number of milliseconds the server waits between attempts to reconnect to the lease database after connectivity has been lost may also be specified: :: "Dhcp4": { "lease-database": { "reconnect-wait-time" : number-of-milliseconds, ... }, ... } The default value for MySQL and PostgreSQL is 0, which disables automatic recovery and causes the server to exit immediately upon detecting the loss of connectivity. The default value for Cassandra is 2000 ms. :: "Dhcp4": { "lease-database": { "on-fail" : "stop-retry-exit", ... }, ... } The possible values are: - ``stop-retry-exit`` - disables the DHCP service while trying to automatically recover lost connections. Shuts down the server on failure after exhausting ``max-reconnect-tries``. This is the default value for MySQL and PostgreSQL. - ``serve-retry-exit`` - continues the DHCP service while trying to automatically recover lost connections. Shuts down the server on failure after exhausting ``max-reconnect-tries``. - ``serve-retry-continue`` - continues the DHCP service and does not shut down the server even if the recovery fails. .. note:: Automatic reconnection to database backends is configured individually per backend. This allows users to tailor the recovery parameters to each backend they use. We do suggest that users enable it either for all backends or none, so behavior is consistent. Losing connectivity to a backend for which reconnect is disabled results (if configured) in the server shutting itself down. This includes cases when the lease database backend and the hosts database backend are connected to the same database instance. It is highly recommended not to change the ``stop-retry-exit`` default setting for the lease manager, as it is critical for the connection to be active while processing DHCP traffic. Change this only if the server is used exclusively as a configuration tool. The host parameter is used by the MySQL and PostgreSQL backends. Cassandra has a concept of contact points that can be used to contact the cluster, instead of a single IP or hostname. It takes a list of comma-separated IP addresses, which may be specified as: :: "Dhcp4": { "lease-database": { "contact-points" : "192.0.2.1,192.0.2.2", ... }, ... } Finally, the credentials of the account under which the server will access the database should be set: :: "Dhcp4": { "lease-database": { "user": "user-name", "password": "password", ... }, ... } If there is no password to the account, set the password to the empty string ``""``. (This is the default.) .. _cassandra-database-configuration4: Cassandra-Specific Parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Cassandra backend is configured slightly differently. Cassandra has a concept of contact points that can be used to contact the cluster, instead of a single IP or hostname. It takes a list of comma-separated IP addresses, which may be specified as: :: "Dhcp4": { "lease-database": { "type": "cql", "contact-points": "ip-address1, ip-address2 [,...]", ... }, ... } Cassandra also supports a number of optional parameters: - ``reconnect-wait-time`` - governs how long Kea waits before attempting to reconnect. Expressed in milliseconds. The default is 2000 [ms]. - ``connect-timeout`` - sets the timeout for connecting to a node. Expressed in milliseconds. The default is 5000 [ms]. - ``request-timeout`` - sets the timeout for waiting for a response from a node. Expressed in milliseconds. The default is 12000 [ms]. - ``tcp-keepalive`` - governs the TCP keep-alive mechanism. Expressed in seconds of delay. If the parameter is not present, the mechanism is disabled. - ``tcp-nodelay`` - enables/disables Nagle's algorithm on connections. The default is ``true``. - ``consistency`` - configures the consistency level. The default is "quorum". Supported values: any, one, two, three, quorum, all, local-quorum, each-quorum, serial, local-serial, local-one. See `Cassandra consistency `__ for more details. - ``serial-consistency`` - configures the serial consistency level, which manages lightweight transaction isolation. The default is "serial". Supported values: any, one, two, three, quorum, all, local-quorum, each-quorum, serial, local-serial, local-one. See `Cassandra serial consistency `__ for more details. For example, a complex Cassandra configuration with most parameters specified could look as follows: :: "Dhcp4": { "lease-database": { "type": "cql", "keyspace": "keatest", "contact-points": "192.0.2.1, 192.0.2.2, 192.0.2.3", "port": 9042, "reconnect-wait-time": 2000, "connect-timeout": 5000, "request-timeout": 12000, "tcp-keepalive": 1, "tcp-nodelay": true }, ... } Similar parameters can be specified for the hosts database. .. _hosts4-storage: Hosts Storage ------------- Kea is also able to store information about host reservations in the database. The hosts database configuration uses the same syntax as the lease database. In fact, the Kea server opens independent connections for each purpose, be it lease or hosts information, which gives the most flexibility. Kea can keep leases and host reservations separately, but can also point to the same database. Currently the supported hosts database types are MySQL, PostgreSQL, and Cassandra. The following configuration can be used to configure a connection to MySQL: :: "Dhcp4": { "hosts-database": { "type": "mysql", "name": "kea", "user": "kea", "password": "secret123", "host": "localhost", "port": 3306 } } Please note that usage of hosts storage is optional. A user can define all host reservations in the configuration file, and that is the recommended way if the number of reservations is small. However, when the number of reservations grows, it is more convenient to use host storage. Please note that both storage methods (the configuration file and one of the supported databases) can be used together. If hosts are defined in both places, the definitions from the configuration file are checked first and external storage is checked later, if necessary. Host information can be placed in multiple stores. Operations are performed on the stores in the order they are defined in the configuration file, although this leads to a restriction in ordering in the case of a host reservation addition; read-only stores must be configured after a (required) read-write store, or the addition will fail. .. note:: Kea requires the database timezone to match the system timezone. For more details, see :ref:`mysql-database-create` and :ref:`pgsql-database-create`. .. _hosts-databases-configuration4: DHCPv4 Hosts Database Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Hosts database configuration is controlled through the Dhcp4/``hosts-database`` parameters. If enabled, the type of database must be set to ``mysql`` or ``postgresql``. :: "Dhcp4": { "hosts-database": { "type": "mysql", ... }, ... } Next, the name of the database to hold the reservations must be set; this is the name used when the lease database was created (see :ref:`supported-databases` for instructions on how to set up the desired database type): :: "Dhcp4": { "hosts-database": { "name": "database-name" , ... }, ... } If the database is located on a different system than the DHCPv4 server, the database host name must also be specified: :: "Dhcp4": { "hosts-database": { "host": remote-host-name, ... }, ... } (Again, it should be noted that this configuration may have a severe impact on server performance.) Normally, the database is on the same machine as the DHCPv4 server. In this case, set the value to the empty string: :: "Dhcp4": { "hosts-database": { "host" : "", ... }, ... } Should the database use a port different than the default, it may be specified as well: :: "Dhcp4": { "hosts-database": { "port" : 12345, ... }, ... } The maximum number of times the server automatically attempts to reconnect to the host database after connectivity has been lost may be specified: :: "Dhcp4": { "hosts-database": { "max-reconnect-tries" : number-of-tries, ... }, ... } If the server is unable to reconnect to the database after making the maximum number of attempts, the server will exit. A value of zero (the default) disables automatic recovery and the server will exit immediately upon detecting a loss of connectivity (MySQL and PostgreSQL only). The number of milliseconds the server waits between attempts to reconnect to the host database after connectivity has been lost may also be specified: :: "Dhcp4": { "hosts-database": { "reconnect-wait-time" : number-of-milliseconds, ... }, ... } The default value for MySQL and PostgreSQL is 0, which disables automatic recovery and causes the server to exit immediately upon detecting the loss of connectivity. The default value for Cassandra is 2000 ms. :: "Dhcp4": { "hosts-database": { "on-fail" : "stop-retry-exit", ... }, ... } The possible values are: - ``stop-retry-exit`` - disables the DHCP service while trying to automatically recover lost connections. Shuts down the server on failure after exhausting ``max-reconnect-tries``. This is the default value for MySQL and PostgreSQL. - ``serve-retry-exit`` - continues the DHCP service while trying to automatically recover lost connections. Shuts down the server on failure after exhausting ``max-reconnect-tries``. - ``serve-retry-continue`` - continues the DHCP service and does not shut down the server even if the recovery fails. .. note:: Automatic reconnection to database backends is configured individually per backend. This allows users to tailor the recovery parameters to each backend they use. We do suggest that users enable it either for all backends or none, so behavior is consistent. Losing connectivity to a backend for which reconnect is disabled results (if configured) in the server shutting itself down. This includes cases when the lease database backend and the hosts database backend are connected to the same database instance. Finally, the credentials of the account under which the server will access the database should be set: :: "Dhcp4": { "hosts-database": { "user": "user-name", "password": "password", ... }, ... } If there is no password to the account, set the password to the empty string ``""``. (This is the default.) The multiple storage extension uses a similar syntax; a configuration is placed into a ``hosts-databases`` list instead of into a ``hosts-database`` entry, as in: :: "Dhcp4": { "hosts-databases": [ { "type": "mysql", ... }, ... ], ... } For Cassandra-specific parameters, see :ref:`cassandra-database-configuration4`. If the same host is configured both in-file and in-database, Kea does not issue a warning, as it would if both were specified in the same data source. Instead, the host configured in-file has priority over the one configured in-database. .. _read-only-database-configuration4: Using Read-Only Databases for Host Reservations With DHCPv4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In some deployments, the user whose name is specified in the database backend configuration may not have write privileges to the database. This is often required by the policy within a given network to secure the data from being unintentionally modified. In many cases administrators have deployed inventory databases, which contain substantially more information about the hosts than just the static reservations assigned to them. The inventory database can be used to create a view of a Kea hosts database and such a view is often read-only. Kea host-database backends operate with an implicit configuration to both read from and write to the database. If the database user does not have write access to the host database, the backend will fail to start and the server will refuse to start (or reconfigure). However, if access to a read-only host database is required for retrieving reservations for clients and/or assigning specific addresses and options, it is possible to explicitly configure Kea to start in "read-only" mode. This is controlled by the ``readonly`` boolean parameter as follows: :: "Dhcp4": { "hosts-database": { "readonly": true, ... }, ... } Setting this parameter to ``false`` configures the database backend to operate in "read-write" mode, which is also the default configuration if the parameter is not specified. .. note:: The ``readonly`` parameter is only supported for MySQL and PostgreSQL databases. .. _dhcp4-interface-configuration: Interface Configuration ----------------------- The DHCPv4 server must be configured to listen on specific network interfaces. The simplest network interface configuration tells the server to listen on all available interfaces: :: "Dhcp4": { "interfaces-config": { "interfaces": [ "*" ] } ... }, The asterisk plays the role of a wildcard and means "listen on all interfaces." However, it is usually a good idea to explicitly specify interface names: :: "Dhcp4": { "interfaces-config": { "interfaces": [ "eth1", "eth3" ] }, ... } It is possible to use an interface wildcard (*) concurrently with explicit interface names: :: "Dhcp4": { "interfaces-config": { "interfaces": [ "eth1", "eth3", "*" ] }, ... } This form of usage should only be used when it is desired to temporarily override a list of interface names and listen on all interfaces. Some deployments of DHCP servers require that the servers listen on interfaces with multiple IPv4 addresses configured. In these situations, the address to use can be selected by appending an IPv4 address to the interface name in the following manner: :: "Dhcp4": { "interfaces-config": { "interfaces": [ "eth1/10.0.0.1", "eth3/192.0.2.3" ] }, ... } Should the server be required to listen on multiple IPv4 addresses assigned to the same interface, multiple addresses can be specified for an interface as in the example below: :: "Dhcp4": { "interfaces-config": { "interfaces": [ "eth1/10.0.0.1", "eth1/10.0.0.2" ] }, ... } Alternatively, if the server should listen on all addresses for the particular interface, an interface name without any address should be specified. Kea supports responding to directly connected clients which do not have an address configured. This requires the server to inject the hardware address of the destination into the data-link layer of the packet being sent to the client. The DHCPv4 server uses raw sockets to achieve this, and builds the entire IP/UDP stack for the outgoing packets. The downside of raw socket use, however, is that incoming and outgoing packets bypass the firewalls (e.g. iptables). Handling traffic on multiple IPv4 addresses assigned to the same interface can be a challenge, as raw sockets are bound to the interface. When the DHCP server is configured to use the raw socket on an interface to receive DHCP traffic, advanced packet filtering techniques (e.g. the BPF) must be used to receive unicast traffic on the desired addresses assigned to the interface. Whether clients use the raw socket or the UDP socket depends on whether they are directly connected (raw socket) or relayed (either raw or UDP socket). Therefore, in deployments where the server does not need to provision the directly connected clients and only receives the unicast packets from the relay agents, the Kea server should be configured to use UDP sockets instead of raw sockets. The following configuration demonstrates how this can be achieved: :: "Dhcp4": { "interfaces-config": { "interfaces": [ "eth1", "eth3" ], "dhcp-socket-type": "udp" }, ... } The ``dhcp-socket-type`` parameter specifies that the IP/UDP sockets will be opened on all interfaces on which the server listens, i.e. "eth1" and "eth3" in this example. If ``dhcp-socket-type`` is set to ``raw``, it configures the server to use raw sockets instead. If the ``dhcp-socket-type`` value is not specified, the default value ``raw`` is used. Using UDP sockets automatically disables the reception of broadcast packets from directly connected clients. This effectively means that UDP sockets can be used for relayed traffic only. When using raw sockets, both the traffic from the directly connected clients and the relayed traffic are handled. Caution should be taken when configuring the server to open multiple raw sockets on the interface with several IPv4 addresses assigned. If the directly connected client sends the message to the broadcast address, all sockets on this link will receive this message and multiple responses will be sent to the client. Therefore, the configuration with multiple IPv4 addresses assigned to the interface should not be used when the directly connected clients are operating on that link. To use a single address on such an interface, the "interface-name/address" notation should be used. .. note:: Specifying the value ``raw`` as the socket type does not guarantee that raw sockets will be used! The use of raw sockets to handle traffic from the directly connected clients is currently supported on Linux and BSD systems only. If raw sockets are not supported on the particular OS in use, the server will issue a warning and fall back to using IP/UDP sockets. In a typical environment, the DHCP server is expected to send back a response on the same network interface on which the query was received. This is the default behavior. However, in some deployments it is desired that the outbound (response) packets be sent as regular traffic and the outbound interface be determined by the routing tables. This kind of asymmetric traffic is uncommon, but valid. Kea supports a parameter called ``outbound-interface`` that controls this behavior. It supports two values: the first one, ``same-as-inbound``, tells Kea to send back the response on the same interface where the query packet was received. This is the default behavior. The second parameter, ``use-routing``, tells Kea to send regular UDP packets and let the kernel's routing table determine the most appropriate interface. This only works when ``dhcp-socket-type`` is set to ``udp``. An example configuration looks as follows: :: "Dhcp4": { "interfaces-config": { "interfaces": [ "eth1", "eth3" ], "dhcp-socket-type": "udp", "outbound-interface": "use-routing" }, ... } Interfaces are re-detected at each reconfiguration. This behavior can be disabled by setting the ``re-detect`` value to ``false``, for instance: :: "Dhcp4": { "interfaces-config": { "interfaces": [ "eth1", "eth3" ], "re-detect": false }, ... } Note that interfaces are not re-detected during ``config-test``. Usually loopback interfaces (e.g. the `lo` or `lo0` interface) are not configured, but if a loopback interface is explicitly configured and IP/UDP sockets are specified, the loopback interface is accepted. For example, this setup can be used to run Kea in a FreeBSD jail having only a loopback interface, to service a relayed DHCP request: :: "Dhcp4": { "interfaces-config": { "interfaces": [ "lo0" ], "dhcp-socket-type": "udp" }, ... } .. _dhcpinform-unicast-issues: Issues With Unicast Responses to DHCPINFORM ------------------------------------------- The use of UDP sockets has certain benefits in deployments where the server receives only relayed traffic; these benefits are mentioned in :ref:`dhcp4-interface-configuration`. From the administrator's perspective it is often desirable to configure the system's firewall to filter out unwanted traffic, and the use of UDP sockets facilitates this. However, the administrator must also be aware of the implications related to filtering certain types of traffic, as it may impair the DHCP server's operation. In this section we focus on the case when the server receives the DHCPINFORM message from the client via a relay. According to `RFC 2131 `__, the server should unicast the DHCPACK response to the address carried in the ``ciaddr`` field. When the UDP socket is in use, the DHCP server relies on the low-level functions of an operating system to build the data link, IP, and UDP layers of the outgoing message. Typically, the OS first uses ARP to obtain the client's link-layer address to be inserted into the frame's header, if the address is not cached from a previous transaction that the client had with the server. When the ARP exchange is successful, the DHCP message can be unicast to the client, using the obtained address. Some system administrators block ARP messages in their network, which causes issues for the server when it responds to the DHCPINFORM messages because the server is unable to send the DHCPACK if the preceding ARP communication fails. Since the OS is entirely responsible for the ARP communication and then sending the DHCP packet over the wire, the DHCP server has no means to determine that the ARP exchange failed and the DHCP response message was dropped. Thus, the server does not log any error messages when the outgoing DHCP response is dropped. At the same time, all hooks pertaining to the packet-sending operation will be called, even though the message never reaches its destination. Note that the issue described in this section is not observed when raw sockets are in use, because, in this case, the DHCP server builds all the layers of the outgoing message on its own and does not use ARP. Instead, it inserts the value carried in the ``chaddr`` field of the DHCPINFORM message into the link layer. Server administrators willing to support DHCPINFORM messages via relays should not block ARP traffic in their networks, or should use raw sockets instead of UDP sockets. .. _ipv4-subnet-id: IPv4 Subnet Identifier ---------------------- The subnet identifier (subnet ID) is a unique number associated with a particular subnet. In principle, it is used to associate clients' leases with their respective subnets. When a subnet identifier is not specified for a subnet being configured, it is automatically assigned by the configuration mechanism. The identifiers are assigned starting at 1 and are monotonically increased for each subsequent subnet: 1, 2, 3 .... If there are multiple subnets configured with auto-generated identifiers and one of them is removed, the subnet identifiers may be renumbered. For example: if there are four subnets and the third is removed, the last subnet will be assigned the identifier that the third subnet had before removal. As a result, the leases stored in the lease database for subnet 3 are now associated with subnet 4, something that may have unexpected consequences. The only remedy for this issue at present is to manually specify a unique identifier for each subnet. .. note:: Subnet IDs must be greater than zero and less than 4294967295. The following configuration assigns the specified subnet identifier to a newly configured subnet: :: "Dhcp4": { "subnet4": [ { "subnet": "192.0.2.0/24", "id": 1024, ... } ] } This identifier will not change for this subnet unless the ``id`` parameter is removed or set to 0. The value of 0 forces auto-generation of the subnet identifier. .. _ipv4-subnet-prefix: IPv4 Subnet Prefix ------------------ The subnet prefix is the second way to identify a subnet. It does not need to have the address part to match the prefix length; for instance, this configuration is accepted: :: "Dhcp4": { "subnet4": [ { "subnet": "192.0.2.1/24", ... } ] } This works even if there is another subnet with the "192.0.2.0/24" prefix; only the textual form of subnets are compared to avoid duplicates. .. note:: Abuse of this feature can lead to incorrect subnet selection (see :ref:`dhcp4-subnet-selection`). .. _dhcp4-address-config: Configuration of IPv4 Address Pools ----------------------------------- The main role of a DHCPv4 server is address assignment. For this, the server must be configured with at least one subnet and one pool of dynamic addresses to be managed. For example, assume that the server is connected to a network segment that uses the 192.0.2.0/24 prefix. The administrator of that network decides that addresses from range 192.0.2.10 to 192.0.2.20 are going to be managed by the Dhcp4 server. Such a configuration can be achieved in the following way: :: "Dhcp4": { "subnet4": [ { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ], ... } ] } Note that ``subnet`` is defined as a simple string, but the ``pools`` parameter is actually a list of pools; for this reason, the pool definition is enclosed in square brackets, even though only one range of addresses is specified. Each ``pool`` is a structure that contains the parameters that describe a single pool. Currently there is only one parameter, ``pool``, which gives the range of addresses in the pool. It is possible to define more than one pool in a subnet; continuing the previous example, further assume that 192.0.2.64/26 should also be managed by the server. It could be written as 192.0.2.64 to 192.0.2.127, or it can be expressed more simply as 192.0.2.64/26. Both formats are supported by Dhcp4 and can be mixed in the pool list. For example, one could define the following pools: :: "Dhcp4": { "subnet4": [ { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.10-192.0.2.20" }, { "pool": "192.0.2.64/26" } ], ... } ], ... } White space in pool definitions is ignored, so spaces before and after the hyphen are optional. They can be used to improve readability. The number of pools is not limited, but for performance reasons it is recommended to use as few as possible. The server may be configured to serve more than one subnet: :: "Dhcp4": { "subnet4": [ { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ], ... }, { "subnet": "192.0.3.0/24", "pools": [ { "pool": "192.0.3.100 - 192.0.3.200" } ], ... }, { "subnet": "192.0.4.0/24", "pools": [ { "pool": "192.0.4.1 - 192.0.4.254" } ], ... } ] } When configuring a DHCPv4 server using prefix/length notation, please pay attention to the boundary values. When specifying that the server can use a given pool, it is also able to allocate the first (typically a network address) and the last (typically a broadcast address) address from that pool. In the aforementioned example of pool 192.0.3.0/24, both the 192.0.3.0 and 192.0.3.255 addresses may be assigned as well. This may be invalid in some network configurations. To avoid this, use the "min-max" notation. .. _dhcp4-t1-t2-times: Sending T1 (Option 58) and T2 (Option 59) ----------------------------------------- According to `RFC 2131 `__, servers should send values for T1 and T2 that are 50% and 87.5% of the lease lifetime, respectively. By default, ``kea-dhcp4`` does not send either value; it can be configured to send values that are either specified explicitly or that are calculated as percentages of the lease time. The server's behavior is governed by a combination of configuration parameters, two of which have already been mentioned. To send specific, fixed values use the following two parameters: - ``renew-timer`` - specifies the value of T1 in seconds. - ``rebind-timer`` - specifies the value of T2 in seconds. The server only sends T2 if it is less than the valid lease time. T1 is only sent if T2 is being sent and T1 is less than T2; or T2 is not being sent and T1 is less than the valid lease time. Calculating the values is controlled by the following three parameters. - ``calculate-tee-times`` - when true, T1 and T2 are calculated as percentages of the valid lease time. It defaults to false. - ``t1-percent`` - the percentage of the valid lease time to use for T1. It is expressed as a real number between 0.0 and 1.0 and must be less than ``t2-percent``. The default value is 0.50, per RFC 2131. - ``t2-percent`` - the percentage of the valid lease time to use for T2. It is expressed as a real number between 0.0 and 1.0 and must be greater than ``t1-percent``. The default value is .875, per RFC 2131. .. .. note:: In the event that both explicit values are specified and ``calculate-tee-times`` is true, the server will use the explicit values. Administrators with a setup where some subnets or shared-networks use explicit values and some use calculated values must not define the explicit values at any level higher than where they will be used. Inheriting them from too high a scope, such as global, will cause them to have explicit values at every level underneath (shared-networks and subnets), effectively disabling calculated values. .. _dhcp4-std-options: Standard DHCPv4 Options ----------------------- One of the major features of the DHCPv4 server is the ability to provide configuration options to clients. Most of the options are sent by the server only if the client explicitly requests them using the Parameter Request List option. Those that do not require inclusion in the Parameter Request List option are commonly used options, e.g. "Domain Server", and options which require special behavior, e.g. "Client FQDN", which is returned to the client if the client has included this option in its message to the server. :ref:`dhcp4-std-options-list` comprises the list of the standard DHCPv4 options whose values can be configured using the configuration structures described in this section. This table excludes the options which require special processing and thus cannot be configured with fixed values. The last column of the table indicates which options can be sent by the server even when they are not requested in the Parameter Request List option, and those which are sent only when explicitly requested. The following example shows how to configure the addresses of DNS servers, which is one of the most frequently used options. Options specified in this way are considered global and apply to all configured subnets. :: "Dhcp4": { "option-data": [ { "name": "domain-name-servers", "code": 6, "space": "dhcp4", "csv-format": true, "data": "192.0.2.1, 192.0.2.2" }, ... ] } Note that either ``name`` or ``code`` is required; there is no need to specify both. ``space`` has a default value of "dhcp4", so this can be skipped as well if a regular (not encapsulated) DHCPv4 option is defined. Finally, ``csv-format`` defaults to "true", so it too can be skipped, unless the option value is specified as a hexadecimal string. Therefore, the above example can be simplified to: :: "Dhcp4": { "option-data": [ { "name": "domain-name-servers", "data": "192.0.2.1, 192.0.2.2" }, ... ] } Defined options are added to the response when the client requests them, with a few exceptions which are always added. To enforce the addition of a particular option, set the ``always-send`` flag to "true" as in: :: "Dhcp4": { "option-data": [ { "name": "domain-name-servers", "data": "192.0.2.1, 192.0.2.2", "always-send": true }, ... ] } The effect is the same as if the client added the option code in the Parameter Request List option (or its equivalent for vendor options): :: "Dhcp4": { "option-data": [ { "name": "domain-name-servers", "data": "192.0.2.1, 192.0.2.2", "always-send": true }, ... ], "subnet4": [ { "subnet": "192.0.3.0/24", "option-data": [ { "name": "domain-name-servers", "data": "192.0.3.1, 192.0.3.2" }, ... ], ... }, ... ], ... } The ``domain-name-servers`` option is always added to responses (the always-send is "sticky"), but the value is the subnet one when the client is localized in the subnet. The ``name`` parameter specifies the option name. For a list of currently supported names, see :ref:`dhcp4-std-options-list` below. The ``code`` parameter specifies the option code, which must match one of the values from that list. The next line specifies the option space, which must always be set to "dhcp4" as these are standard DHCPv4 options. For other option spaces, including custom option spaces, see :ref:`dhcp4-option-spaces`. The next line specifies the format in which the data will be entered; use of CSV (comma-separated values) is recommended. The sixth line gives the actual value to be sent to clients. The data parameter is specified as normal text, with values separated by commas if more than one value is allowed. Options can also be configured as hexadecimal values. If ``csv-format`` is set to "false", option data must be specified as a hexadecimal string. The following commands configure the ``domain-name-servers`` option for all subnets with the following addresses: 192.0.3.1 and 192.0.3.2. Note that ``csv-format`` is set to "false". :: "Dhcp4": { "option-data": [ { "name": "domain-name-servers", "code": 6, "space": "dhcp4", "csv-format": false, "data": "C0 00 03 01 C0 00 03 02" }, ... ], ... } Kea supports the following formats when specifying hexadecimal data: - ``Delimited octets`` - one or more octets separated by either colons or spaces (':' or ' '). While each octet may contain one or two digits, we strongly recommend always using two digits. Valid examples are `ab:cd:ef` and `ab cd ef`. - ``String of digits`` - a continuous string of hexadecimal digits with or without a `0x` prefix. Valid examples are `0xabcdef` and `abcdef`. Care should be taken to use proper encoding when using hexadecimal format; Kea's ability to validate data correctness in hexadecimal is limited. As of Kea 1.6.0, it is also possible to specify data for binary options as a single-quoted text string within double quotes as shown (note that ``csv-format`` must be set to false): :: "Dhcp4": { "option-data": [ { "name": "user-class", "code": 77, "space": "dhcp4", "csv-format": false, "data": "'convert this text to binary'" }, ... ], ... } Most of the parameters in the ``option-data`` structure are optional and can be omitted in some circumstances, as discussed in :ref:`dhcp4-option-data-defaults`. It is possible to specify or override options on a per-subnet basis. If clients connected to most subnets are expected to get the same values of a given option, administrators should use global options. On the other hand, if different values are used in each subnet, it does not make sense to specify global option values; rather, only subnet-specific ones should be set. The following commands override the global DNS servers option for a particular subnet, setting a single DNS server with address 192.0.2.3: :: "Dhcp4": { "subnet4": [ { "option-data": [ { "name": "domain-name-servers", "code": 6, "space": "dhcp4", "csv-format": true, "data": "192.0.2.3" }, ... ], ... }, ... ], ... } In some cases it is useful to associate some options with an address pool from which a client is assigned a lease. Pool-specific option values override subnet-specific and global option values; it is not possible to prioritize assignment of pool-specific options via the order of pool declarations in the server configuration. The following configuration snippet demonstrates how to specify the DNS servers option, which is assigned to a client only if the client obtains an address from the given pool: :: "Dhcp4": { "subnet4": [ { "pools": [ { "pool": "192.0.2.1 - 192.0.2.200", "option-data": [ { "name": "domain-name-servers", "data": "192.0.2.3" }, ... ], ... }, ... ], ... }, ... ], ... } Options can also be specified in class or host reservation scope. The current Kea options precedence order is (from most important to least): host reservation, pool, subnet, shared network, class, global. The currently supported standard DHCPv4 options are listed in :ref:`dhcp4-std-options-list`. "Name" and "Code" are the values that should be used as a name/code in the option-data structures. "Type" designates the format of the data; the meanings of the various types are given in :ref:`dhcp-types`. When a data field is a string and that string contains the comma (,; U+002C) character, the comma must be escaped with two backslashes (\; U+005C). This double escape is required because both the routine splitting of CSV data into fields and JSON use the same escape character; a single escape (\,) would make the JSON invalid. For example, the string "foo,bar" must be represented as: :: "Dhcp4": { "subnet4": [ { "pools": [ { "option-data": [ { "name": "boot-file-name", "data": "foo\\,bar" } ] }, ... ], ... }, ... ], ... } Some options are designated as arrays, which means that more than one value is allowed. For example, the option ``time-servers`` allows the specification of more than one IPv4 address, enabling clients to obtain the addresses of multiple NTP servers. :ref:`dhcp4-custom-options` describes the configuration syntax to create custom option definitions (formats). Creation of custom definitions for standard options is generally not permitted, even if the definition being created matches the actual option format defined in the RFCs. There is an exception to this rule for standard options for which Kea currently does not provide a definition. To use such options, a server administrator must create a definition as described in :ref:`dhcp4-custom-options` in the "dhcp4" option space. This definition should match the option format described in the relevant RFC, but the configuration mechanism will allow any option format as it currently has no means to validate it. .. _dhcp4-std-options-list: .. table:: List of Standard DHCPv4 Options Configurable by an Administrator +----------------------------------------+------+---------------------------+-------------+-------------+ | Name | Code | Type | Array? | Returned if | | | | | | not | | | | | | requested? | +========================================+======+===========================+=============+=============+ | time-offset | 2 | int32 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | routers | 3 | ipv4-address | true | true | +----------------------------------------+------+---------------------------+-------------+-------------+ | time-servers | 4 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | name-servers | 5 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | domain-name-servers | 6 | ipv4-address | true | true | +----------------------------------------+------+---------------------------+-------------+-------------+ | log-servers | 7 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | cookie-servers | 8 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | lpr-servers | 9 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | impress-servers | 10 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | resource-location-servers | 11 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | boot-size | 13 | uint16 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | merit-dump | 14 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | domain-name | 15 | fqdn | false | true | +----------------------------------------+------+---------------------------+-------------+-------------+ | swap-server | 16 | ipv4-address | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | root-path | 17 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | extensions-path | 18 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | ip-forwarding | 19 | boolean | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | non-local-source-routing | 20 | boolean | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | policy-filter | 21 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | max-dgram-reassembly | 22 | uint16 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | default-ip-ttl | 23 | uint8 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | path-mtu-aging-timeout | 24 | uint32 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | path-mtu-plateau-table | 25 | uint16 | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | interface-mtu | 26 | uint16 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | all-subnets-local | 27 | boolean | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | broadcast-address | 28 | ipv4-address | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | perform-mask-discovery | 29 | boolean | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | mask-supplier | 30 | boolean | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | router-discovery | 31 | boolean | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | router-solicitation-address | 32 | ipv4-address | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | static-routes | 33 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | trailer-encapsulation | 34 | boolean | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | arp-cache-timeout | 35 | uint32 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | ieee802-3-encapsulation | 36 | boolean | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | default-tcp-ttl | 37 | uint8 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | tcp-keepalive-interval | 38 | uint32 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | tcp-keepalive-garbage | 39 | boolean | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | nis-domain | 40 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | nis-servers | 41 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | ntp-servers | 42 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | vendor-encapsulated-options | 43 | empty | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | netbios-name-servers | 44 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | netbios-dd-server | 45 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | netbios-node-type | 46 | uint8 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | netbios-scope | 47 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | font-servers | 48 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | x-display-manager | 49 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | dhcp-option-overload | 52 | uint8 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | dhcp-server-identifier | 54 | ipv4-address | false | true | +----------------------------------------+------+---------------------------+-------------+-------------+ | dhcp-message | 56 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | dhcp-max-message-size | 57 | uint16 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | vendor-class-identifier | 60 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | nwip-domain-name | 62 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | nwip-suboptions | 63 | binary | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | nisplus-domain-name | 64 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | nisplus-servers | 65 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | tftp-server-name | 66 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | boot-file-name | 67 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | mobile-ip-home-agent | 68 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | smtp-server | 69 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | pop-server | 70 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | nntp-server | 71 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | www-server | 72 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | finger-server | 73 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | irc-server | 74 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | streettalk-server | 75 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | streettalk-directory-assistance-server | 76 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | user-class | 77 | binary | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | slp-directory-agent | 78 | record (boolean, | true | false | | | | ipv4-address) | | | +----------------------------------------+------+---------------------------+-------------+-------------+ | slp-service-scope | 79 | record (boolean, string) | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | nds-server | 85 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | nds-tree-name | 86 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | nds-context | 87 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | bcms-controller-names | 88 | fqdn | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | bcms-controller-address | 89 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | client-system | 93 | uint16 | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | client-ndi | 94 | record (uint8, uint8, | false | false | | | | uint8) | | | +----------------------------------------+------+---------------------------+-------------+-------------+ | uuid-guid | 97 | record (uint8, binary) | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | uap-servers | 98 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | geoconf-civic | 99 | binary | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | pcode | 100 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | tcode | 101 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | v6-only-preferred | 108 | uint32 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | netinfo-server-address | 112 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | netinfo-server-tag | 113 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | default-url | 114 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | auto-config | 116 | uint8 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | name-service-search | 117 | uint16 | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | domain-search | 119 | fqdn | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | vivco-suboptions | 124 | record (uint32, binary) | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | vivso-suboptions | 125 | uint32 | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | pana-agent | 136 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | v4-lost | 137 | fqdn | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | capwap-ac-v4 | 138 | ipv4-address | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | sip-ua-cs-domains | 141 | fqdn | true | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | rdnss-selection | 146 | record (uint8, | true | false | | | | ipv4-address, | | | | | | ipv4-address, fqdn) | | | +----------------------------------------+------+---------------------------+-------------+-------------+ | v4-portparams | 159 | record (uint8, psid) | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | v4-captive-portal | 160 | string | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ | option-6rd | 212 | record (uint8, uint8, | true | false | | | | ipv6-address, | | | | | | ipv4-address) | | | +----------------------------------------+------+---------------------------+-------------+-------------+ | v4-access-domain | 213 | fqdn | false | false | +----------------------------------------+------+---------------------------+-------------+-------------+ Kea also supports other options than those listed above; the following options are returned by the Kea engine itself and in general should not be configured manually. .. table:: List of Standard DHCPv4 Options Managed by Kea on Its Own and Not Directly Configurable by an Administrator +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | Name | Code | Type | Description | +================================+=======+=======================================+===================================================================+ | subnet-mask | 1 | ipv4-address | calculated automatically, based on subnet definition. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | host-name | 12 | string | sent by client, generally governed by the DNS configuration. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | dhcp-requested-address | 50 | ipv6-address | may be sent by the client and the server should not set it. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | dhcp-lease-time | 51 | uint32 | set automatically based on the ``valid-lifetime`` parameter. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | dhcp-message-type | 53 | string | sent by clients and servers. Set by the Kea engine depending on | | | | | the situation and should never be configured explicitly. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | dhcp-parameter-request-list | 55 | uint8 array | sent by clients and should never be sent by the server. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | dhcp-renewal-time | 58 | uint32 | governed by ``renew-timer`` parameter. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | dhcp-rebinding-time | 59 | uint32 | governed by ``rebind-timer`` parameter. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | dhcp-client-identifier | 61 | binary | sent by client, echoed back with the value sent by the client. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | fqdn | 81 | record (uint8, uint8, uint8, fqdn) | part of the DDNS and D2 configuration. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | dhcp-agent-options | 82 | empty | sent by the relay agent. This is an empty container option; see | | | | | RAI option detail in later part of this section. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | authenticate | 90 | binary | sent by client, kea does not validate it yet. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | client-last-transaction-time | 91 | uint32 | sent by client, server does not set it. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | associated-ip | 92 | ipv4-address array | sent by client, server responds with list of addresses. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ | subnet-selection | 118 | ipv4-address | if present in client's messages, will be used in the subnet | | | | | selection process. | +--------------------------------+-------+---------------------------------------+-------------------------------------------------------------------+ The following table lists all option types used in the previous two tables with a description of what values are accepted for them. .. _dhcp-types: .. table:: List of Standard DHCP Option Types +-----------------+-------------------------------------------------------+ | Name | Meaning | +=================+=======================================================+ | binary | An arbitrary string of bytes, specified as a set | | | of hexadecimal digits. | +-----------------+-------------------------------------------------------+ | boolean | A boolean value with allowed | | | values true or false. | +-----------------+-------------------------------------------------------+ | empty | No value; data is carried in | | | sub-options. | +-----------------+-------------------------------------------------------+ | fqdn | Fully qualified domain name (e.g. | | | www.example.com). | +-----------------+-------------------------------------------------------+ | ipv4-address | IPv4 address in the usual | | | dotted-decimal notation (e.g. | | | 192.0.2.1). | +-----------------+-------------------------------------------------------+ | ipv6-address | IPv6 address in the usual colon | | | notation (e.g. 2001:db8::1). | +-----------------+-------------------------------------------------------+ | ipv6-prefix | IPv6 prefix and prefix length | | | specified using CIDR notation, | | | e.g. 2001:db8:1::/64. This data | | | type is used to represent an | | | 8-bit field conveying a prefix | | | length and the variable length | | | prefix value. | +-----------------+-------------------------------------------------------+ | psid | PSID and PSID length separated by | | | a slash, e.g. 3/4 specifies | | | PSID=3 and PSID length=4. In the | | | wire format it is represented by | | | an 8-bit field carrying PSID | | | length (in this case equal to 4) | | | and the 16-bits-long PSID value | | | field (in this case equal to | | | "0011000000000000b" using binary | | | notation). Allowed values for a | | | PSID length are 0 to 16. See `RFC | | | 7597 `__ | | | for details about the PSID wire | | | representation. | +-----------------+-------------------------------------------------------+ | record | Structured data that may be | | | comprised of any types (except | | | "record" and "empty"). The array | | | flag applies to the last field | | | only. | +-----------------+-------------------------------------------------------+ | string | Any text. Please note that Kea | | | silently discards any | | | terminating/trailing nulls from | | | the end of 'string' options when | | | unpacking received packets. This | | | is in keeping with `RFC 2132, | | | Section | | | 2 `__. | +-----------------+-------------------------------------------------------+ | tuple | A length encoded as an 8- (16- | | | for DHCPv6) bit unsigned integer | | | followed by a string of this | | | length. | +-----------------+-------------------------------------------------------+ | uint8 | 8-bit unsigned integer with | | | allowed values 0 to 255. | +-----------------+-------------------------------------------------------+ | uint16 | 16-bit unsigned integer with | | | allowed values 0 to 65535. | +-----------------+-------------------------------------------------------+ | uint32 | 32-bit unsigned integer with | | | allowed values 0 to 4294967295. | +-----------------+-------------------------------------------------------+ | int8 | 8-bit signed integer with allowed | | | values -128 to 127. | +-----------------+-------------------------------------------------------+ | int16 | 16-bit signed integer with | | | allowed values -32768 to 32767. | +-----------------+-------------------------------------------------------+ | int32 | 32-bit signed integer with | | | allowed values -2147483648 to | | | 2147483647. | +-----------------+-------------------------------------------------------+ Kea also supports the Relay Agent Information (RAI) option, sometimes referred to as the relay option, agent option, or simply option 82. The option itself is just a container and does not convey any information on its own. The following table contains a list of RAI sub-options that Kea can understand. The RAI and its sub-options are inserted by the relay agent and received by Kea; there is no need for Kea to be configured with those options. .. table:: List of RAI Sub-options That Kea Can Understand. +--------------------+------+----------------------------------------------------------------------+ | Name | Code | Comment | +====================+======+======================================================================+ | circuit-id | 1 | Used when host-reservation-identifiers is set to `circuit-id`. | +--------------------+------+----------------------------------------------------------------------+ | remote-id | 2 | Can be used with flex-id to identify hosts. | +--------------------+------+----------------------------------------------------------------------+ | link selection | 5 | If present, is used to select the appropriate subnet. | +--------------------+------+----------------------------------------------------------------------+ | subscriber-id | 6 | Can be used with flex-id to identify hosts. | +--------------------+------+----------------------------------------------------------------------+ | relay-source-port | 19 | If sent by the relay, Kea sends back its responses to this port. | +--------------------+------+----------------------------------------------------------------------+ All other RAI sub-options can be used in client classification to classify incoming packets to specific classes and/or by flex-id to construct a unique device identifier. .. _dhcp4-custom-options: Custom DHCPv4 Options --------------------- Kea supports custom (non-standard) DHCPv4 options. Let's say that we want to define a new DHCPv4 option called "foo", which will have code 222 and will convey a single, unsigned, 32-bit integer value. We can define such an option by putting the following entry in the configuration file: :: "Dhcp4": { "option-def": [ { "name": "foo", "code": 222, "type": "uint32", "array": false, "record-types": "", "space": "dhcp4", "encapsulate": "" }, ... ], ... } The ``false`` value of the ``array`` parameter determines that the option does NOT comprise an array of "uint32" values but is, instead, a single value. Two other parameters have been left blank: ``record-types`` and ``encapsulate``. The former specifies the comma-separated list of option data fields, if the option comprises a record of data fields. The ``record-types`` value should be non-empty if ``type`` is set to "record"; otherwise it must be left blank. The latter parameter specifies the name of the option space being encapsulated by the particular option. If the particular option does not encapsulate any option space, the parameter should be left blank. Note that the ``option-def`` configuration statement only defines the format of an option and does not set its value(s). The ``name``, ``code``, and ``type`` parameters are required; all others are optional. The ``array`` default value is ``false``. The ``record-types`` and ``encapsulate`` default values are blank (``""``). The default ``space`` is "dhcp4". Once the new option format is defined, its value is set in the same way as for a standard option. For example, the following commands set a global value that applies to all subnets. :: "Dhcp4": { "option-data": [ { "name": "foo", "code": 222, "space": "dhcp4", "csv-format": true, "data": "12345" }, ... ], ... } New options can take more complex forms than the simple use of primitives (uint8, string, ipv4-address, etc.); it is possible to define an option comprising a number of existing primitives. For example, say we want to define a new option that will consist of an IPv4 address, followed by an unsigned 16-bit integer, followed by a boolean value, followed by a text string. Such an option could be defined in the following way: :: "Dhcp4": { "option-def": [ { "name": "bar", "code": 223, "space": "dhcp4", "type": "record", "array": false, "record-types": "ipv4-address, uint16, boolean, string", "encapsulate": "" }, ... ], ... } The ``type`` is set to "record" to indicate that the option contains multiple values of different types. These types are given as a comma-separated list in the ``record-types`` field and should be ones from those listed in :ref:`dhcp-types`. The values of the option are set in an ``option-data`` statement as follows: :: "Dhcp4": { "option-data": [ { "name": "bar", "space": "dhcp4", "code": 223, "csv-format": true, "data": "192.0.2.100, 123, true, Hello World" } ], ... } ``csv-format`` is set to ``true`` to indicate that the ``data`` field comprises a comma-separated list of values. The values in ``data`` must correspond to the types set in the ``record-types`` field of the option definition. When ``array`` is set to ``true`` and ``type`` is set to "record", the last field is an array, i.e. it can contain more than one value, as in: :: "Dhcp4": { "option-def": [ { "name": "bar", "code": 223, "space": "dhcp4", "type": "record", "array": true, "record-types": "ipv4-address, uint16", "encapsulate": "" }, ... ], ... } The new option content is one IPv4 address followed by one or more 16- bit unsigned integers. .. note:: In general, boolean values are specified as ``true`` or ``false``, without quotes. Some specific boolean parameters may also accept ``"true"``, ``"false"``, ``0``, ``1``, ``"0"``, and ``"1"``. .. note:: Numbers can be specified in decimal or hexadecimal format. The hexadecimal format can be either plain (e.g. abcd) or prefixed with 0x (e.g. 0xabcd). .. _dhcp4-private-opts: DHCPv4 Private Options ---------------------- Options with a code between 224 and 254 are reserved for private use. They can be defined at the global scope or at the client-class local scope; this allows option definitions to be used depending on context, and option data to be set accordingly. For instance, to configure an old PXEClient vendor: :: "Dhcp4": { "client-classes": [ { "name": "pxeclient", "test": "option[vendor-class-identifier].text == 'PXEClient'", "option-def": [ { "name": "configfile", "code": 209, "type": "string" } ], ... }, ... ], ... } As the Vendor-Specific Information (VSI) option (code 43) has vendor-specific format, i.e. can carry either raw binary value or sub-options, this mechanism is also available for this option. In the following example taken from a real configuration, two vendor classes use option 43 for different and incompatible purposes: :: "Dhcp4": { "option-def": [ { "name": "cookie", "code": 1, "type": "string", "space": "APC" }, { "name": "mtftp-ip", "code": 1, "type": "ipv4-address", "space": "PXE" }, ... ], "client-classes": [ { "name": "APC", "test": "option[vendor-class-identifier].text == 'APC'", "option-def": [ { "name": "vendor-encapsulated-options", "type": "empty", "encapsulate": "APC" } ], "option-data": [ { "name": "cookie", "space": "APC", "data": "1APC" }, { "name": "vendor-encapsulated-options" }, ... ], ... }, { "name": "PXE", "test": "option[vendor-class-identifier].text == 'PXE'", "option-def": [ { "name": "vendor-encapsulated-options", "type": "empty", "encapsulate": "PXE" } ], "option-data": [ { "name": "mtftp-ip", "space": "PXE", "data": "0.0.0.0" }, { "name": "vendor-encapsulated-options" }, ... ], ... }, ... ], ... } The definition used to decode a VSI option is: 1. The local definition of a client class the incoming packet belongs to; 2. If none, the global definition; 3. If none, the last-resort definition described in the next section, :ref:`dhcp4-vendor-opts` (backward-compatible with previous Kea versions). .. note:: This last-resort definition for the Vendor-Specific Information option (code 43) is not compatible with a raw binary value. When there are known cases where a raw binary value will be used, a client class must be defined with both a classification expression matching these cases and an option definition for the VSI option with a binary type and no encapsulation. .. note:: By default, in the Vendor-Specific Information option (code 43), sub-option code 0 and 255 mean PAD and END respectively, according to `RFC 2132 `_. In other words, the sub-option code values of 0 and 255 are reserved. Kea does, however, allow users to define sub-option codes from 0 to 255. If sub-options with codes 0 and/or 255 are defined, bytes with that value are no longer treated as a PAD or an END, but as the sub-option code when parsing a VSI option in an incoming query. Option 43 input processing (also called unpacking) is deferred so that it happens after classification. This means clients cannot be classified using option 43 sub-options. The definition used to unpack option 43 is determined as follows: - If defined at the global scope, this definition is used. - If defined at client class scope and the packet belongs to this class, the client class definition is used. - If not defined at global scope nor in a client class to which the packet belongs, the built-in last resort definition is used. This definition only says the sub-option space is "vendor-encapsulated-options-space". The output definition selection is a bit simpler: - If the packet belongs to a client class which defines the option 43, use this definition. - If defined at the global scope, use this definition. - Otherwise, use the built-in last-resort definition. Since they use a specific/per vendor option space, sub-options are defined at the global scope. .. note:: Option definitions in client classes are allowed only for this limited option set (codes 43 and from 224 to 254), and only for DHCPv4. .. _dhcp4-vendor-opts: DHCPv4 Vendor-Specific Options ------------------------------ Currently there are two option spaces defined for the DHCPv4 daemon: "dhcp4" (for the top-level DHCPv4 options) and "vendor-encapsulated-options-space", which is empty by default but in which options can be defined. Those options are carried in the Vendor-Specific Information option (code 43). The following examples show how to define an option "foo" with code 1 that comprises an IPv4 address, an unsigned 16-bit integer, and a string. The "foo" option is conveyed in a Vendor-Specific Information option. The first step is to define the format of the option: :: "Dhcp4": { "option-def": [ { "name": "foo", "code": 1, "space": "vendor-encapsulated-options-space", "type": "record", "array": false, "record-types": "ipv4-address, uint16, string", "encapsulate": "" } ], ... } (Note that the option space is set to ``vendor-encapsulated-options-space``.) Once the option format is defined, the next step is to define actual values for that option: :: "Dhcp4": { "option-data": [ { "name": "foo", "space": "vendor-encapsulated-options-space", "code": 1, "csv-format": true, "data": "192.0.2.3, 123, Hello World" } ], ... } In this example, we also include the Vendor-Specific Information option, which conveys our sub-option "foo". This is required; otherwise, the option will not be included in messages sent to the client. :: "Dhcp4": { "option-data": [ { "name": "vendor-encapsulated-options" } ], ... } Alternatively, the option can be specified using its code. :: "Dhcp4": { "option-data": [ { "code": 43 } ], ... } Another popular option that is often somewhat imprecisely called the "vendor option" is option 125. Its proper name is the "vendor-independent vendor-specific information option" or "vivso". The idea behind vivso options is that each vendor has its own unique set of options with their own custom formats. The vendor is identified by a 32-bit unsigned integer called `enterprise-id` or `vendor-id`. For example, vivso with vendor-id 4491 represents DOCSIS options, and they are often seen when dealing with cable modems. In Kea each vendor is represented by its own vendor space. Since there are hundreds of vendors and sometimes they use different option definitions for different hardware, it is impossible for Kea to support them all natively. Fortunately, it's easy to define support for new vendor options. Let's take an example of the Genexis home gateway. This device requires sending the vivso 125 option with a sub-option 2 that contains a string with the TFTP server URL. To support such a device, three steps are needed: first, we need to define option definitions that will explain how the option is supposed to be formed. Second, we need to define option values. Third, we need to tell Kea when to send those specific options, which we can do via client classification. An example snippet of a configuration could look similar to the following: :: { // First, we need to define that the suboption 2 in vivso option for // vendor-id 25167 has a specific format (it's a plain string in this example). // After this definition, we can specify values for option tftp. "option-def": [ { // We define a short name, so the option can be referenced by name. // The option has code 2 and resides within vendor space 25167. // Its data is a plain string. "name": "tftp", "code": 2, "space": "vendor-25167", "type": "string" } ], "client-classes": [ { // We now need to tell Kea how to recognize when to use vendor space 25167. // Usually we can use a simple expression, such as checking if the device // sent a vivso option with specific vendor-id, e.g. "vendor[4491].exists". // Unfortunately, Genexis is a bit unusual in this aspect, because it // doesn't send vivso. In this case we need to look into the vendor class // (option code 60) and see if there's a specific string that identifies // the device. "name": "cpe_genexis", "test": "substring(option[60].hex,0,7) == 'HMC1000'", // Once the device is recognized, we want to send two options: // the vivso option with vendor-id set to 25167, and a suboption 2. "option-data": [ { "name": "vivso-suboptions", "data": "25167" }, // The suboption 2 value is defined as any other option. However, // we want to send this suboption 2, even when the client didn't // explicitly request it (often there is no way to do that for // vendor options). Therefore we use always-send to force Kea // to always send this option when 25167 vendor space is involved. { "name": "tftp", "space": "vendor-25167", "data": "tftp://192.0.2.1/genexis/HMC1000.v1.3.0-R.img", "always-send": true } ] } ] } By default Kea sends back only those options that are requested by a client, unless there are protocol rules that tell the DHCP server to always send an option. This approach works nicely in most cases and avoids problems with clients refusing responses with options they don't understand. Unfortunately, this is more complex when we consider vendor options. Some vendors (such as DOCSIS, identified by vendor option 4491) have a mechanism to request specific vendor options and Kea is able to honor those. Unfortunately, for many other vendors, such as Genexis (25167) as discussed above, Kea does not have such a mechanism, so it cannot send any sub-options on its own. To solve this issue, we came up with the concept of persistent options. Kea can be told to always send options, even if the client did not request them. This can be achieved by adding ``"always-send": true`` to the option definition. Note that in this particular case an option is defined in vendor space 25167. With ``always-send`` enabled, the option is sent every time there is a need to deal with vendor space 25167. Another possibility is to redefine the option; see :ref:`dhcp4-private-opts`. Kea comes with several example configuration files. Some of them showcase how to configure options 60 and 43. See ``doc/examples/kea4/vendor-specific.json`` and ``doc/examples/kea6/vivso.json`` in the Kea sources. .. note:: Currently only one vendor is supported for vivco-suboptions (code 124) and vivso-suboptions (code 125) options. Specifying multiple enterprise numbers within a single option instance or multiple options with different enterprise numbers is not supported. .. _dhcp4-option-spaces: Nested DHCPv4 Options (Custom Option Spaces) -------------------------------------------- It is sometimes useful to define a completely new option space, such as when a user creates a new option in the standard option space ("dhcp4") and wants this option to convey sub-options. Since they are in a separate space, sub-option codes have a separate numbering scheme and may overlap with the codes of standard options. Note that the creation of a new option space is not required when defining sub-options for a standard option, because one is created by default if the standard option is meant to convey any sub-options (see :ref:`dhcp4-vendor-opts`). If we want a DHCPv4 option called "container" with code 222, that conveys two sub-options with codes 1 and 2, we first need to define the new sub-options: :: "Dhcp4": { "option-def": [ { "name": "subopt1", "code": 1, "space": "isc", "type": "ipv4-address", "record-types": "", "array": false, "encapsulate": "" }, { "name": "subopt2", "code": 2, "space": "isc", "type": "string", "record-types": "", "array": false, "encapsulate": "" } ], ... } Note that we have defined the options to belong to a new option space (in this case, ``"isc"``). The next step is to define a regular DHCPv4 option with the desired code and specify that it should include options from the new option space: :: "Dhcp4": { "option-def": [ ..., { "name": "container", "code": 222, "space": "dhcp4", "type": "empty", "array": false, "record-types": "", "encapsulate": "isc" } ], ... } The name of the option space in which the sub-options are defined is set in the ``encapsulate`` field. The ``type`` field is set to ``empty``, to indicate that this option does not carry any data other than sub-options. Finally, we can set values for the new options: :: "Dhcp4": { "option-data": [ { "name": "subopt1", "code": 1, "space": "isc", "data": "192.0.2.3" }, } "name": "subopt2", "code": 2, "space": "isc", "data": "Hello world" }, { "name": "container", "code": 222, "space": "dhcp4" } ], ... } It is possible to create an option which carries some data in addition to the sub-options defined in the encapsulated option space. For example, if the ``"container"`` option from the previous example were required to carry a uint16 value as well as the sub-options, the ``type`` value would have to be set to ``"uint16"`` in the option definition. (Such an option would then have the following data structure: DHCP header, uint16 value, sub-options.) The value specified with the ``data`` parameter — which should be a valid integer enclosed in quotes, e.g. ``"123"`` — would then be assigned to the uint16 field in the ``"container"`` option. .. _dhcp4-option-data-defaults: Unspecified Parameters for DHCPv4 Option Configuration ------------------------------------------------------ In many cases it is not required to specify all parameters for an option configuration, and the default values can be used. However, it is important to understand the implications of not specifying some of them, as it may result in configuration errors. The list below explains the behavior of the server when a particular parameter is not explicitly specified: - ``name`` - the server requires either an option name or an option code to identify an option. If this parameter is unspecified, the option code must be specified. - ``code`` - the server requires either an option name or an option code to identify an option. This parameter may be left unspecified if the ``name`` parameter is specified. However, this also requires that the particular option has a definition (either as a standard option or an administrator-created definition for the option using an ``'option-def'`` structure), as the option definition associates an option with a particular name. It is possible to configure an option for which there is no definition (unspecified option format). Configuration of such options requires the use of the option code. - ``space`` - if the option space is unspecified it defaults to ``'dhcp4'``, which is an option space holding standard DHCPv4 options. - ``data`` - if the option data is unspecified it defaults to an empty value. The empty value is mostly used for the options which have no payload (boolean options), but it is legal to specify empty values for some options which carry variable-length data and for which the specification allows a length of 0. For such options, the data parameter may be omitted in the configuration. - ``csv-format`` - if this value is not specified, the server assumes that the option data is specified as a list of comma-separated values to be assigned to individual fields of the DHCP option. .. _dhcp4-stateless-configuration: Stateless Configuration of DHCPv4 Clients ----------------------------------------- The DHCPv4 server supports stateless client configuration, whereby the client has an IP address configured (e.g. using manual configuration) and only contacts the server to obtain other configuration parameters, such as addresses of DNS servers. To obtain the stateless configuration parameters, the client sends the DHCPINFORM message to the server with the ``ciaddr`` set to the address that the client is currently using. The server unicasts the DHCPACK message to the client that includes the stateless configuration (`"yiaddr"` not set). The server responds to the DHCPINFORM when the client is associated with a subnet defined in the server's configuration. An example subnet configuration looks like this: :: "Dhcp4": { "subnet4": [ { "subnet": "192.0.2.0/24" "option-data": [ { "name": "domain-name-servers", "code": 6, "data": "192.0.2.200,192.0.2.201", "csv-format": true, "space": "dhcp4" } ] } ] } This subnet specifies the single option which will be included in the DHCPACK message to the client in response to DHCPINFORM. The subnet definition does not require the address pool configuration if it will be used solely for stateless configuration. This server will associate the subnet with the client if one of the following conditions is met: - The DHCPINFORM is relayed and the ``giaddr`` matches the configured subnet. - The DHCPINFORM is unicast from the client and the ``ciaddr`` matches the configured subnet. - The DHCPINFORM is unicast from the client and the ``ciaddr`` is not set, but the source address of the IP packet matches the configured subnet. - The DHCPINFORM is not relayed and the IP address on the interface on which the message is received matches the configured subnet. .. _dhcp4-client-classifier: Client Classification in DHCPv4 ------------------------------- The DHCPv4 server includes support for client classification. For a deeper discussion of the classification process, see :ref:`classify`. In certain cases it is useful to configure the server to differentiate between DHCP client types and treat them accordingly. Client classification can be used to modify the behavior of almost any part of DHCP message processing. Kea currently offers client classification via private options and option 43 deferred unpacking; subnet selection; pool selection; assignment of different options; and, for cable modems, specific options for use with the TFTP server address and the boot file field. Kea can be instructed to limit access to given subnets based on class information. This is particularly useful for cases where two types of devices share the same link and are expected to be served from two different subnets. The primary use case for such a scenario is cable networks, where there are two classes of devices: the cable modem itself, which should be handed a lease from subnet A; and all other devices behind the modem, which should get a lease from subnet B. That segregation is essential to prevent overly curious end-users from playing with their cable modems. For details on how to set up class restrictions on subnets, see :ref:`classification-subnets`. When subnets belong to a shared network, the classification applies to subnet selection but not to pools; that is, a pool in a subnet limited to a particular class can still be used by clients which do not belong to the class, if the pool they are expected to use is exhausted. So the limit on access based on class information is also available at the pool level within a subnet: see :ref:`classification-pools`. This is useful when segregating clients belonging to the same subnet into different address ranges. In a similar way, a pool can be constrained to serve only known clients, i.e. clients which have a reservation, using the built-in ``KNOWN`` or ``UNKNOWN`` classes. Addresses can be assigned to registered clients without giving a different address per reservation, for instance when there are not enough available addresses. The determination whether there is a reservation for a given client is made after a subnet is selected, so it is not possible to use ``KNOWN``/``UNKNOWN`` classes to select a shared network or a subnet. The process of classification is conducted in five steps. The first step is to assess an incoming packet and assign it to zero or more classes. The second step is to choose a subnet, possibly based on the class information. When the incoming packet is in the special class ``DROP``, it is dropped and a debug message logged. The next step is to evaluate class expressions depending on the built-in ``KNOWN``/``UNKNOWN`` classes after host reservation lookup, using them for pool selection and assigning classes from host reservations. The list of required classes is then built and each class of the list has its expression evaluated; when it returns ``true`` the packet is added as a member of the class. The last step is to assign options, again possibly based on the class information. More complete and detailed information is available in :ref:`classify`. There are two main methods of classification. The first is automatic and relies on examining the values in the vendor class options or the existence of a host reservation. Information from these options is extracted, and a class name is constructed from it and added to the class list for the packet. The second specifies an expression that is evaluated for each packet. If the result is ``true``, the packet is a member of the class. .. note:: Care should be taken with client classification, as it is easy for clients that do not meet class criteria to be denied all service. Setting Fixed Fields in Classification ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It is possible to specify that clients belonging to a particular class should receive packets with specific values in certain fixed fields. In particular, three fixed fields are supported: ``next-server`` (conveys an IPv4 address, which is set in the ``siaddr`` field), ``server-hostname`` (conveys a server hostname, can be up to 64 bytes long, and is sent in the ``sname`` field) and ``boot-file-name`` (conveys the configuration file, can be up to 128 bytes long, and is sent using the ``file`` field). Obviously, there are many ways to assign clients to specific classes, but for PXE clients the client architecture type option (code 93) seems to be particularly suited to make the distinction. The following example checks whether the client identifies itself as a PXE device with architecture EFI x86-64, and sets several fields if it does. See `Section 2.1 of RFC 4578 `__) or the client documentation for specific values. :: "Dhcp4": { "client-classes": [ { "name": "ipxe_efi_x64", "test": "option[93].hex == 0x0009", "next-server": "192.0.2.254", "server-hostname": "hal9000", "boot-file-name": "/dev/null" }, ... ], ... } If an incoming packet is matched to multiple classes, then the value used for each field will come from the first class that specifies the field, in the order the classes are assigned to the packet. .. note:: The classes are ordered as specified in the configuration. Using Vendor Class Information in Classification ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The server checks whether an incoming packet includes the vendor class identifier option (60). If it does, the content of that option is prepended with ``VENDOR_CLASS\_``, and it is interpreted as a class. For example, modern cable modems send this option with value ``docsis3.0``, so the packet belongs to the class ``VENDOR_CLASS_docsis3.0``. .. note:: Certain special actions for clients in `VENDOR_CLASS_docsis3.0` can be achieved by defining `VENDOR_CLASS_docsis3.0` and setting its ``next-server`` and ``boot-file-name`` values appropriately. This example shows a configuration using an automatically generated ``VENDOR_CLASS\_`` class. The administrator of the network has decided that addresses from the range 192.0.2.10 to 192.0.2.20 are going to be managed by the Dhcp4 server and only clients belonging to the DOCSIS 3.0 client class are allowed to use that pool. :: "Dhcp4": { "subnet4": [ { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ], "client-class": "VENDOR_CLASS_docsis3.0" } ], ... } Defining and Using Custom Classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following example shows how to configure a class using an expression and a subnet using that class. This configuration defines the class named ``Client_foo``. It is comprised of all clients whose client IDs (option 61) start with the string ``foo``. Members of this class will be given addresses from 192.0.2.10 to 192.0.2.20 and the addresses of their DNS servers set to 192.0.2.1 and 192.0.2.2. :: "Dhcp4": { "client-classes": [ { "name": "Client_foo", "test": "substring(option[61].hex,0,3) == 'foo'", "option-data": [ { "name": "domain-name-servers", "code": 6, "space": "dhcp4", "csv-format": true, "data": "192.0.2.1, 192.0.2.2" } ] }, ... ], "subnet4": [ { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ], "client-class": "Client_foo" }, ... ], ... } .. _dhcp4-required-class: Required Classification ~~~~~~~~~~~~~~~~~~~~~~~ In some cases it is useful to limit the scope of a class to a shared network, subnet, or pool. There are two parameters which are used to limit the scope of the class by instructing the server to evaluate test expressions when required. The first one is the per-class ``only-if-required`` flag, which is ``false`` by default. When it is set to ``true``, the test expression of the class is not evaluated at the reception of the incoming packet but later, and only if the class evaluation is required. The second is ``require-client-classes``, which takes a list of class names and is valid in shared-network, subnet, and pool scope. Classes in these lists are marked as required and evaluated after selection of this specific shared network/subnet/pool and before output-option processing. In this example, a class is assigned to the incoming packet when the specified subnet is used: :: "Dhcp4": { "client-classes": [ { "name": "Client_foo", "test": "member('ALL')", "only-if-required": true }, ... ], "subnet4": [ { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ], "require-client-classes": [ "Client_foo" ], ... }, ... ], ... } Required evaluation can be used to express complex dependencies like subnet membership. It can also be used to reverse the precedence; if ``option-data`` is set in a subnet, it takes precedence over ``option-data`` in a class. If ``option-data`` is moved to a required class and required in the subnet, a class evaluated earlier may take precedence. Required evaluation is also available at the shared-network and pool levels. The order in which required classes are considered is: shared-network, subnet, and pool, i.e. in the reverse order from the way in which ``option-data`` is processed. .. _dhcp4-ddns-config: DDNS for DHCPv4 --------------- As mentioned earlier, ``kea-dhcp4`` can be configured to generate requests to the DHCP-DDNS server, ``kea-dhcp-ddns``, (referred to herein as "D2") to update DNS entries. These requests are known as Name Change Requests or NCRs. Each NCR contains the following information: 1. Whether it is a request to add (update) or remove DNS entries. 2. Whether the change requests forward DNS updates (A records), reverse DNS updates (PTR records), or both. 3. The Fully Qualified Domain Name (FQDN), lease address, and DHCID (information identifying the client associated with the FQDN). Prior to Kea 1.7.1, all parameters for controlling DDNS were within the global ``dhcp-ddns`` section of ``kea-dhcp4``. Beginning with Kea 1.7.1, DDNS-related parameters were split into two groups: 1. Connectivity Parameters These are parameters which specify where and how ``kea-dhcp4`` connects to and communicates with D2. These parameters can only be specified within the top-level ``dhcp-ddns`` section in the ``kea-dhcp4`` configuration. The connectivity parameters are listed below: - ``enable-updates`` - ``server-ip`` - ``server-port`` - ``sender-ip`` - ``sender-port`` - ``max-queue-size`` - ``ncr-protocol`` - ``ncr-format"`` 2. Behavioral Parameters These parameters influence behavior such as how client host names and FQDN options are handled. They have been moved out of the ``dhcp-ddns`` section so that they may be specified at the global, shared-network, and/or subnet levels. Furthermore, they are inherited downward from global to shared-network to subnet. In other words, if a parameter is not specified at a given level, the value for that level comes from the level above it. The behavioral parameters are as follows: - ``ddns-send-updates`` - ``ddns-override-no-update`` - ``ddns-override-client-update`` - ``ddns-replace-client-name"`` - ``ddns-generated-prefix`` - ``ddns-qualifying-suffix`` - ``ddns-update-on-renew`` - ``ddns-use-conflict-resolution`` - ``hostname-char-set`` - ``hostname-char-replacement`` .. note:: For backward compatibility, configuration parsing still recognizes the original behavioral parameters specified in ``dhcp-ddns``. It does so by translating the parameter into its global equivalent. If a parameter is specified both globally and in ``dhcp-ddns``, the latter value is ignored. In either case, a log is emitted explaining what has occurred. Specifying these values within ``dhcp-ddns`` is deprecated and support for it will be removed. The default configuration and values would appear as follows: :: "Dhcp4": { "dhcp-ddns": { // Connectivity parameters "enable-updates": false, "server-ip": "127.0.0.1", "server-port":53001, "sender-ip":"", "sender-port":0, "max-queue-size":1024, "ncr-protocol":"UDP", "ncr-format":"JSON" }, // Behavioral parameters (global) "ddns-send-updates": true, "ddns-override-no-update": false, "ddns-override-client-update": false, "ddns-replace-client-name": "never", "ddns-generated-prefix": "myhost", "ddns-qualifying-suffix": "", "ddns-update-on-renew": false, "ddns-use-conflict-resolution": true, "hostname-char-set": "", "hostname-char-replacement": "" ... } As of Kea 1.7.1, there are two parameters which determine if ``kea-dhcp4`` can generate DDNS requests to D2: the existing ``dhcp-ddns:enable-updates`` parameter, which now only controls whether ``kea-dhcp4`` connects to D2; and the new behavioral parameter, ``ddns-send-updates``, which determines whether DDNS updates are enabled at a given level (i.e. global, shared-network, or subnet). The following table shows how the two parameters function together: .. table:: Enabling and Disabling DDNS Updates +-----------------+--------------------+-------------------------------+ | dhcp-ddns: | Global | Outcome | | enable-updates | ddns-send-updates | | +=================+====================+===============================+ | false (default) | false | no updates at any scope | +-----------------+--------------------+-------------------------------+ | false | true (default) | no updates at any scope | +-----------------+--------------------+-------------------------------+ | true | false | updates only at scopes with | | | | a local value of true for | | | | ddns-enable-updates | +-----------------+--------------------+-------------------------------+ | true | true | updates at all scopes except | | | | those with a local value of | | | | false for ddns-enable-updates | +-----------------+--------------------+-------------------------------+ Kea 1.9.1 added two new parameters; the first is ``ddns-update-on-renew``. Normally, when leases are renewed the server only updates DNS if the DNS information for the lease (e.g. FQDN, DNS update direction flags) has changed. Setting ``ddns-update-on-renew`` to `true` instructs the server to always update the DNS information when a lease is renewed, even if its DNS information has not changed. This allows Kea to "self-heal" if it was previously unable to add DNS entries or they were somehow lost by the DNS server. .. note:: Setting ``ddns-update-on-renew`` to `true` may impact performance, especially for servers with numerous clients that renew often. The second parameter added in Kea 1.9.1 is ``ddns-use-conflict-resolution``. The value of this parameter is passed by ``kea-dhcp4`` to D2 with each DNS update request. When `true`, (the default value), D2 employs conflict resolution, as described in `RFC 4703 `__, when attempting to fulfill the update request. When false, D2 simply attempts to update the DNS entries per the request, regardless of whether they conflict with existing entries owned by other DHCP4 clients. .. note:: Setting ``ddns-use-conflict-resolution`` to `false` disables the overwrite safeguards that the rules of conflict resolution ( `RFC 4703 `__) are intended to prevent. This means that existing entries for a FQDN or an IP address made for Client-A can be deleted or replaced by entries for Client-B. Furthermore, there are two scenarios by which entries for multiple clients for the same key (e.g. FQDN or IP) can be created. 1. Client-B uses the same FQDN as Client-A but a different IP address. In this case, the forward DNS entries (A and DHCID RRs) for Client-A will be deleted as they match the FQDN and new entries for Client-B will be added. The reverse DNS entries (PTR and DHCID RRs) for Client-A, however, will not be deleted as they belong to a different IP address, while new entries for Client-B will still be added. 2. Client-B uses the same IP address as Client-A but a different FQDN. In this case the reverse DNS entries (PTR and DHCID RRs) for Client-A will be deleted as they match the IP address and new entries for Client-B will be added. The forward DNS entries (A and DHCID RRs) for Client-A, however, will not be deleted, as they belong to a different FQDN while new entries for Client-B will still be added. Disabling conflict resolution should be done only after careful review of specific use cases. The best way to avoid unwanted DNS entries is to always ensure lease changes are processed through Kea, whether they are released, expire, or are deleted via the ``lease-del4`` command, prior to reassigning either FQDNs or IP addresses. Doing so causes ``kea-dhcp4`` to generate DNS removal requests to D2. .. note:: The DNS entries Kea creates contain a value for TTL (time to live). As of Kea 1.9.3, kea-dhcp4 calculates that value based on `RFC 4702, Section 5 `__, which suggests that the TTL value be 1/3 of the lease's lifetime, with a minimum value of 10 minutes. In earlier versions, the server set the TTL value equal to the lease's valid lifetime. .. _dhcpv4-d2-io-config: DHCP-DDNS Server Connectivity ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For NCRs to reach the D2 server, ``kea-dhcp4`` must be able to communicate with it. ``kea-dhcp4`` uses the following configuration parameters to control this communication: - ``enable-updates`` - As of Kea 1.7.1, this parameter only enables connectivity to ``kea-dhcp-ddns`` such that DDNS updates can be constructed and sent. It must be `true` for NCRs to be generated and sent to D2. It defaults to `false`. - ``server-ip`` - This is the IP address on which D2 listens for requests. The default is the local loopback interface at address 127.0.0.1. Either an IPv4 or IPv6 address may be specified. - ``server-port`` - This is the port on which D2 listens for requests. The default value is 53001. - ``sender-ip`` - This is the IP address which ``kea-dhcp4`` uses to send requests to D2. The default value is blank, which instructs ``kea-dhcp4`` to select a suitable address. - ``sender-port`` - This is the port which ``kea-dhcp4`` uses to send requests to D2. The default value of 0 instructs ``kea-dhcp4`` to select a suitable port. - ``max-queue-size`` - This is the maximum number of requests allowed to queue waiting to be sent to D2. This value guards against requests accumulating uncontrollably if they are being generated faster than they can be delivered. If the number of requests queued for transmission reaches this value, DDNS updating will be turned off until the queue backlog has been sufficiently reduced. The intent is to allow the ``kea-dhcp4`` server to continue lease operations without running the risk that its memory usage grows without limit. The default value is 1024. - ``ncr-protocol`` - This specifies the socket protocol to use when sending requests to D2. Currently only UDP is supported. - ``ncr-format`` - This specifies the packet format to use when sending requests to D2. Currently only JSON format is supported. By default, ``kea-dhcp-ddns`` is assumed to be running on the same machine as ``kea-dhcp4``, and all of the default values mentioned above should be sufficient. If, however, D2 has been configured to listen on a different address or port, these values must be altered accordingly. For example, if D2 has been configured to listen on 192.168.1.10 port 900, the following configuration is required: :: "Dhcp4": { "dhcp-ddns": { "server-ip": "192.168.1.10", "server-port": 900, ... }, ... } .. _dhcpv4-d2-rules-config: When Does the ``kea-dhcp4`` Server Generate a DDNS Request? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``kea-dhcp4`` follows the behavior prescribed for DHCP servers in `RFC 4702 `__. It is important to keep in mind that ``kea-dhcp4`` makes the initial decision of when and what to update and forwards that information to D2 in the form of NCRs. Carrying out the actual DNS updates and dealing with such things as conflict resolution are within the purview of D2 itself (see :ref:`dhcp-ddns-server`). This section describes when ``kea-dhcp4`` generates NCRs and the configuration parameters that can be used to influence this decision. It assumes that both the connectivity parameter ``enable-updates`` and the behavioral parameter ``ddns-send-updates``, are `true`. In general, ``kea-dhcp4`` generates DDNS update requests when: 1. A new lease is granted in response to a DHCPREQUEST; 2. An existing lease is renewed but the FQDN associated with it has changed; or 3. An existing lease is released in response to a DHCPRELEASE. In the second case, lease renewal, two DDNS requests are issued: one request to remove entries for the previous FQDN, and a second request to add entries for the new FQDN. In the third case, a lease release - a single DDNS request - to remove its entries will be made. As for the first case, the decisions involved when granting a new lease are more complex. When a new lease is granted, ``kea-dhcp4`` generates a DDNS update request if the DHCPREQUEST contains either the FQDN option (code 81) or the Host Name option (code 12). If both are present, the server uses the FQDN option. By default, ``kea-dhcp4`` respects the FQDN N and S flags specified by the client as shown in the following table: .. table:: Default FQDN Flag Behavior +------------+---------------------+-----------------+-------------+ | Client | Client Intent | Server Response | Server | | Flags:N-S | | | Flags:N-S-O | +============+=====================+=================+=============+ | 0-0 | Client wants to | Server | 1-0-0 | | | do forward | generates | | | | updates, server | reverse-only | | | | should do | request | | | | reverse updates | | | +------------+---------------------+-----------------+-------------+ | 0-1 | Server should | Server | 0-1-0 | | | do both forward | generates | | | | and reverse | request to | | | | updates | update both | | | | | directions | | +------------+---------------------+-----------------+-------------+ | 1-0 | Client wants no | Server does not | 1-0-0 | | | updates done | generate a | | | | | request | | +------------+---------------------+-----------------+-------------+ The first row in the table above represents "client delegation." Here the DHCP client states that it intends to do the forward DNS updates and the server should do the reverse updates. By default, ``kea-dhcp4`` honors the client's wishes and generates a DDNS request to the D2 server to update only reverse DNS data. The parameter ``ddns-override-client-update`` can be used to instruct the server to override client delegation requests. When this parameter is `"true"`, ``kea-dhcp4`` disregards requests for client delegation and generates a DDNS request to update both forward and reverse DNS data. In this case, the N-S-O flags in the server's response to the client will be 0-1-1 respectively. (Note that the flag combination N=1, S=1 is prohibited according to `RFC 4702 `__. If such a combination is received from the client, the packet will be dropped by ``kea-dhcp4``.) To override client delegation, set the following values in the configuration file: :: "Dhcp4": { ... "ddns-override-client-update": true, ... } The third row in the table above describes the case in which the client requests that no DNS updates be done. The parameter ``ddns-override-no-update`` can be used to instruct the server to disregard the client's wishes. When this parameter is true, ``kea-dhcp4`` generates DDNS update requests to ``kea-dhcp-ddns`` even if the client requests that no updates be done. The N-S-O flags in the server's response to the client will be 0-1-1. To override client delegation, issue the following commands: :: "Dhcp4": { ... "ddns-override-no-update": true, ... } ``kea-dhcp4`` always generates DDNS update requests if the client request only contains the Host Name option. In addition, it includes an FQDN option in the response to the client with the FQDN N-S-O flags set to 0-1-0, respectively. The domain name portion of the FQDN option is the name submitted to D2 in the DDNS update request. .. _dhcpv4-fqdn-name-generation: ``kea-dhcp4`` Name Generation for DDNS Update Requests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Each Name Change Request must of course include the fully qualified domain name whose DNS entries are to be affected. ``kea-dhcp4`` can be configured to supply a portion or all of that name, based on what it receives from the client in the DHCPREQUEST. The default rules for constructing the FQDN that will be used for DNS entries are: 1. If the DHCPREQUEST contains the client FQDN option, take the candidate name from there; otherwise, take it from the Host Name option. 2. If the candidate name is a partial (i.e. unqualified) name, then add a configurable suffix to the name and use the result as the FQDN. 3. If the candidate name provided is empty, generate an FQDN using a configurable prefix and suffix. 4. If the client provides neither option, then take no DNS action. These rules can be amended by setting the ``ddns-replace-client-name`` parameter, which provides the following modes of behavior: - ``never`` - use the name the client sent. If the client sent no name, do not generate one. This is the default mode. - ``always`` - replace the name the client sent. If the client sent no name, generate one for the client. - ``when-present`` - replace the name the client sent. If the client sent no name, do not generate one. - ``when-not-present`` - use the name the client sent. If the client sent no name, generate one for the client. .. note:: In early versions of Kea, this parameter was a boolean and permitted only values of ``true`` and ``false``. Boolean values have been deprecated and are no longer accepted. Administrators currently using booleans must replace them with the desired mode name. A value of ``true`` maps to ``"when-present"``, while ``false`` maps to ``"never"``. For example, to instruct ``kea-dhcp4`` to always generate the FQDN for a client, set the parameter ``ddns-replace-client-name`` to ``always`` as follows: :: "Dhcp4": { ... "ddns-replace-client-name": "always", ... } The prefix used in the generation of an FQDN is specified by the ``generated-prefix`` parameter. The default value is `"myhost"`. To alter its value, simply set it to the desired string: :: "Dhcp4": { ... "ddns-generated-prefix": "another.host", ... } The suffix used when generating an FQDN, or when qualifying a partial name, is specified by the ``ddns-qualifying-suffix`` parameter. It is strongly recommended that the user supply a value for the qualifying prefix when DDNS updates are enabled. For obvious reasons, we cannot supply a meaningful default. :: "Dhcp4": { ... "ddns-qualifying-suffix": "foo.example.org", ... } When generating a name, ``kea-dhcp4`` constructs the name in the format: ``[ddns-generated-prefix]-[address-text].[ddns-qualifying-suffix]`` where ``address-text`` is simply the lease IP address converted to a hyphenated string. For example, if the lease address is 172.16.1.10, the qualifying suffix "example.com", and the default value is used for ``ddns-generated-prefix``, the generated FQDN is: ``myhost-172-16-1-10.example.com.`` .. _dhcp4-host-name-sanitization: Sanitizing Client Host Name and FQDN Names ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Some DHCP clients may provide values in the Host Name option (option code 12) or FQDN option (option code 81) that contain undesirable characters. It is possible to configure ``kea-dhcp4`` to sanitize these values. The most typical use case is ensuring that only characters that are permitted by RFC 1035 be included: A-Z, a-z, 0-9, and "-". This may be accomplished with the following two parameters: - ``hostname-char-set`` - a regular expression describing the invalid character set. This can be any valid, regular expression using POSIX extended expression syntax. Embedded nulls (0x00) are always considered an invalid character to be replaced (or omitted). - ``hostname-char-replacement`` - a string of zero or more characters with which to replace each invalid character in the host name. An empty string causes invalid characters to be OMITTED rather than replaced. .. note:: Starting with Kea 1.7.5, the default values are as follows: - "hostname-char-set": "[^A-Za-z0-9.-]", - "hostname-char-replacement": "" This enables sanitizing and omits any character that is not a letter, digit, hyphen, dot, or null. The following configuration replaces anything other than a letter, digit, hyphen, or dot with the letter 'x': :: "Dhcp4": { ... "hostname-char-set": "[^A-Za-z0-9.-]", "hostname-char-replacement": "x", ... } Thus, a client-supplied value of "myhost-$[123.org" would become "myhost-xx123.org". Sanitizing is performed only on the portion of the name supplied by the client, and it is performed before applying a qualifying suffix (if one is defined and needed). .. note:: The following are some considerations to keep in mind: Name sanitizing is meant to catch the more common cases of invalid characters through a relatively simple character-replacement scheme. It is difficult to devise a scheme that works well in all cases, for both Host Name and FQDN options. Administrators who find they have clients with odd corner cases of character combinations that cannot be readily handled with this mechanism should consider writing a hook that can carry out sufficiently complex logic to address their needs. If clients include domain names in the Host Name option and the administrator wants these preserved, they need to make sure that the dot, ".", is considered a valid character by the ``hostname-char-set`` expression, such as this: `"[^A-Za-z0-9.-]"`. This does not affect dots in FQDN Option values. When scrubbing FQDNs, dots are treated as delimiters and used to separate the option value into individual domain labels that are scrubbed and then re-assembled. If clients are sending values that differ only by characters considered as invalid by the hostname-char-set, be aware that scrubbing them will yield identical values. In such cases, DDNS conflict rules will permit only one of them to register the name. Finally, given the latitude clients have in the values they send, it is virtually impossible to guarantee that a combination of these two parameters will always yield a name that is valid for use in DNS. For example, using an empty value for ``hostname-char-replacement`` could yield an empty domain label within a name, if that label consists only of invalid characters. .. note:: Since the 1.6.0 Kea release, it is possible to specify ``hostname-char-set`` and/or ``hostname-char-replacement`` at the global scope. This allows host names to be sanitized without requiring a ``dhcp-ddns`` entry. When a ``hostname-char`` parameter is defined at both the global scope and in a ``dhcp-ddns`` entry, the second (local) value is used. .. _dhcp4-next-server: Next Server (``siaddr``) ------------------------ In some cases, clients want to obtain configuration from a TFTP server. Although there is a dedicated option for it, some devices may use the ``siaddr`` field in the DHCPv4 packet for that purpose. That specific field can be configured using the ``next-server`` directive. It is possible to define it in the global scope or for a given subnet only. If both are defined, the subnet value takes precedence. The value in the subnet can be set to `0.0.0.0`, which means that ``next-server`` should not be sent. It can also be set to an empty string, which is equivalent to it not being defined at all; that is, it uses the global value. The ``server-hostname`` (which conveys a server hostname, can be up to 64 bytes long, and is in the sname field) and ``boot-file-name`` (which conveys the configuration file, can be up to 128 bytes long, and is sent using the file field) directives are handled the same way as ``next-server``. :: "Dhcp4": { "next-server": "192.0.2.123", "boot-file-name": "/dev/null", ..., "subnet4": [ { "next-server": "192.0.2.234", "server-hostname": "some-name.example.org", "boot-file-name": "bootfile.efi", ... } ] } .. _dhcp4-echo-client-id: Echoing Client-ID (RFC 6842) ---------------------------- The original DHCPv4 specification (`RFC 2131 `__) states that the DHCPv4 server must not send back client-id options when responding to clients. However, in some cases that results in confused clients that do not have a MAC address or client-id; see `RFC 6842 `__ for details. That behavior changed with the publication of `RFC 6842 `__, which updated `RFC 2131 `__. That update states that the server must send the client-id if the client sent it, and that is Kea's default behavior. However, in some cases older devices that do not support `RFC 6842 `__ may refuse to accept responses that include the client-id option. To enable backward compatibility, an optional configuration parameter has been introduced. To configure it, use the following configuration statement: :: "Dhcp4": { "echo-client-id": false, ... } .. _dhcp4-match-client-id: Using Client Identifier and Hardware Address -------------------------------------------- The DHCP server must be able to identify the client from which it receives the message and distinguish it from other clients. There are many reasons why this identification is required; the most important ones are: - When the client contacts the server to allocate a new lease, the server must store the client identification information in the lease database as a search key. - When the client tries to renew or release the existing lease, the server must be able to find the existing lease entry in the database for this client, using the client identification information as a search key. - Some configurations use static reservations for the IP addresses and other configuration information. The server's administrator uses client identification information to create these static assignments. - In dual-stack networks there is often a need to correlate the lease information stored in DHCPv4 and DHCPv6 servers for a particular host. Using common identification information by the DHCPv4 and DHCPv6 clients allows the network administrator to achieve this correlation and better administer the network. DHCPv4 uses two distinct identifiers which are placed by the client in the queries sent to the server and copied by the server to its responses to the client: ``chaddr`` and ``client-identifier``. The former was introduced as a part of the BOOTP specification and it is also used by DHCP to carry the hardware address of the interface used to send the query to the server (MAC address for the Ethernet). The latter is carried in the client-identifier option, introduced in `RFC 2132 `__. `RFC 2131 `__ indicates that the server may use both of these identifiers to identify the client but the client identifier, if present, takes precedence over ``chaddr``. One of the reasons for this is that the client identifier is independent from the hardware used by the client to communicate with the server. For example, if the client obtained the lease using one network card and then the network card is moved to another host, the server will wrongly identify this host as the one which obtained the lease. Moreover, `RFC 4361 `__ gives the recommendation to use a DUID (see `RFC 8415 `__, the DHCPv6 specification) carried as a client identifier when dual-stack networks are in use to provide consistent identification information for the client, regardless of the type of protocol it is using. Kea adheres to these specifications, and the client identifier by default takes precedence over the value carried in the ``chaddr`` field when the server searches, creates, updates, or removes the client's lease. When the server receives a DHCPDISCOVER or DHCPREQUEST message from the client, it tries to find out if the client already has a lease in the database; if it does, the server hands out that lease rather than allocates a new one. Each lease in the lease database is associated with the client identifier and/or ``chaddr``. The server first uses the client identifier (if present) to search for the lease; if one is found, the server treats this lease as belonging to the client, even if the current ``chaddr`` and the ``chaddr`` associated with the lease do not match. This facilitates the scenario when the network card on the client system has been replaced and thus the new MAC address appears in the messages sent by the DHCP client. If the server fails to find the lease using the client identifier, it performs another lookup using the ``chaddr``. If this lookup returns no result, the client is considered to not have a lease and a new lease is created. A common problem reported by network operators is that poor client implementations do not use stable client identifiers, instead generating a new client identifier each time the client connects to the network. Another well-known case is when the client changes its client identifier during the multi-stage boot process (PXE). In such cases, the MAC address of the client's interface remains stable, and using the ``chaddr`` field to identify the client guarantees that the particular system is considered to be the same client, even though its client identifier changes. To address this problem, Kea includes a configuration option which enables client identification using ``chaddr`` only. This instructs the server to ignore the client identifier during lease lookups and allocations for a particular subnet. Consider the following simplified server configuration: :: "Dhcp4": { ... "match-client-id": true, ... "subnet4": [ { "subnet": "192.0.10.0/24", "pools": [ { "pool": "192.0.2.23-192.0.2.87" } ], "match-client-id": false }, { "subnet": "10.0.0.0/8", "pools": [ { "pool": "10.0.0.23-10.0.2.99" } ], } ] } The ``match-client-id`` is a boolean value which controls this behavior. The default value of ``true`` indicates that the server will use the client identifier for lease lookups and ``chaddr`` if the first lookup returns no results. ``false`` means that the server will only use the ``chaddr`` to search for the client's lease. Whether the DHCID for DNS updates is generated from the client identifier or ``chaddr`` is controlled through the same parameter. The ``match-client-id`` parameter may appear both in the global configuration scope and/or under any subnet declaration. In the example shown above, the effective value of the ``match-client-id`` will be ``false`` for the subnet 192.0.10.0/24, because the subnet-specific setting of the parameter overrides the global value of the parameter. The effective value of the ``match-client-id`` for the subnet 10.0.0.0/8 will be set to ``true``, because the subnet declaration lacks this parameter and the global setting is by default used for this subnet. In fact, the global entry for this parameter could be omitted in this case, because ``true`` is the default value. It is important to understand what happens when the client obtains its lease for one setting of the ``match-client-id`` and then renews it when the setting has been changed. First, consider the case when the client obtains the lease and the ``match-client-id`` is set to ``true``. The server stores the lease information, including the client identifier (if supplied) and ``chaddr``, in the lease database. When the setting is changed and the client renews the lease, the server will determine that it should use the ``chaddr`` to search for the existing lease. If the client has not changed its MAC address, the server should successfully find the existing lease. The client identifier associated with the returned lease will be ignored and the client will be allowed to use this lease. When the lease is renewed only the ``chaddr`` will be recorded for this lease, according to the new server setting. In the second case, the client has the lease with only a ``chaddr`` value recorded. When the ``match-client-id`` setting is changed to ``true``, the server will first try to use the client identifier to find the existing client's lease. This will return no results because the client identifier was not recorded for this lease. The server will then use the ``chaddr`` and the lease will be found. If the lease appears to have no client identifier recorded, the server will assume that this lease belongs to the client and that it was created with the previous setting of the ``match-client-id``. However, if the lease contains a client identifier which is different from the client identifier used by the client, the lease will be assumed to belong to another client and a new lease will be allocated. .. _dhcp4-authoritative: Authoritative DHCPv4 Server Behavior ------------------------------------ The original DHCPv4 specification (`RFC 2131 `__) states that if a client requests an address in the INIT-REBOOT state of which the server has no knowledge, the server must remain silent, except if the server knows that the client has requested an IP address from the wrong network. By default, Kea follows the behavior of the ISC ``dhcpd`` daemon instead of the specification and also remains silent if the client requests an IP address from the wrong network, because configuration information about a given network segment is not known to be correct. Kea only rejects a client's DHCPREQUEST with a DHCPNAK message if it already has a lease for the client with a different IP address. Administrators can override this behavior through the boolean ``authoritative`` (``false`` by default) setting. In authoritative mode, ``authoritative`` set to ``true``, Kea always rejects INIT-REBOOT requests from unknown clients with DHCPNAK messages. The ``authoritative`` setting can be specified in global, shared-network, and subnet configuration scope and is automatically inherited from the parent scope, if not specified. All subnets in a shared-network must have the same ``authoritative`` setting. .. _dhcp4-dhcp4o6-config: DHCPv4-over-DHCPv6: DHCPv4 Side ------------------------------- The support of DHCPv4-over-DHCPv6 transport is described in `RFC 7341 `__ and is implemented using cooperating DHCPv4 and DHCPv6 servers. This section is about the configuration of the DHCPv4 side (the DHCPv6 side is described in :ref:`dhcp6-dhcp4o6-config`). .. note:: DHCPv4-over-DHCPv6 support is experimental and the details of the inter-process communication may change; both the DHCPv4 and DHCPv6 sides should be running the same version of Kea. For instance, the support of port relay (RFC 8357) introduced an incompatible change. The ``dhcp4o6-port`` global parameter specifies the first of the two consecutive ports of the UDP sockets used for the communication between the DHCPv6 and DHCPv4 servers. The DHCPv4 server is bound to ::1 on ``port`` + 1 and connected to ::1 on ``port``. With DHCPv4-over-DHCPv6, the DHCPv4 server does not have access to several of the identifiers it would normally use to select a subnet. To address this issue, three new configuration entries are available; the presence of any of these allows the subnet to be used with DHCPv4-over-DHCPv6. These entries are: - ``4o6-subnet``: takes a prefix (i.e., an IPv6 address followed by a slash and a prefix length) which is matched against the source address. - ``4o6-interface-id``: takes a relay interface ID option value. - ``4o6-interface``: takes an interface name which is matched against the incoming interface name. The following configuration was used during some tests: :: { # DHCPv4 conf "Dhcp4": { "interfaces-config": { "interfaces": [ "eno33554984" ] }, "lease-database": { "type": "memfile", "name": "leases4" }, "valid-lifetime": 4000, "subnet4": [ { "subnet": "10.10.10.0/24", "4o6-interface": "eno33554984", "4o6-subnet": "2001:db8:1:1::/64", "pools": [ { "pool": "10.10.10.100 - 10.10.10.199" } ] } ], "dhcp4o6-port": 6767, "loggers": [ { "name": "kea-dhcp4", "output_options": [ { "output": "/tmp/kea-dhcp4.log" } ], "severity": "DEBUG", "debuglevel": 0 } ] } } .. _sanity-checks4: Sanity Checks in DHCPv4 ----------------------- An important aspect of a well-running DHCP system is an assurance that the data remain consistent. However, in some cases it may be convenient to tolerate certain inconsistent data. For example, a network administrator that temporarily removes a subnet from a configuration would not want all the leases associated with it to disappear from the lease database. Kea has a mechanism to control sanity checks such as this. Kea supports a configuration scope called ``sanity-checks``. It currently allows only a single parameter, called ``lease-checks``, which governs the verification carried out when a new lease is loaded from a lease file. This mechanism permits Kea to attempt to correct inconsistent data. Every subnet has a ``subnet-id`` value; this is how Kea internally identifies subnets. Each lease has a ``subnet-id`` parameter as well, which identifies which subnet it belongs to. However, if the configuration has changed, it is possible that a lease could exist with a ``subnet-id``, but without any subnet that matches it. Also, it is possible that the subnet's configuration has changed and the ``subnet-id`` now belongs to a subnet that does not match the lease. Kea's corrective algorithm first checks to see if there is a subnet with the ``subnet-id`` specified by the lease. If there is, it verifies whether the lease belongs to that subnet. If not, depending on the ``lease-checks`` setting, the lease is discarded, a warning is displayed, or a new subnet is selected for the lease that matches it topologically. There are five levels which are supported: - ``none`` - do no special checks; accept the lease as is. - ``warn`` - if problems are detected display a warning, but accept the lease data anyway. This is the default value. - ``fix`` - if a data inconsistency is discovered, try to correct it. If the correction is not successful, insert the incorrect data anyway. - ``fix-del`` - if a data inconsistency is discovered, try to correct it. If the correction is not successful, reject the lease. This setting ensures the data's correctness, but some incorrect data may be lost. Use with care. - ``del`` - if any inconsistency is detected, reject the lease. This is the strictest mode; use with care. This feature is currently implemented for the memfile backend. The sanity check applies to the lease database in memory, not to the lease file, i.e. inconsistent leases will stay in the lease file. An example configuration that sets this parameter looks as follows: :: "Dhcp4": { "sanity-checks": { "lease-checks": "fix-del" }, ... } .. _dhcp4-store-extended-info: Storing Extended Lease Information ---------------------------------- To support such features as DHCP Leasequery (`RFC 4388 `__) additional information must be stored with each lease. Kea does not currently offer a Leasequery hook library, but other hook libraries may already be using ``user-context``. Because the amount of information for each lease has ramifications in terms of performance and system resource consumption, storing this additional information is configurable through the ``store-extended-info`` parameter. It defaults to ``false`` and may be set at the global, shared-network, and subnet levels. :: "Dhcp4": { "store-extended-info": true, ... } When set to ``true``, information relevant to the DHCPREQUEST asking for the lease is added into the lease's user-context as a map element labeled `"ISC"`. Currently, the map contains a single value, the ``relay-agent-info`` option (DHCP Option 82), when the DHCPREQUEST received contains it. Since DHCPREQUESTs sent as renewals will likely not contain this information, the values taken from the last DHCPREQUEST that did contain it will be retained on the lease. The lease's user-context will look something like this: :: { "ISC": { "relay-agent-info": "0x52050104AABBCCDD" } } .. note:: As mentioned above, it is possible that other hook libraries are already using ``user-context``. Enabling ``store-extended-info`` should not interfere with any other ``user-context`` content, as long as it does not also use an element labeled `"ISC"`. In other words, ``user-context`` is intended to be a flexible container serving multiple purposes. As long as no other purpose also writes an `"ISC"` element to user-context there should not be a conflict. .. _dhcp4-multi-threading-settings: Multi-Threading Settings ------------------------ The Kea server can be configured to process packets in parallel using multiple threads. These settings can be found under the ``multi-threading`` structure and are represented by: - ``enable-multi-threading`` - use multiple threads to process packets in parallel (default false). - ``thread-pool-size`` - specify the number of threads to process packets in parallel. It may be set to 0 (auto-detect), or any positive number explicitly sets the thread count. The default is 0. - ``packet-queue-size`` - specify the size of the queue used by the thread pool to process packets. It may be set to 0 (unlimited), or any positive number explicitly sets the queue size. The default is 64. An example configuration that sets these parameters looks as follows: :: "Dhcp4": { "multi-threading": { "enable-multi-threading": true, "thread-pool-size": 4, "packet-queue-size": 16 } ... } Multi-Threading Settings in Different Backends ---------------------------------------------- Both ``kea-dhcp4`` and ``kea-dhcp6`` are tested internally to determine which settings give the best performance. Although this section describes our results, they are just recommendations and are very dependent on the particular hardware that was used for testing. We strongly advise that administrators run their own performance tests. A full report of performance results for the latest stable Kea can be found `here `_. This includes hardware and test scenario descriptions, as well as current results. After enabling multi-threading, the number of threads is set by the ``thread-pool-size`` parameter. Results from our tests show that the best settings for ``kea-dhcp4`` are: - ``thread-pool-size``: 4 when using ``memfile`` for storing leases. - ``thread-pool-size``: 12 or more when using ``mysql`` for storing leases. - ``thread-pool-size``: 8 when using ``postgresql``. Another very important parameter is ``packet-queue-size``; in our tests we used it as a multiplier of ``thread-pool-size``. So the actual setting strongly depends on ``thread-pool-size``. We saw the best results in our tests with the following settings: - ``packet-queue-size``: 7 * ``thread-pool-size`` when using ``memfile`` for storing leases; in our case it was 7 * 4 = 28. This means that at any given time, up to 28 packets could be queued. - ``packet-queue-size``: 66 * ``thread-pool-size`` when using ``mysql`` for storing leases; in our case it was 66 * 12 = 792. This means that up to 792 packets could be queued. - ``packet-queue-size``: 11 * ``thread-pool-size`` when using ``postgresql`` for storing leases; in our case it was 11 * 8 = 88. IPv6-Only Preferred Networks ---------------------------- `RFC8925 `_, recently published by the IETF, specifies a DHCPv4 option to indicate that a host supports an IPv6-only mode and is willing to forgo obtaining an IPv4 address if the network provides IPv6 connectivity. The general idea is that a network administrator can enable this option to signal to compatible dual-stack devices that IPv6 connectivity is available and they can shut down their IPv4 stack. The new option ``v6-only-preferred`` content is a 32-bit unsigned integer and specifies for how long the device should disable its stack. The value is expressed in seconds. The RFC mentions the ``V6ONLY_WAIT`` timer. This is implemented in Kea by setting the value of the ``v6-only-preferred`` option. This follows the usual practice of setting options; the option value can be specified on the pool, subnet, shared network, or global levels, or even via host reservations. There is no special processing involved; it follows the standard Kea option processing regime. The option is not sent back unless the client explicitly requests it. For example, to enable the option for the whole subnet, the following configuration can be used: :: "subnet4": [ { "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ], "subnet": "192.0.2.0/24", "option-data": [ { // This will make the v6-only capable devices to disable their // v4 stack for half an hour and then try again "name": "v6-only-preferred", "data": "1800" } ] } ], Lease Caching ------------- Clients that attempt multiple renewals in a short period can cause the server to update and write to the database frequently, resulting in a performance impact on the server. The cache parameters instruct the DHCP server to avoid updating leases too frequently, thus avoiding this behavior. Instead, the server assigns the same lease (i.e. reuses it) with no modifications except for CLTT (Client Last Transmission Time), which does not require disk operations. The two parameters are the ``cache-threshold`` double and the ``cache-max-age`` integer; they have no default setting, i.e. the lease caching feature must be explicitly enabled. These parameters can be configured at the global, shared network, and subnet levels. The subnet level has precedence over the shared network level, while the global level is used as a last resort. For example: :: "subnet4": [ { "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ], "subnet": "192.0.2.0/24", "cache-threshold": .25, "cache-max-age": 600, "valid-lifetime": 2000, ... } ], When an already assigned lease can fulfill a client query: - any important change, e.g. for DDNS parameter, hostname, or valid lifetime reduction, makes the lease not reusable. - lease age, i.e. the difference between the creation or last modification time and the current time, is computed (elapsed duration). - if ``cache-max-age`` is explicitly configured, it is compared with the lease age; leases that are too old are not reusable. This means that the value 0 for ``cache-max-age`` disables the lease cache feature. - if ``cache-threshold`` is explicitly configured and is between 0.0 and 1.0, it expresses the percentage of the lease valid lifetime which is allowed for the lease age. Values below and including 0.0 and values greater than 1.0 disable the lease cache feature. In our example, a lease with a valid lifetime of 2000 seconds can be reused if it was committed less than 500 seconds ago. With a lifetime of 3000 seconds, the maximum age of 600 seconds applies. In outbound client responses (e.g. DHCPACK messages), the ``dhcp-lease-time`` option is set to the reusable valid lifetime, i.e. the expiration date does not change. Other options based on the valid lifetime e.g. ``dhcp-renewal-time`` and ``dhcp-rebinding-time``, also depend on the reusable lifetime. .. _host-reservation-v4: Host Reservation in DHCPv4 ========================== There are many cases where it is useful to provide a configuration on a per-host basis. The most obvious one is to reserve a specific, static address for exclusive use by a given client (host); the returning client receives the same address from the server every time, and other clients generally do not receive that address. Another situation when host reservations are applicable is when a host has specific requirements, e.g. a printer that needs additional DHCP options. Yet another possible use case is to define unique names for hosts. There may be cases when a new reservation has been made for a client for an address currently in use by another client. We call this situation a "conflict." These conflicts get resolved automatically over time as described in subsequent sections. Once the conflict is resolved, the correct client will receive the reserved configuration when it renews. Host reservations are defined as parameters for each subnet. Each host must have its own unique identifier, such as the hardware/MAC address. There is an optional ``reservations`` array in the ``subnet4`` structure; each element in that array is a structure that holds information about reservations for a single host. In particular, the structure must have a unique host identifier. In the DHCPv4 context, the identifier is usually a hardware or MAC address. In most cases an IP address will be specified. It is also possible to specify a hostname, host-specific options, or fields carried within the DHCPv4 message such as ``siaddr``, ``sname``, or ``file``. .. note:: Kea versions 1.7.10 and newer require that the reserved address must be within the subnet. The following example shows how to reserve addresses for specific hosts in a subnet: :: "subnet4": [ { "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ], "subnet": "192.0.2.0/24", "interface": "eth0", "reservations": [ { "hw-address": "1a:1b:1c:1d:1e:1f", "ip-address": "192.0.2.202" }, { "duid": "0a:0b:0c:0d:0e:0f", "ip-address": "192.0.2.100", "hostname": "alice-laptop" }, { "circuit-id": "'charter950'", "ip-address": "192.0.2.203" }, { "client-id": "01:11:22:33:44:55:66", "ip-address": "192.0.2.204" } ] } ] The first entry reserves the 192.0.2.202 address for the client that uses a MAC address of 1a:1b:1c:1d:1e:1f. The second entry reserves the address 192.0.2.100 and the hostname of `alice-laptop` for the client using a DUID 0a:0b:0c:0d:0e:0f. (If DNS updates are planned, it is strongly recommended that the hostnames be unique.) The third example reserves address 192.0.3.203 for a client whose request would be relayed by a relay agent that inserts a ``circuit-id`` option with the value `"charter950"`. The fourth entry reserves address 192.0.2.204 for a client that uses a client identifier with value 01:11:22:33:44:55:66. The above example is used for illustrational purposes only; in actual deployments it is recommended to use as few types as possible (preferably just one). See :ref:`reservations4-tuning` for a detailed discussion of this point. Making a reservation for a mobile host that may visit multiple subnets requires a separate host definition in each subnet that host is expected to visit. It is not possible to define multiple host definitions with the same hardware address in a single subnet. Multiple host definitions with the same hardware address are valid if each is in a different subnet. Adding host reservations incurs a performance penalty. In principle, when a server that does not support host reservation responds to a query, it needs to check whether there is a lease for a given address being considered for allocation or renewal. The server that does support host reservation has to perform additional checks: not only whether the address is currently used (i.e., if there is a lease for it), but also whether the address could be used by someone else (i.e., if there is a reservation for it). That additional check incurs extra overhead. .. _reservation4-types: Address Reservation Types ------------------------- In a typical scenario there is an IPv4 subnet defined, e.g. 192.0.2.0/24, with a certain part of it dedicated for dynamic allocation by the DHCPv4 server. That dynamic part is referred to as a dynamic pool or simply a pool. In principle, a host reservation can reserve any address that belongs to the subnet. The reservations that specify addresses that belong to configured pools are called "in-pool reservations." In contrast, those that do not belong to dynamic pools are called "out-of-pool reservations." There is no formal difference in the reservation syntax and both reservation types are handled uniformly. Kea supports global host reservations. These are reservations that are specified at the global level within the configuration and that do not belong to any specific subnet. Kea will still match inbound client packets to a subnet as before, but when the subnet's reservation mode is set to ``"global"``, Kea looks for host reservations only among the global reservations defined. Typically, such reservations would be used to reserve hostnames for clients which may move from one subnet to another. .. note:: Global reservations, while useful in certain circumstances, have aspects that must be given due consideration when using them. Please see :ref:`reservation4-conflict` for more details. .. note:: Since Kea 1.9.1, reservation mode has been replaced by three boolean flags, ``"reservations-global"``, ``"reservations-in-subnet"``, and ``"reservations-out-of-pool"``, which allow the configuration of host reservations both globally and in a subnet. In such cases a subnet host reservation has preference over a global reservation when both exist for the same client. .. _reservation4-conflict: Conflicts in DHCPv4 Reservations -------------------------------- As reservations and lease information are stored separately, conflicts may arise. Consider the following series of events: the server has configured the dynamic pool of addresses from the range of 192.0.2.10 to 192.0.2.20. Host A requests an address and gets 192.0.2.10. Now the system administrator decides to reserve address 192.0.2.10 for Host B. In general, reserving an address that is currently assigned to someone else is not recommended, but there are valid use cases where such an operation is warranted. The server now has a conflict to resolve. If Host B boots up and requests an address, the server is not able to assign the reserved address 192.0.2.10. A naive approach would to be immediately remove the existing lease for Host A and create a new one for Host B. That would not solve the problem, though, because as soon as Host B gets the address, it will detect that the address is already in use (by Host A) and will send a DHCPDECLINE message. Therefore, in this situation, the server has to temporarily assign a different address from the dynamic pool (not matching what has been reserved) to Host B. When Host A renews its address, the server will discover that the address being renewed is now reserved for another host - Host B. The server will inform Host A that it is no longer allowed to use it by sending a DHCPNAK message. The server will not remove the lease, though, as there's a small chance that the DHCPNAK will not be delivered if the network is lossy. If that happens, the client will not receive any responses, so it will retransmit its DHCPREQUEST packet. Once the DHCPNAK is received by Host A, it will revert to server discovery and will eventually get a different address. Besides allocating a new lease, the server will also remove the old one. As a result, address 192.0.2.10 will become free. When Host B tries to renew its temporarily assigned address, the server will detect that it has a valid lease, but will note that there is a reservation for a different address. The server will send DHCPNAK to inform Host B that its address is no longer usable, but will keep its lease (again, the DHCPNAK may be lost, so the server will keep it until the client returns for a new address). Host B will revert to the server discovery phase and will eventually send a DHCPREQUEST message. This time the server will find that there is a reservation for that host and that the reserved address 192.0.2.10 is not used, so it will be granted. It will also remove the lease for the temporarily assigned address that Host B previously obtained. This recovery will succeed, even if other hosts attempt to get the reserved address. If Host C requests the address 192.0.2.10 after the reservation is made, the server will either offer a different address (when responding to DHCPDISCOVER) or send DHCPNAK (when responding to DHCPREQUEST). This mechanism allows the server to fully recover from a case where reservations conflict with existing leases; however, this procedure takes roughly as long as the value set for ``renew-timer``. The best way to avoid such a recovery is not to define new reservations that conflict with existing leases. Another recommendation is to use out-of-pool reservations. If the reserved address does not belong to a pool, there is no way that other clients can get it. .. note:: The conflict-resolution mechanism does not work for global reservations. Although the global address reservations feature may be useful in certain settings, it is generally recommended not to use global reservations for addresses. Administrators who do choose to use global reservations must manually ensure that the reserved addresses are not in dynamic pools. .. _reservation4-hostname: Reserving a Hostname -------------------- When the reservation for a client includes the ``hostname``, the server returns this hostname to the client in the Client FQDN or Hostname option. The server responds with the Client FQDN option only if the client has included the Client FQDN option in its message to the server. The server responds with the Hostname option if the client included the Hostname option in its message to the server, or if the client requested the Hostname option using the Parameter Request List option. The server returns the Hostname option even if it is not configured to perform DNS updates. The reserved hostname always takes precedence over the hostname supplied by the client or the autogenerated (from the IPv4 address) hostname. The server qualifies the reserved hostname with the value of the ``ddns-qualifying-suffix`` parameter. For example, the following subnet configuration: :: { "subnet4": [ { "subnet": "10.0.0.0/24", "pools": [ { "pool": "10.0.0.10-10.0.0.100" } ], "ddns-qualifying-suffix": "example.isc.org.", "reservations": [ { "hw-address": "aa:bb:cc:dd:ee:ff", "hostname": "alice-laptop" } ] }], "dhcp-ddns": { "enable-updates": true, } } will result in assigning the `"alice-laptop.example.isc.org."` hostname to the client using the MAC address "aa:bb:cc:dd:ee:ff". If the ``ddns-qualifying-suffix`` is not specified, the default (empty) value will be used, and in this case the value specified as a ``hostname`` will be treated as a fully qualified name. Thus, by leaving the ``ddns-qualifying-suffix`` empty it is possible to qualify hostnames for different clients with different domain names: :: { "subnet4": [ { "subnet": "10.0.0.0/24", "pools": [ { "pool": "10.0.0.10-10.0.0.100" } ], "reservations": [ { "hw-address": "aa:bb:cc:dd:ee:ff", "hostname": "alice-laptop.isc.org." }, { "hw-address": "12:34:56:78:99:AA", "hostname": "mark-desktop.example.org." } ] }], "dhcp-ddns": { "enable-updates": true, } } .. _reservation4-options: Including Specific DHCPv4 Options in Reservations ------------------------------------------------- Kea offers the ability to specify options on a per-host basis. These options follow the same rules as any other options. These can be standard options (see :ref:`dhcp4-std-options`), custom options (see :ref:`dhcp4-custom-options`), or vendor-specific options (see :ref:`dhcp4-vendor-opts`). The following example demonstrates how standard options can be defined: :: { "subnet4": [ { "reservations": [ { "hw-address": "aa:bb:cc:dd:ee:ff", "ip-address": "192.0.2.1", "option-data": [ { "name": "cookie-servers", "data": "10.1.1.202,10.1.1.203" }, { "name": "log-servers", "data": "10.1.1.200,10.1.1.201" } ] } ] } ] } Vendor-specific options can be reserved in a similar manner: :: { "subnet4": [ { "reservations": [ { "hw-address": "aa:bb:cc:dd:ee:ff", "ip-address": "10.0.0.7", "option-data": [ { "name": "vivso-suboptions", "data": "4491" }, { "name": "tftp-servers", "space": "vendor-4491", "data": "10.1.1.202,10.1.1.203" } ] } ] } ] } Options defined at the host level have the highest priority. In other words, if there are options defined with the same type on the global, subnet, class, and host levels, the host-specific values are used. .. _reservation4-message-fields: Reserving Next Server, Server Hostname, and Boot File Name ---------------------------------------------------------- BOOTP/DHCPv4 messages include `"siaddr"`, `"sname"`, and `"file"` fields. Even though DHCPv4 includes corresponding options, such as option 66 and option 67, some clients may not support these options. For this reason, server administrators often use the `"siaddr"`, `"sname"`, and `"file"` fields instead. With Kea, it is possible to make static reservations for these DHCPv4 message fields: :: { "subnet4": [ { "reservations": [ { "hw-address": "aa:bb:cc:dd:ee:ff", "next-server": "10.1.1.2", "server-hostname": "server-hostname.example.org", "boot-file-name": "/tmp/bootfile.efi" } ] } ] } Note that those parameters can be specified in combination with other parameters for a reservation, such as a reserved IPv4 address. These parameters are optional; a subset of them can be specified, or all of them can be omitted. .. _reservation4-client-classes: Reserving Client Classes in DHCPv4 ---------------------------------- :ref:`classification-using-expressions` explains how to configure the server to assign classes to a client, based on the content of the options that this client sends to the server. Host reservation mechanisms also allow for the static assignment of classes to clients. The definitions of these classes are placed in the Kea configuration or a database. The following configuration snippet shows how to specify that a client belongs to the classes ``reserved-class1`` and ``reserved-class2``. Those classes are associated with specific options sent to the clients which belong to them. :: { "client-classes": [ { "name": "reserved-class1", "option-data": [ { "name": "routers", "data": "10.0.0.200" } ] }, { "name": "reserved-class2", "option-data": [ { "name": "domain-name-servers", "data": "10.0.0.201" } ] } ], "subnet4": [ { "subnet": "10.0.0.0/24", "pools": [ { "pool": "10.0.0.10-10.0.0.100" } ], "reservations": [ { "hw-address": "aa:bb:cc:dd:ee:ff", "client-classes": [ "reserved-class1", "reserved-class2" ] } ] } ] } In some cases the host reservations can be used in conjunction with client classes specified within the Kea configuration. In particular, when a host reservation exists for a client within a given subnet, the `"KNOWN"` built-in class is assigned to the client. Conversely, when there is no static assignment for the client, the `"UNKNOWN"` class is assigned to the client. Class expressions within the Kea configuration file can refer to `"KNOWN"` or `"UNKNOWN"` classes using the `"member"` operator. For example: :: { "client-classes": [ { "name": "dependent-class", "test": "member('KNOWN')", "only-if-required": true } ] } Note that the ``only-if-required`` parameter is needed here to force evaluation of the class after the lease has been allocated and thus the reserved class has been also assigned. .. note:: The classes specified in non-global host reservations are assigned to the processed packet after all classes with the ``only-if-required`` parameter set to ``false`` have been evaluated. This means that these classes must not depend on the statically assigned classes from the host reservations. If there is a need to create such a dependency, the ``only-if-required`` parameter must be set to ``true`` for the dependent classes. Such classes are evaluated after the static classes have been assigned to the packet. This, however, imposes additional configuration overhead, because all classes marked as ``only-if-required`` must be listed in the ``require-client-classes`` list for every subnet where they are used. .. note:: Client classes specified within the Kea configuration file may depend on the classes specified within the global host reservations. In such a case the ``only-if-required`` parameter is not needed. Refer to :ref:`pool-selection-with-class-reservations4` and :ref:`subnet-selection-with-class-reservations4` for the specific use cases. .. _reservations4-mysql-pgsql-cql: Storing Host Reservations in MySQL, PostgreSQL, or Cassandra ------------------------------------------------------------ Kea can store host reservations in MySQL, PostgreSQL, or Cassandra. See :ref:`hosts4-storage` for information on how to configure Kea to use reservations stored in MySQL, PostgreSQL, or Cassandra. Kea provides a dedicated hook for managing reservations in a database; section :ref:`host-cmds` provides detailed information. The `Kea wiki `__ provides some examples of how to conduct common host reservation operations. .. note:: In Kea, the maximum length of an option specified per-host is arbitrarily set to 4096 bytes. .. _reservations4-tuning: Fine-Tuning DHCPv4 Host Reservation ----------------------------------- The host reservation capability introduces additional restrictions for the allocation engine (the component of Kea that selects an address for a client) during lease selection and renewal. In particular, three major checks are necessary. First, when selecting a new lease, it is not sufficient for a candidate lease to simply not be in use by another DHCP client; it also must not be reserved for another client. Second, when renewing a lease, an additional check must be performed to see whether the address being renewed is reserved for another client. Finally, when a host renews an address, the server must check whether there is a reservation for this host, which would mean the existing (dynamically allocated) address should be revoked and the reserved one be used instead. Some of those checks may be unnecessary in certain deployments, and not performing them may improve performance. The Kea server provides the ``reservation-mode`` configuration parameter to select the types of reservations allowed for a particular subnet. Each reservation type has different constraints for the checks to be performed by the server when allocating or renewing a lease for the client. Allowed values are: - ``all`` - enables both in-pool and out-of-pool host reservation types. This setting is the default value, and is the safest and most flexible. However, as all checks are conducted, it is also the slowest. It does not check against global reservations. - ``out-of-pool`` - allows only out-of-pool host reservations. With this setting in place, the server may assume that all host reservations are for addresses that do not belong to the dynamic pool. Therefore, it can skip the reservation checks when dealing with in-pool addresses, thus improving performance. Do not use this mode if any reservations use in-pool addresses. Caution is advised when using this setting; Kea does not sanity-check the reservations against ``reservation-mode`` and misconfiguration may cause problems. - ``global`` - allows only global host reservations. With this setting in place, the server searches for reservations for a client only among the defined global reservations. If an address is specified, the server skips the reservation checks carried out when dealing in other modes, thus improving performance. Caution is advised when using this setting; Kea does not sanity-check reservations when ``global`` is set, and misconfiguration may cause problems. - ``disabled`` - host reservation support is disabled. As there are no reservations, the server skips all checks. Any reservations defined are completely ignored. As checks are skipped, the server may operate faster in this mode. Since Kea 1.9.1, the ``reservation-mode`` parameter is replaced by the ``reservations-global``, ``reservations-in-subnet``, and ``reservations-out-of-pool`` flags. The flags can be activated independently and can produce various combinations, some of which were not supported by the deprecated ``reservation-mode``. The ``reservation-mode`` parameter can be specified at: - global level: ``.Dhcp4["reservation-mode"]`` (lowest priority: gets overridden by all others) - subnet level: ``.Dhcp4.subnet4[]["reservation-mode"]`` (low priority) - shared-network level: ``.Dhcp4["shared-networks"][]["reservation-mode"]`` (high priority) - shared-network subnet-level: ``.Dhcp4["shared-networks"][].subnet4[]["reservation-mode"]`` (highest priority: overrides all others) To decide which ``"reservation-mode"`` to choose, the following decision diagram may be useful: :: O | v +-----------------------------+------------------------------+ | Is per-host configuration needed, such as | | reserving specific addresses, | | assigning specific options or | | assigning packets to specific classes on per-device basis? | +-+-----------------+----------------------------------------+ | | no| yes| | | +--------------------------------------+ | | | For all given hosts, | +--> "disabled" +-->+ can the reserved resources | | be used in all configured subnets? | +--------+---------------------------+-+ | | +----------------------------+ |no |yes | Is | | | | at least one reservation +<--+ "global" <--+ | used to reserve addresses? | +-+------------------------+-+ | | no| yes| +---------------------------+ | | | Is high leases-per-second | +--> "out-of-pool" +-->+ performance or efficient | ^ | resource usage | | | (CPU ticks, RAM usage, | | | database roundtrips) | | | important to your setup? | | +-+----------------+--------+ | | | | yes| no| | | | | +-------------+ | | | | | | +----------------------+ | | | | Can it be guaranteed | | | +-->+ that the reserved | | | | addresses | | | | aren't part of the | | | | pools configured | | | | in the respective | | | | subnet? | | | +-+------------------+-+ | | | | | | yes| no| | | | | V +----------------+ +--> "all" An example configuration that disables reservations looks as follows: .. code-block:: json { "Dhcp4": { "subnet4": [ { "pools": [ { "pool": "192.0.2.10-192.0.2.100" } ], "reservation-mode": "disabled", "subnet": "192.0.2.0/24" } ] } } An example configuration using global reservations is shown below: .. code-block:: json { "Dhcp4": { "reservation-mode": "global", "reservations": [ { "hostname": "host-one", "hw-address": "01:bb:cc:dd:ee:ff" }, { "hostname": "host-two", "hw-address": "02:bb:cc:dd:ee:ff" } ], "subnet4": [ { "pools": [ { "pool": "192.0.2.10-192.0.2.100" } ], "subnet": "192.0.2.0/24" } ] } } The meaning of the reservation flags are: - ``reservations-global``: fetch global reservations. - ``reservations-in-subnet``: fetch subnet reservations. For a shared network this includes all subnet members of the shared network. - ``reservations-out-of-pool``: this makes sense only when the ``reservations-in-subnet`` flag is ``true``. When ``reservations-out-of-pool`` is ``true``, the server may assume that all host reservations are for addresses that do not belong to the dynamic pool. Therefore, it can skip the reservation checks when dealing with in-pool addresses, thus improving performance. The server will not assign reserved addresses that are inside the dynamic pools to the respective clients. This also means that the addresses matching the respective reservations from inside the dynamic pools (if any) can be dynamically assigned to any client. The ``disabled`` value from the deprecated ``reservation-mode`` corresponds to: .. code-block:: json { "Dhcp4": { "reservations-global": false, "reservations-in-subnet": false } } The ``global`` value from the deprecated ``reservation-mode`` corresponds to: .. code-block:: json { "Dhcp4": { "reservations-global": true, "reservations-in-subnet": false } } The ``out-of-pool`` value from the deprecated ``reservation-mode`` corresponds to: .. code-block:: json { "Dhcp4": { "reservations-global": false, "reservations-in-subnet": true, "reservations-out-of-pool": true } } And the ``all`` value from the deprecated ``reservation-mode`` corresponds to: .. code-block:: json { "Dhcp4": { "reservations-global": false, "reservations-in-subnet": true, "reservations-out-of-pool": false } } To activate both ``global`` and ``all``, the following combination can be used: .. code-block:: json { "Dhcp4": { "reservations-global": true, "reservations-in-subnet": true, "reservations-out-of-pool": false } } To activate both ``global`` and ``out-of-pool``, the following combination can be used: .. code-block:: json { "Dhcp4": { "reservations-global": true, "reservations-in-subnet": true, "reservations-out-of-pool": true } } Note that enabling ``out-of-pool`` and disabling ``in-subnet`` at the same time is not recommended because ``out-of-pool`` applies to host reservations in a subnet, which are fetched only when the ``in-subnet`` flag is true. The parameter can be specified at the global, subnet, and shared-network levels. An example configuration that disables reservations looks as follows: .. code-block:: json { "Dhcp4": { "subnet4": [ { "reservations-global": false, "reservations-in-subnet": false, "subnet": "192.0.2.0/24" } ] } } An example configuration using global reservations is shown below: .. code-block:: json { "Dhcp4": { "reservations": [ { "hostname": "host-one", "hw-address": "01:bb:cc:dd:ee:ff" }, { "hostname": "host-two", "hw-address": "02:bb:cc:dd:ee:ff" } ], "reservations-global": true, "reservations-in-subnet": false, "subnet4": [ { "pools": [ { "pool": "192.0.2.10-192.0.2.100" } ], "subnet": "192.0.2.0/24" } ] } } For more details regarding global reservations, see :ref:`global-reservations4`. Another aspect of host reservations is the different types of identifiers. Kea currently supports four types of identifiers: ``hw-address``, ``duid``, ``client-id``, and ``circuit-id``. This is beneficial from a usability perspective; however, there is one drawback. For each incoming packet, Kea has to extract each identifier type and then query the database to see if there is a reservation by this particular identifier. If nothing is found, the next identifier is extracted and the next query is issued. This process continues until either a reservation is found or all identifier types have been checked. Over time, with an increasing number of supported identifier types, Kea would become slower and slower. To address this problem, a parameter called ``host-reservation-identifiers`` is available. It takes a list of identifier types as a parameter. Kea checks only those identifier types enumerated in ``host-reservation-identifiers``. From a performance perspective, the number of identifier types should be kept to a minimum, ideally one. If the deployment uses several reservation types, please enumerate them from most- to least-frequently used, as this increases the chances of Kea finding the reservation using the fewest queries. An example of a ``host-reservation-identifiers`` configuration looks as follows: :: "host-reservation-identifiers": [ "circuit-id", "hw-address", "duid", "client-id" ], "subnet4": [ { "subnet": "192.0.2.0/24", ... } ] If not specified, the default value is: :: "host-reservation-identifiers": [ "hw-address", "duid", "circuit-id", "client-id" ] .. _global-reservations4: Global Reservations in DHCPv4 ----------------------------- In some deployments, such as mobile, clients can roam within the network and certain parameters must be specified regardless of the client's current location. To meet such a need, Kea offers a global reservation mechanism. The idea behind it is that regular host reservations are tied to specific subnets, by using a specific subnet ID. Kea can specify a global reservation that can be used in every subnet that has global reservations enabled. This feature can be used to assign certain parameters, such as hostname or other dedicated, host-specific options. It can also be used to assign addresses. However, global reservations that assign addresses bypass the whole topology determination provided by the DHCP logic implemented in Kea. It is very easy to misuse this feature and get a configuration that is inconsistent. To give a specific example, imagine a global reservation for address 192.0.2.100 and two subnets 192.0.2.0/24 and 192.0.5.0/24. If global reservations are used in both subnets and a device matching global host reservations visits part of the network that is serviced by 192.0.5.0/24, it will get an IP address 192.0.2.100, a subnet 192.0.5.0, and a default router 192.0.5.1. Obviously, such a configuration is unusable, as the client will not be able to reach its default gateway. To use global host reservations, a configuration similar to the following can be used: :: "Dhcp4:" { # This specifies global reservations. # They will apply to all subnets that # have global reservations enabled. "reservations": [ { "hw-address": "aa:bb:cc:dd:ee:ff", "hostname": "hw-host-dynamic" }, { "hw-address": "01:02:03:04:05:06", "hostname": "hw-host-fixed", # Use of IP addresses in global reservations is risky. # If used outside of a matching subnet, such as 192.0.1.0/24, # it will result in a broken configuration being handed # to the client. "ip-address": "192.0.1.77" }, { "duid": "01:02:03:04:05", "hostname": "duid-host" }, { "circuit-id": "'charter950'", "hostname": "circuit-id-host" }, { "client-id": "01:11:22:33:44:55:66", "hostname": "client-id-host" } ], "valid-lifetime": 600, "subnet4": [ { "subnet": "10.0.0.0/24", # It is replaced by the "reservations-global" # "reservations-in-subnet" and "reservations-out-of-pool" # parameters. # "reservation-mode": "global", # Specify if the server should lookup global reservations. "reservations-global": true, # Specify if the server should lookup in-subnet reservations. "reservations-in-subnet": false, # Specify if the server can assume that all reserved addresses # are out-of-pool. It can be ignored because "reservations-in-subnet" # is false. # "reservations-out-of-pool": false, "pools": [ { "pool": "10.0.0.10-10.0.0.100" } ] } ] } When using database backends, the global host reservations are distinguished from regular reservations by using a ``subnet-id`` value of zero. .. _pool-selection-with-class-reservations4: Pool Selection with Client Class Reservations --------------------------------------------- Client classes can be specified in the Kea configuration file and/or via host reservations. The classes specified in the Kea configuration file are evaluated immediately after receiving the DHCP packet and therefore can be used to influence subnet selection using the ``client-class`` parameter specified in the subnet scope. The classes specified within the host reservations are fetched and assigned to the packet after the server has already selected a subnet for the client. This means that the client class specified within a host reservation cannot be used to influence subnet assignment for this client, unless the subnet belongs to a shared network. If the subnet belongs to a shared network, the server may dynamically change the subnet assignment while trying to allocate a lease. If the subnet does not belong to a shared network, the subnet is not changed once selected. If the subnet does not belong to a shared network, it is possible to use host reservation-based client classification to select an address pool within the subnet as follows: :: "Dhcp4": { "client-classes": [ { "name": "reserved_class" }, { "name": "unreserved_class", "test": "not member('reserved_class')" } ], "subnet4": [ { "subnet": "192.0.2.0/24", "reservations": [{" "hw-address": "aa:bb:cc:dd:ee:fe", "client-classes": [ "reserved_class" ] }], "pools": [ { "pool": "192.0.2.10-192.0.2.20", "client-class": "reserved_class" }, { "pool": "192.0.2.30-192.0.2.40", "client-class": "unreserved_class" } ] } ] } The ``reserved_class`` is declared without the ``test`` parameter because it may only be assigned to the client via the host reservation mechanism. The second class, ``unreserved_class``, is assigned to the clients which do not belong to the ``reserved_class``. The first pool within the subnet is only used for clients having a reservation for the ``reserved_class``. The second pool is used for clients not having such a reservation. The configuration snippet includes one host reservation which causes the client with the MAC address aa:bb:cc:dd:ee:fe to be assigned to the ``reserved_class``. Thus, this client will be given an IP address from the first address pool. .. _subnet-selection-with-class-reservations4: Subnet Selection with Client Class Reservations ----------------------------------------------- There is one specific use case when subnet selection may be influenced by client classes specified within host reservations: when the client belongs to a shared network. In such a case it is possible to use classification to select a subnet within this shared network. Consider the following example: :: "Dhcp4": { "client-classes": [ { "name": "reserved_class" }, { "name: "unreserved_class", "test": "not member('reserved_class')" } ], "reservations": [{" "hw-address": "aa:bb:cc:dd:ee:fe", "client-classes": [ "reserved_class" ] }], # It is replaced by the "reservations-global" # "reservations-in-subnet" and "reservations-out-of-pool" parameters. # Specify if the server should lookup global reservations. "reservations-global": true, # Specify if the server should lookup in-subnet reservations. "reservations-in-subnet": false, # Specify if the server can assume that all reserved addresses # are out-of-pool. It can be ignored because "reservations-in-subnet" # is false, but if specified, it is inherited by "shared-networks" # and "subnet4" levels. # "reservations-out-of-pool": false, "shared-networks": [{ "subnet4": [ { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.10-192.0.2.20", "client-class": "reserved_class" } ] }, { "subnet": "192.0.3.0/24", "pools": [ { "pool": "192.0.3.10-192.0.3.20", "client-class": "unreserved_class" } ] } ] }] } This is similar to the example described in :ref:`pool-selection-with-class-reservations4`. This time, however, there are two subnets, each of which has a pool associated with a different class. The clients that do not have a reservation for the ``reserved_class`` are assigned an address from the subnet 192.0.3.0/24. Clients with a reservation for the ``reserved_class`` are assigned an address from the subnet 192.0.2.0/24. The subnets must belong to the same shared network. In addition, the reservation for the client class must be specified at the global scope (global reservation) and ``reservations-global`` must be set to ``true``. In the example above, the ``client-class`` could also be specified at the subnet level rather than the pool level, and would yield the same effect. .. _multiple-reservations-same-ip4: Multiple Reservations for the Same IP ------------------------------------- Host reservations were designed to preclude the creation of multiple reservations for the same IP address within a particular subnet, to avoid having two different clients compete for the same address. When using the default settings, the server returns a configuration error when it finds two or more reservations for the same IP address within a subnet in the Kea configuration file. The :ref:`host-cmds` hook library returns an error in response to the ``reservation-add`` command when it detects that the reservation exists in the database for the IP address for which the new reservation is being added. In some deployments a single host can select one of several network interfaces to communicate with the DHCP server, and the server must assign the same IP address to the host regardless of the interface used. Since each interface is assigned a different MAC address, it implies that several host reservations must be created to associate all of the MAC addresses present on this host with IP addresses. Using different IP addresses for each interface is impractical and is considered a waste of the IPv4 address space, especially since the host typically uses only one interface for communication with the server, hence only one IP address is in use. This causes a need to create multiple host reservations for a single IP address within a subnet; this is supported beginning with the Kea 1.9.1 release as an optional mode of operation, enabled with the ``ip-reservations-unique`` global parameter. ``ip-reservations-unique`` is a boolean parameter that defaults to ``true``, which forbids the specification of more than one reservation for the same IP address within a given subnet. Setting this parameter to ``false`` allows such reservations to be created both in the Kea configuration file and in the host database backend, via the ``host-cmds`` hook library. This setting is currently supported by the most popular host database backends, i.e. MySQL and PostgreSQL. It is not supported for Cassandra, Host Cache (see :ref:`hooks-host-cache`), or the RADIUS backend (see :ref:`hooks-radius`). An attempt to set ``ip-reservations-unique`` to ``false`` when any of these three backends is in use yields a configuration error. .. note:: When ``ip-reservations-unique`` is set to ``true`` (the default value), the server ensures that IP reservations are unique for a subnet within a single host backend and/or Kea configuration file. It does not guarantee that the reservations are unique across multiple backends. The following is an example configuration with two reservations for the same IP address but different MAC addresses: :: "Dhcp4": { "ip-reservations-unique": false, "subnet4": [ { "subnet": "192.0.2.0/24", "reservations": [ { "hw-address": "1a:1b:1c:1d:1e:1f", "ip-address": "192.0.2.11" }, { "hw-address": "2a:2b:2c:2d:2e:2f", "ip-address": "192.0.2.11" } ] } ] } It is possible to control the ``ip-reservations-unique`` parameter via the :ref:`dhcp4-cb`. If the new setting of this parameter conflicts with the currently used backends (backends do not support the new setting), the new setting is ignored and a warning log message is generated. The backends continue to use the default setting, expecting that IP reservations are unique within each subnet. To allow the creation of non-unique IP reservations, the administrator must remove the backends which lack support for them from the configuration file. Administrators must be careful when they have been using multiple reservations for the same IP address and later decide to return to the default mode in which this is no longer allowed. Admins must make sure that at most one reservation for a given IP address exists within a subnet, prior to switching back to the default mode. If such duplicates are left in the configuration file, the server reports a configuration error. Leaving such reservations in the host databases does not cause configuration errors but may lead to lease allocation errors during the server's operation, when it unexpectedly finds multiple reservations for the same IP address. .. note:: Currently the server does not verify whether multiple reservations for the same IP address exist in MySQL and/or PostgreSQL host databases when ``ip-reservations-unique`` is updated from ``true`` to ``false``. This may cause issues with lease allocations. The administrator must ensure that there is at most one reservation for each IP address within each subnet, prior to the configuration update. .. _shared-network4: Shared Networks in DHCPv4 ========================= DHCP servers use subnet information in two ways. It is used to both determine the point of attachment, i.e. where the client is connected to the network, and to group information pertaining to a specific location in the network. However, it is sometimes useful to have more than one logical IP subnet deployed on the same physical link. Understanding that two or more subnets are used on the same link requires additional logic in the DHCP server. This capability is called "shared networks" in Kea, and sometimes also "shared subnets"; in Microsoft's nomenclature it is called "multinet." There are many use cases where the feature is useful; here we explain just a handful of the most common ones. The first and by far most common use case is an existing network that has grown and is running out of available address space. Rather than migrating all devices to a new, larger subnet, it is easier to simply configure additional subnets on top of the existing one. Sometimes, due to address space fragmentation (e.g. only many disjointed /24s are available), this is the only choice. Also, configuring additional subnets has the advantage of not disrupting the operation of existing devices. Another very frequent use case comes from cable networks. There are two types of devices in cable networks: cable modems and the end-user devices behind them. It is a common practice to use different subnets for cable modems to prevent users from tinkering with them. In this case, the distinction is based on the type of device, rather than on address-space exhaustion. A client connected to a shared network may be assigned an address from any of the pools defined within the subnets belonging to the shared network. Internally, the server selects one of the subnets belonging to a shared network and tries to allocate an address from this subnet. If the server is unable to allocate an address from the selected subnet (e.g., due to address-pool exhaustion), it uses another subnet from the same shared network and tries to allocate an address from this subnet. The server typically allocates all addresses available in a given subnet before it starts allocating addresses from other subnets belonging to the same shared network. However, in certain situations the client can be allocated an address from another subnet before the address pools in the first subnet get exhausted; this sometimes occurs when the client provides a hint that belongs to another subnet, or the client has reservations in a subnet other than the default. .. note:: Deployments should not assume that Kea waits until it has allocated all the addresses from the first subnet in a shared network before allocating addresses from other subnets. In order to define a shared network an additional configuration scope is introduced: :: { "Dhcp4": { "shared-networks": [ { # Name of the shared network. It may be an arbitrary string # and it must be unique among all shared networks. "name": "my-secret-lair-level-1", # The subnet selector can be specified at the shared network level. # Subnets from this shared network will be selected for directly # connected clients sending requests to the server's "eth0" interface. "interface": "eth0", # This starts a list of subnets in this shared network. # There are two subnets in this example. "subnet4": [ { "subnet": "10.0.0.0/8", "pools": [ { "pool": "10.0.0.1 - 10.0.0.99" } ], }, { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.100 - 192.0.2.199" } ] } ], } ], # end of shared-networks # It is likely that in the network there will be a mix of regular, # "plain" subnets and shared networks. It is perfectly valid to mix # them in the same configuration file. # # This is a regular subnet. It is not part of any shared network. "subnet4": [ { "subnet": "192.0.3.0/24", "pools": [ { "pool": "192.0.3.1 - 192.0.3.200" } ], "interface": "eth1" } ] } # end of Dhcp4 } As demonstrated in the example, it is possible to mix shared and regular ("plain") subnets. Each shared network must have a unique name. This is similar to the ID for subnets, but gives administrators more flexibility. It is used for logging, but also internally for identifying shared networks. In principle it makes sense to define only shared networks that consist of two or more subnets. However, for testing purposes, an empty subnet or a network with just a single subnet is allowed. This is not a recommended practice in production networks, as the shared network logic requires additional processing and thus lowers the server's performance. To avoid unnecessary performance degradation, the shared subnets should only be defined when required by the deployment. Shared networks provide the ability to specify many parameters in the shared network scope that apply to all subnets within it. If necessary, it is possible to specify a parameter in the shared network scope and then override its value in the subnet scope. For example: :: "shared-networks": [ { "name": "lab-network3", "interface": "eth0", # This applies to all subnets in this shared network, unless # values are overridden on subnet scope. "valid-lifetime": 600, # This option is made available to all subnets in this shared # network. "option-data": [ { "name": "log-servers", "data": "1.2.3.4" } ], "subnet4": [ { "subnet": "10.0.0.0/8", "pools": [ { "pool": "10.0.0.1 - 10.0.0.99" } ], # This particular subnet uses different values. "valid-lifetime": 1200, "option-data": [ { "name": "log-servers", "data": "10.0.0.254" }, { "name": "routers", "data": "10.0.0.254" } ] }, { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.100 - 192.0.2.199" } ], # This subnet does not specify its own valid-lifetime value, # so it is inherited from shared network scope. "option-data": [ { "name": "routers", "data": "192.0.2.1" } ] } ] } ] In this example, there is a `"log-servers"` option defined that is available to clients in both subnets in this shared network. Also, the valid lifetime is set to 10 minutes (600s). However, the first subnet overrides some of the values (the valid lifetime is 20 minutes, there is a different IP address for `"log-servers"`), but also adds its own option (the router address). Assuming a client asking for router and `"log-servers"` options is assigned a lease from this subnet, it will get a lease for 20 minutes and a `"log-servers"` and routers value of 10.0.0.254. If the same client is assigned to the second subnet, it will get a 10-minute lease, a `"log-servers"` value of 1.2.3.4, and routers set to 192.0.2.1. Local and Relayed Traffic in Shared Networks -------------------------------------------- It is possible to specify an interface name at the shared network level, to tell the server that this specific shared network is reachable directly (not via relays) using the local network interface. As all subnets in a shared network are expected to be used on the same physical link, it is a configuration error to attempt to define a shared network using subnets that are reachable over different interfaces. In other words, all subnets within the shared network must have the same value of the `"interface"` parameter. The following configuration is an example of what **NOT** to do: :: "shared-networks": [ { "name": "office-floor-2", "subnet4": [ { "subnet": "10.0.0.0/8", "pools": [ { "pool": "10.0.0.1 - 10.0.0.99" } ], "interface": "eth0" }, { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.100 - 192.0.2.199" } ], # Specifying the different interface name is a configuration # error. This value should rather be "eth0" or the interface # name in the other subnet should be "eth1". "interface": "eth1" } ] } ] To minimize the chance of configuration errors, it is often more convenient to simply specify the interface name once, at the shared network level, as shown in the example below. :: "shared-networks": [ { "name": "office-floor-2", # This tells Kea that the whole shared network is reachable over a # local interface. This applies to all subnets in this network. "interface": "eth0", "subnet4": [ { "subnet": "10.0.0.0/8", "pools": [ { "pool": "10.0.0.1 - 10.0.0.99" } ], }, { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.100 - 192.0.2.199" } ] } ] } ] With relayed traffic, subnets are typically selected using the relay agents' addresses. If the subnets are used independently (not grouped within a shared network), a different relay address can be specified for each of these subnets. When multiple subnets belong to a shared network they must be selected via the same relay address and, similarly to the case of the local traffic described above, it is a configuration error to specify different relay addresses for the respective subnets in the shared network. The following configuration is another example of what **NOT** to do: :: "shared-networks": [ { "name": "kakapo", "subnet4": [ { "subnet": "192.0.2.0/26", "relay": { "ip-addresses": [ "192.1.1.1" ] }, "pools": [ { "pool": "192.0.2.63 - 192.0.2.63" } ] }, { "subnet": "10.0.0.0/24", "relay": { # Specifying a different relay address for this # subnet is a configuration error. In this case # it should be 192.1.1.1 or the relay address # in the previous subnet should be 192.2.2.2. "ip-addresses": [ "192.2.2.2" ] }, "pools": [ { "pool": "10.0.0.16 - 10.0.0.16" } ] } ] } ] Again, it is better to specify the relay address at the shared network level; this value will be inherited by all subnets belonging to the shared network. :: "shared-networks": [ { "name": "kakapo", "relay": { # This relay address is inherited by both subnets. "ip-addresses": [ "192.1.1.1" ] }, "subnet4": [ { "subnet": "192.0.2.0/26", "pools": [ { "pool": "192.0.2.63 - 192.0.2.63" } ] }, { "subnet": "10.0.0.0/24", "pools": [ { "pool": "10.0.0.16 - 10.0.0.16" } ] } ] } ] Even though it is technically possible to configure two (or more) subnets within the shared network to use different relay addresses, this will almost always lead to a different behavior than what the user expects. In this case, the Kea server will initially select one of the subnets by matching the relay address in the client's packet with the subnet's configuration. However, it MAY end up using the other subnet (even though it does not match the relay address) if the client already has a lease in this subnet or has a host reservation in this subnet, or simply if the initially selected subnet has no more addresses available. Therefore, it is strongly recommended to always specify subnet selectors (interface or relay address) at the shared-network level if the subnets belong to a shared network, as it is rarely useful to specify them at the subnet level and it may lead to the configuration errors described above. Client Classification in Shared Networks ---------------------------------------- Sometimes it is desirable to segregate clients into specific subnets based on certain properties. This mechanism is called client classification and is described in :ref:`classify`. Client classification can be applied to subnets belonging to shared networks in the same way as it is used for subnets specified outside of shared networks. It is important to understand how the server selects subnets for clients when client classification is in use, to ensure that the appropriate subnet is selected for a given client type. If a subnet is associated with a class, only the clients belonging to this class can use this subnet. If there are no classes specified for a subnet, any client connected to a given shared network can use this subnet. A common mistake is to assume that a subnet including a client class is preferred over subnets without client classes. Consider the following example: :: { "client-classes": [ { "name": "b-devices", "test": "option[93].hex == 0x0002" } ], "shared-networks": [ { "name": "galah", "interface": "eth0", "subnet4": [ { "subnet": "192.0.2.0/26", "pools": [ { "pool": "192.0.2.1 - 192.0.2.63" } ], }, { "subnet": "10.0.0.0/24", "pools": [ { "pool": "10.0.0.2 - 10.0.0.250" } ], "client-class": "b-devices" } ] } ] } If the client belongs to the `"b-devices"` class (because it includes option 93 with a value of 0x0002), that does not guarantee that the subnet 10.0.0.0/24 will be used (or preferred) for this client. The server can use either of the two subnets, because the subnet 192.0.2.0/26 is also allowed for this client. The client classification used in this case should be perceived as a way to restrict access to certain subnets, rather than a way to express subnet preference. For example, if the client does not belong to the `"b-devices"` class, it may only use the subnet 192.0.2.0/26 and will never use the subnet 10.0.0.0/24. A typical use case for client classification is in a cable network, where cable modems should use one subnet and other devices should use another subnet within the same shared network. In this case it is necessary to apply classification on all subnets. The following example defines two classes of devices, and the subnet selection is made based on option 93 values. :: { "client-classes": [ { "name": "a-devices", "test": "option[93].hex == 0x0001" }, { "name": "b-devices", "test": "option[93].hex == 0x0002" } ], "shared-networks": [ { "name": "galah", "interface": "eth0", "subnet4": [ { "subnet": "192.0.2.0/26", "pools": [ { "pool": "192.0.2.1 - 192.0.2.63" } ], "client-class": "a-devices" }, { "subnet": "10.0.0.0/24", "pools": [ { "pool": "10.0.0.2 - 10.0.0.250" } ], "client-class": "b-devices" } ] } ] } In this example each class has its own restriction. Only clients that belong to class `"a-devices"` are able to use subnet 192.0.2.0/26 and only clients belonging to `"b-devices"` are able to use subnet 10.0.0.0/24. Care should be taken not to define too-restrictive classification rules, as clients that are unable to use any subnets will be refused service. However, this may be a desired outcome if one wishes to provide service only to clients with known properties (e.g. only VoIP phones allowed on a given link). Note that it is possible to achieve an effect similar to the one presented in this section without the use of shared networks. If the subnets are placed in the global subnets scope, rather than in the shared network, the server will still use classification rules to pick the right subnet for a given class of devices. The major benefit of placing subnets within the shared network is that common parameters for the logically grouped subnets can be specified once in the shared-network scope, e.g. the `"interface"` or `"relay"` parameter. All subnets belonging to this shared network will inherit those parameters. Host Reservations in Shared Networks ------------------------------------ Subnets that are part of a shared network allow host reservations, similar to regular subnets: :: { "shared-networks": [ { "name": "frog", "interface": "eth0", "subnet4": [ { "subnet": "192.0.2.0/26", "id": 100, "pools": [ { "pool": "192.0.2.1 - 192.0.2.63" } ], "reservations": [ { "hw-address": "aa:bb:cc:dd:ee:ff", "ip-address": "192.0.2.28" } ] }, { "subnet": "10.0.0.0/24", "id": 101, "pools": [ { "pool": "10.0.0.1 - 10.0.0.254" } ], "reservations": [ { "hw-address": "11:22:33:44:55:66", "ip-address": "10.0.0.29" } ] } ] } ] } It is worth noting that Kea conducts additional checks when processing a packet if shared networks are defined. First, instead of simply checking whether there is a reservation for a given client in its initially selected subnet, Kea looks through all subnets in a shared network for a reservation. This is one of the reasons why defining a shared network may impact performance. If there is a reservation for a client in any subnet, that particular subnet is selected for the client. Although it is technically not an error, it is considered bad practice to define reservations for the same host in multiple subnets belonging to the same shared network. While not strictly mandatory, it is strongly recommended to use explicit `"id"` values for subnets if database storage will be used for host reservations. If an ID is not specified, the values for it are auto-generated, i.e. Kea assigns increasing integer values starting from 1. Thus, the auto-generated IDs are not stable across configuration changes. .. _dhcp4-serverid: Server Identifier in DHCPv4 =========================== The DHCPv4 protocol uses a "server identifier" to allow clients to discriminate between several servers present on the same link; this value is an IPv4 address of the server. The server chooses the IPv4 address of the interface on which the message from the client (or relay) has been received. A single server instance uses multiple server identifiers if it is receiving queries on multiple interfaces. It is possible to override the default server identifier values by specifying the `"dhcp-server-identifier"` option. This option configuration is only supported at the subnet, shared network, client class, and global levels. It must not be specified at the host-reservation level. When configuring the `"dhcp-server-identifier"` option at client-class level, the class must not set the ``only-if-required`` flag, because this class would not be evaluated before the server determines if the received DHCP message should be accepted for processing. Such classes are evaluated after subnet selection. See :ref:`dhcp4-required-class` for details. The following example demonstrates how to override the server identifier for a subnet: :: "subnet4": [ { "subnet": "192.0.2.0/24", "option-data": [ { "name": "dhcp-server-identifier", "data": "10.2.5.76" } ], ... } ] .. _dhcp4-subnet-selection: How the DHCPv4 Server Selects a Subnet for the Client ===================================================== The DHCPv4 server differentiates between directly connected clients, clients trying to renew leases, and clients sending their messages through relays. For directly connected clients, the server checks the configuration for the interface on which the message has been received and, if the server configuration does not match any configured subnet, the message is discarded. Assuming that the server's interface is configured with the IPv4 address 192.0.2.3, the server only processes messages received through this interface from a directly connected client if there is a subnet configured to which this IPv4 address belongs, such as 192.0.2.0/24. The server uses this subnet to assign an IPv4 address for the client. The rule above does not apply when the client unicasts its message, i.e. is trying to renew its lease; such a message is accepted through any interface. The renewing client sets ``ciaddr`` to the currently used IPv4 address, and the server uses this address to select the subnet for the client (in particular, to extend the lease using this address). If the message is relayed it is accepted through any interface. The ``giaddr`` set by the relay agent is used to select the subnet for the client. It is also possible to specify a relay IPv4 address for a given subnet. It can be used to match incoming packets into a subnet in uncommon configurations, e.g. shared networks. See :ref:`dhcp4-relay-override` for details. .. note:: The subnet selection mechanism described in this section is based on the assumption that client classification is not used. The classification mechanism alters the way in which a subnet is selected for the client, depending on the classes to which the client belongs. .. note:: Starting with Kea 1.7.9, the order used to find a subnet which matches required conditions to be selected is the ascending subnet identifier order. When the selected subnet is a member of a shared network, the whole shared network is selected. .. _dhcp4-relay-override: Using a Specific Relay Agent for a Subnet ----------------------------------------- A relay must have an interface connected to the link on which the clients are being configured. Typically the relay has an IPv4 address configured on that interface, which belongs to the subnet from which the server assigns addresses. Normally, the server is able to use the IPv4 address inserted by the relay (in the ``giaddr`` field of the DHCPv4 packet) to select the appropriate subnet. However, that is not always the case. In certain uncommon — but valid — deployments, the relay address may not match the subnet. This usually means that there is more than one subnet allocated for a given link. The two most common examples where this is the case are long-lasting network renumbering (where both old and new address space is still being used) and a cable network. In a cable network, both cable modems and the devices behind them are physically connected to the same link, yet they use distinct addressing. In such a case, the DHCPv4 server needs additional information (the IPv4 address of the relay) to properly select an appropriate subnet. The following example assumes that there is a subnet 192.0.2.0/24 that is accessible via a relay that uses 10.0.0.1 as its IPv4 address. The server is able to select this subnet for any incoming packets that come from a relay that has an address in the 192.0.2.0/24 subnet. It also selects that subnet for a relay with address 10.0.0.1. :: "Dhcp4": { "subnet4": [ { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ], "relay": { "ip-addresses": [ "10.0.0.1" ] }, } ], } If `"relay"` is specified, the `"ip-addresses"` parameter within it is mandatory. The `"ip-addresses"` parameter supports specifying a list of addresses. .. _dhcp4-srv-example-client-class-relay: Segregating IPv4 Clients in a Cable Network ------------------------------------------- In certain cases, it is useful to mix relay address information (introduced in :ref:`dhcp4-relay-override`), with client classification (explained in :ref:`classify`). One specific example is in a cable network, where modems typically get addresses from a different subnet than all the devices connected behind them. Let us assume that there is one Cable Modem Termination System (CMTS)) with one CM MAC (a physical link that modems are connected to). We want the modems to get addresses from the 10.1.1.0/24 subnet, while everything connected behind the modems should get addresses from the 192.0.2.0/24 subnet. The CMTS that acts as a relay uses address 10.1.1.1. The following configuration can serve that configuration: :: "Dhcp4": { "subnet4": [ { "subnet": "10.1.1.0/24", "pools": [ { "pool": "10.1.1.2 - 10.1.1.20" } ], "client-class" "docsis3.0", "relay": { "ip-addresses": [ "10.1.1.1 ]" } }, { "subnet": "192.0.2.0/24", "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ], "relay": { "ip-addresses": [ "10.1.1.1" ] } } ], ... } .. _dhcp4-decline: Duplicate Addresses (DHCPDECLINE Support) ========================================= The DHCPv4 server is configured with a certain pool of addresses that it is expected to hand out to DHCPv4 clients. It is assumed that the server is authoritative and has complete jurisdiction over those addresses. However, for various reasons such as misconfiguration or a faulty client implementation that retains its address beyond the valid lifetime, there may be devices connected that use those addresses without the server's approval or knowledge. Such an unwelcome event can be detected by legitimate clients (using ARP or ICMP Echo Request mechanisms) and reported to the DHCPv4 server using a DHCPDECLINE message. The server does a sanity check (to see whether the client declining an address really was supposed to use it) and then conducts a clean-up operation. Any DNS entries related to that address are removed, the event is logged, and hooks are triggered. After that is complete, the address is marked as declined (which indicates that it is used by an unknown entity and thus not available for assignment) and a probation time is set on it. Unless otherwise configured, the probation period lasts 24 hours; after that period, the server will recover the lease (i.e. put it back into the available state) and the address will be available for assignment again. It should be noted that if the underlying issue of a misconfigured device is not resolved, the duplicate-address scenario will repeat. If reconfigured correctly, this mechanism provides an opportunity to recover from such an event automatically, without any system administrator intervention. To configure the decline probation period to a value other than the default, the following syntax can be used: :: "Dhcp4": { "decline-probation-period": 3600, "subnet4": [ ... ], ... } The parameter is expressed in seconds, so the example above instructs the server to recycle declined leases after one hour. There are several statistics and hook points associated with the decline handling procedure. The ``lease4_decline`` hook is triggered after the incoming DHCPDECLINE message has been sanitized and the server is about to decline the lease. The ``declined-addresses`` statistic is increased after the hook returns (both the global and subnet-specific variants). (See :ref:`dhcp4-stats` and :ref:`hooks-libraries` for more details on DHCPv4 statistics and Kea hook points.) Once the probation time elapses, the declined lease is recovered using the standard expired-lease reclamation procedure, with several additional steps. In particular, both ``declined-addresses`` statistics (global and subnet-specific) are decreased. At the same time, ``reclaimed-declined-addresses`` statistics (again in two variants, global and subnet-specific) are increased. A note about statistics: The server does not decrease the ``assigned-addresses`` statistics when a DHCPDECLINE is received and processed successfully. While technically a declined address is no longer assigned, the primary usage of the ``assigned-addresses`` statistic is to monitor pool utilization. Most people would forget to include ``declined-addresses`` in the calculation, and simply use ``assigned-addresses``/``total-addresses``. This would cause a bias towards under-representing pool utilization. As this has a potential to cause major issues, ISC decided not to decrease ``assigned-addresses`` immediately after receiving DHCPDECLINE, but to do it later when Kea recovers the address back to the available pool. .. _dhcp4-stats: Statistics in the DHCPv4 Server =============================== The DHCPv4 server supports the following statistics: .. tabularcolumns:: |p{0.2\linewidth}|p{0.1\linewidth}|p{0.7\linewidth}| .. table:: DHCPv4 Statistics :class: longtable :widths: 20 10 70 +-------------------------------------------+----------------+------------------------------------+ | Statistic | Data Type | Description | +===========================================+================+====================================+ | pkt4-received | integer | Number of DHCPv4 packets | | | | received. This includes all | | | | packets: valid, bogus, | | | | corrupted, rejected, etc. This | | | | statistic is expected to grow | | | | rapidly. | +-------------------------------------------+----------------+------------------------------------+ | pkt4-discover-received | integer | Number of | | | | DHCPDISCOVER packets | | | | received. This | | | | statistic is expected | | | | to grow; its increase | | | | means that clients | | | | that just booted | | | | started their | | | | configuration process | | | | and their initial | | | | packets reached the | | | | Kea server. | +-------------------------------------------+----------------+------------------------------------+ | pkt4-offer-received | integer | Number of DHCPOFFER | | | | packets received. | | | | This statistic is | | | | expected to remain | | | | zero at all times, as | | | | DHCPOFFER packets are | | | | sent by the server | | | | and the server is | | | | never expected to | | | | receive them. A | | | | non-zero value | | | | indicates an error. | | | | One likely cause | | | | would be a | | | | misbehaving relay | | | | agent that | | | | incorrectly forwards | | | | DHCPOFFER messages | | | | towards the server, | | | | rather than back to | | | | the clients. | +-------------------------------------------+----------------+------------------------------------+ | pkt4-request-received | integer | Number of DHCPREQUEST | | | | packets received. | | | | This statistic is | | | | expected to grow. Its | | | | increase means that | | | | clients that just | | | | booted received the | | | | server's response | | | | (DHCPOFFER) and | | | | accepted it, and are | | | | now requesting an | | | | address | | | | (DHCPREQUEST). | +-------------------------------------------+----------------+------------------------------------+ | pkt4-ack-received | integer | Number of DHCPACK | | | | packets received. | | | | This statistic is | | | | expected to remain | | | | zero at all times, as | | | | DHCPACK packets are | | | | sent by the server | | | | and the server is | | | | never expected to | | | | receive them. A | | | | non-zero value | | | | indicates an error. | | | | One likely cause | | | | would be a | | | | misbehaving relay | | | | agent that | | | | incorrectly forwards | | | | DHCPACK messages | | | | towards the server, | | | | rather than back to | | | | the clients. | +-------------------------------------------+----------------+------------------------------------+ | pkt4-nak-received | integer | Number of DHCPNAK | | | | packets received. | | | | This statistic is | | | | expected to remain | | | | zero at all times, as | | | | DHCPNAK packets are | | | | sent by the server | | | | and the server is | | | | never expected to | | | | receive them. A | | | | non-zero value | | | | indicates an error. | | | | One likely cause | | | | would be a | | | | misbehaving relay | | | | agent that | | | | incorrectly forwards | | | | DHCPNAK messages | | | | towards the server, | | | | rather than back to | | | | the clients. | +-------------------------------------------+----------------+------------------------------------+ | pkt4-release-received | integer | Number of DHCPRELEASE | | | | packets received. | | | | This statistic is | | | | expected to grow. Its | | | | increase means that | | | | clients that had an | | | | address are shutting | | | | down or ceasing to | | | | use their addresses. | +-------------------------------------------+----------------+------------------------------------+ | pkt4-decline-received | integer | Number of DHCPDECLINE | | | | packets received. | | | | This statistic is | | | | expected to remain | | | | close to zero. Its | | | | increase means that a | | | | client leased an | | | | address, but | | | | discovered that the | | | | address is currently | | | | used by an unknown | | | | device elsewhere in the | | | | network. | +-------------------------------------------+----------------+------------------------------------+ | pkt4-inform-received | integer | Number of DHCPINFORM | | | | packets received. | | | | This statistic is | | | | expected to grow. Its | | | | increase means that | | | | there are clients | | | | that either do not | | | | need an address or | | | | already have an | | | | address and are | | | | interested only in | | | | getting additional | | | | configuration | | | | parameters. | +-------------------------------------------+----------------+------------------------------------+ | pkt4-unknown-received | integer | Number of packets | | | | received of an | | | | unknown type. A | | | | non-zero value of | | | | this statistic | | | | indicates that the | | | | server received a | | | | packet that it was not | | | | able to recognize, | | | | either with an | | | | unsupported type or | | | | possibly malformed | | | | (without a message-type | | | | option). | +-------------------------------------------+----------------+------------------------------------+ | pkt4-sent | integer | Number of DHCPv4 | | | | packets sent. This | | | | statistic is expected | | | | to grow every time | | | | the server transmits | | | | a packet. In general, | | | | it should roughly | | | | match pkt4-received, | | | | as most incoming | | | | packets cause the | | | | server to respond. | | | | There are exceptions | | | | (e.g. DHCPRELEASE), | | | | so do not worry if it | | | | is less than | | | | pkt4-received. | +-------------------------------------------+----------------+------------------------------------+ | pkt4-offer-sent | integer | Number of DHCPOFFER | | | | packets sent. This | | | | statistic is expected | | | | to grow in most cases | | | | after a DHCPDISCOVER | | | | is processed. There | | | | are certain uncommon, | | | | but valid, cases | | | | where incoming | | | | DHCPDISCOVER packets | | | | are dropped, but in | | | | general this | | | | statistic is expected | | | | to be close to | | | | pkt4-discover-received. | +-------------------------------------------+----------------+------------------------------------+ | pkt4-ack-sent | integer | Number of DHCPACK | | | | packets sent. This | | | | statistic is expected | | | | to grow in most cases | | | | after a DHCPREQUEST | | | | is processed. There | | | | are certain cases | | | | where DHCPNAK is sent | | | | instead. In general, | | | | the sum of | | | | pkt4-ack-sent and | | | | pkt4-nak-sent should | | | | be close to | | | | pkt4-request-received. | +-------------------------------------------+----------------+------------------------------------+ | pkt4-nak-sent | integer | Number of DHCPNAK | | | | packets sent. This | | | | statistic is expected | | | | to grow when the | | | | server chooses not to | | | | honor the address | | | | requested by a | | | | client. In general, | | | | the sum of | | | | pkt4-ack-sent and | | | | pkt4-nak-sent should | | | | be close to | | | | pkt4-request-received. | +-------------------------------------------+----------------+------------------------------------+ | pkt4-parse-failed | integer | Number of incoming | | | | packets that could | | | | not be parsed. A | | | | non-zero value of | | | | this statistic | | | | indicates that the | | | | server received a | | | | malformed or | | | | truncated packet. | | | | This may indicate | | | | problems in the | | | | network, faulty | | | | clients, or a bug in | | | | the server. | +-------------------------------------------+----------------+------------------------------------+ | pkt4-receive-drop | integer | Number of incoming | | | | packets that were | | | | dropped. The exact | | | | reason for dropping | | | | packets is logged, | | | | but the most common | | | | reasons may be: an | | | | unacceptable packet | | | | type was received, direct | | | | responses are | | | | forbidden, or the | | | | server-id sent by the | | | | client does not match | | | | the server's | | | | server-id. | +-------------------------------------------+----------------+------------------------------------+ | subnet[id].total-addresses | integer | Total number of | | | | addresses available | | | | for DHCPv4 | | | | management; in other | | | | words, this is the | | | | sum of all addresses | | | | in all configured | | | | pools. This statistic | | | | changes only during | | | | configuration | | | | updates. It does | | | | not take into account | | | | any addresses that | | | | may be reserved due | | | | to host reservation. | | | | The *id* is the | | | | subnet-id of a given | | | | subnet. This | | | | statistic is exposed | | | | for each subnet | | | | separately, and is | | | | reset during a | | | | reconfiguration | | | | event. | +-------------------------------------------+----------------+------------------------------------+ | cumulative-assigned-addresses | integer | Cumulative number of | | | | addresses that have been | | | | assigned since | | | | server startup. It is | | | | incremented each time | | | | an address is assigned and | | | | is not reset when the server | | | | is reconfigured. | +-------------------------------------------+----------------+------------------------------------+ | subnet[id].cumulative-assigned-addresses | integer | Cumulative number of assigned | | | | addresses in a given | | | | subnet. It increases | | | | every time a new | | | | lease is allocated | | | | (as a result of | | | | receiving a | | | | DHCPREQUEST message) | | | | and never decreases. | | | | The *id* is the subnet-id | | | | of the subnet. This | | | | statistic is exposed | | | | for each subnet | | | | separately, and is | | | | reset during a | | | | reconfiguration | | | | event. | +-------------------------------------------+----------------+------------------------------------+ | subnet[id].assigned-addresses | integer | Number of assigned | | | | addresses in a given | | | | subnet. It increases | | | | every time a new | | | | lease is allocated | | | | (as a result of | | | | receiving a | | | | DHCPREQUEST message) | | | | and decreases | | | | every time a lease is | | | | released (a | | | | DHCPRELEASE message | | | | is received) or | | | | expires. The *id* is | | | | the subnet-id of the | | | | subnet. This | | | | statistic is exposed | | | | for each subnet | | | | separately, and is | | | | reset during a | | | | reconfiguration | | | | event. | +-------------------------------------------+----------------+------------------------------------+ | reclaimed-leases | integer | Number of expired | | | | leases that have been | | | | reclaimed since | | | | server startup. It is | | | | incremented each time | | | | an expired lease is | | | | reclaimed and never | | | | decreases. It can be | | | | used as a long-term | | | | indicator of how many | | | | actual leases have been | | | | reclaimed. | | | | This is a global | | | | statistic that covers | | | | all subnets. | +-------------------------------------------+----------------+------------------------------------+ | subnet[id].reclaimed-leases | integer | Number of expired | | | | leases associated | | | | with a given subnet | | | | (*id* is the | | | | subnet-id) that have | | | | been reclaimed since | | | | server startup. It is | | | | incremented each time | | | | an expired lease is | | | | reclaimed. | | | | The *id* is the | | | | subnet-id of a | | | | given subnet. This | | | | statistic is exposed | | | | for each subnet | | | | separately. | +-------------------------------------------+----------------+------------------------------------+ | declined-addresses | integer | Number of IPv4 | | | | addresses that are | | | | currently declined; a | | | | count of the number | | | | of leases currently | | | | unavailable. Once a | | | | lease is recovered, | | | | this statistic is | | | | decreased; | | | | ideally, this | | | | statistic should be | | | | zero. If this | | | | statistic is non-zero | | | | or increasing, a | | | | network administrator | | | | should investigate | | | | whether there is a | | | | misbehaving device in | | | | the network. This is | | | | a global statistic | | | | that covers all | | | | subnets. | +-------------------------------------------+----------------+------------------------------------+ | subnet[id].declined-addresses | integer | Number of IPv4 | | | | addresses that are | | | | currently declined in | | | | a given subnet; a | | | | count of the number | | | | of leases currently | | | | unavailable. Once a | | | | lease is recovered, | | | | this statistic is | | | | decreased; | | | | ideally, this | | | | statistic should be | | | | zero. If this | | | | statistic is non-zero | | | | or increasing, a | | | | network administrator | | | | should investigate | | | | whether there is a | | | | misbehaving device in | | | | the network. The *id* | | | | is the subnet-id of a | | | | given subnet. This | | | | statistic is exposed | | | | for each subnet | | | | separately. | +-------------------------------------------+----------------+------------------------------------+ | reclaimed-declined-addresses | integer | Number of IPv4 | | | | addresses that were | | | | declined, but have | | | | now been recovered. | | | | Unlike | | | | declined-addresses, | | | | this statistic never | | | | decreases. It can be | | | | used as a long-term | | | | indicator of how many | | | | actual valid declines | | | | were processed and | | | | recovered from. This | | | | is a global statistic | | | | that covers all | | | | subnets. | +-------------------------------------------+----------------+------------------------------------+ | subnet[id].reclaimed-declined-addresses | integer | Number of IPv4 | | | | addresses that were | | | | declined, but have | | | | now been recovered. | | | | Unlike | | | | declined-addresses, | | | | this statistic never | | | | decreases. It can be | | | | used as a long-term | | | | indicator of how many | | | | actual valid declines | | | | were processed and | | | | recovered from. The | | | | *id* is the subnet-id | | | | of a given subnet. | | | | This statistic is | | | | exposed for each | | | | subnet separately. | +-------------------------------------------+----------------+------------------------------------+ | pkt4-lease-query-received | integer | Number of IPv4 DHCPLEASEQUERY | | | | packets received. (Only exists if | | | | Leasequery hook library is | | | | loaded.) | +-------------------------------------------+----------------+------------------------------------+ | pkt4-lease-query-response-unknown-sent | integer | Number of IPv4 DHCPLEASEUNKNOWN | | | | responses sent. (Only exists if | | | | Leasequery hook library is | | | | loaded.) | +-------------------------------------------+----------------+------------------------------------+ | pkt4-lease-query-response-unassigned-sent | integer | Number of IPv4 DHCPLEASEUNASSIGNED | | | | responses sent. (Only exists if | | | | Leasequery hook library is | | | | loaded.) | +-------------------------------------------+----------------+------------------------------------+ | pkt4-lease-query-response-active-sent | integer | Number of IPv4 DHCPLEASEACTIVE | | | | responses sent. (Only exists if | | | | Leasequery hook library is | | | | loaded.) | +-------------------------------------------+----------------+------------------------------------+ .. note:: This section describes DHCPv4-specific statistics. For a general overview and usage of statistics, see :ref:`stats`. Since Kea 1.7.7, the DHCPv4 server provides two global parameters to control statistics default sample limits: - ``statistic-default-sample-count`` - determines the default maximum number of samples which are kept. The special value of zero indicates that a default maximum age should be used. - ``statistic-default-sample-age`` - determines the default maximum age in seconds of samples which are kept. For instance, to reduce the statistic-keeping overhead, set the default maximum sample count to 1 so only one sample is kept: :: "Dhcp4": { "statistic-default-sample-count": 1, "subnet4": [ ... ], ... } Statistics can be retrieved periodically to gain more insight into Kea operations. One tool that leverages that capability is ISC Stork. See :ref:`stork` for details. .. _dhcp4-ctrl-channel: Management API for the DHCPv4 Server ==================================== The management API allows the issuing of specific management commands, such as statistics retrieval, reconfiguration, or shutdown. For more details, see :ref:`ctrl-channel`. Currently, the only supported communication channel type is the UNIX stream socket. By default there are no sockets open; to instruct Kea to open a socket, the following entry in the configuration file can be used: :: "Dhcp4": { "control-socket": { "socket-type": "unix", "socket-name": "/path/to/the/unix/socket" }, "subnet4": [ ... ], ... } The length of the path specified by the ``socket-name`` parameter is restricted by the maximum length for the UNIX socket name on the administrator's operating system, i.e. the size of the ``sun_path`` field in the ``sockaddr_un`` structure, decreased by 1. This value varies on different operating systems, between 91 and 107 characters. Typical values are 107 on Linux and 103 on FreeBSD. Communication over the control channel is conducted using JSON structures. See the `Control Channel section in the Kea Developer's Guide `__ for more details. The DHCPv4 server supports the following operational commands: - build-report - config-get - config-reload - config-set - config-test - config-write - dhcp-disable - dhcp-enable - leases-reclaim - list-commands - shutdown - status-get - version-get as described in :ref:`commands-common`. In addition, it supports the following statistics-related commands: - statistic-get - statistic-reset - statistic-remove - statistic-get-all - statistic-reset-all - statistic-remove-all - statistic-sample-age-set - statistic-sample-age-set-all - statistic-sample-count-set - statistic-sample-count-set-all as described in :ref:`command-stats`. .. _dhcp4-user-contexts: User Contexts in IPv4 ===================== Kea allows the loading of hook libraries that can sometimes benefit from additional parameters. If such a parameter is specific to the whole library, it is typically defined as a parameter for the hook library. However, sometimes there is a need to specify parameters that are different for each pool. See :ref:`user-context` for additional background regarding the user context idea. See :ref:`user-context-hooks` for a discussion from the hooks perspective. User contexts can be specified at global scope; at the shared network, subnet, pool, client class, option data, or definition level; and via host reservation. One other useful feature is the ability to store comments or descriptions. Let's consider an imaginary case of devices that have colored LED lights. Depending on their location, they should glow red, blue, or green. It would be easy to write a hook library that would send specific values, maybe as a vendor option. However, the server has to have some way to specify that value for each pool. This need is addressed by user contexts. In essence, any user data can be specified in the user context as long as it is a valid JSON map. For example, the aforementioned case of LED devices could be configured in the following way: :: "Dhcp4": { "subnet4": [{ "subnet": "192.0.2.0/24", "pools": [{ "pool": "192.0.2.10 - 192.0.2.20", # This is pool specific user context "user-context": { "color": "red" } } ], # This is a subnet-specific user context. Any type # of information can be entered here as long as it is valid JSON. "user-context": { "comment": "network on the second floor", "last-modified": "2017-09-04 13:32", "description": "you can put anything you like here", "phones": [ "x1234", "x2345" ], "devices-registered": 42, "billing": false } } ] } Kea does not interpret or use the user-context information; it simply stores it and makes it available to the hook libraries. It is up to each hook library to extract that information and use it. The parser translates a "comment" entry into a user context with the entry, which allows a comment to be attached inside the configuration itself. .. _dhcp4-std: Supported DHCP Standards ======================== The following standards are currently supported: - *BOOTP Vendor Information Extensions*, `RFC 1497 `__: This requires the open source BOOTP hook to be loaded. See :ref:`hooks-bootp` for details. - *Dynamic Host Configuration Protocol*, `RFC 2131 `__: Supported messages are DHCPDISCOVER (1), DHCPOFFER (2), DHCPREQUEST (3), DHCPRELEASE (7), DHCPINFORM (8), DHCPACK (5), and DHCPNAK(6). - *DHCP Options and BOOTP Vendor Extensions*, `RFC 2132 `__: Supported options are PAD (0), END(255), Message Type(53), DHCP Server Identifier (54), Domain Name (15), DNS Servers (6), IP Address Lease Time (51), Subnet Mask (1), and Routers (3). - *The IPv4 Subnet Selection Option for DHCP*, `RFC 3011 `__: The subnet selection option is supported. If received in a packet, it is used in the subnet selection process. - *DHCP Relay Agent Information Option*, `RFC 3046 `__: Relay Agent Information, Circuit ID, and Remote ID options are supported. - *Link Selection sub-option for the Relay Agent Option*, `RFC 3527 `__: The link selection sub-option is supported. - *Vendor-Identifying Vendor Options for Dynamic Host Configuration Protocol version 4*, `RFC 3925 `__: Vendor-Identifying Vendor Class and Vendor-Identifying Vendor-Specific Information options are supported. - *Subscriber-ID Suboption for the DHCP Relay Agent Option*, `RFC 3993 `__: The Subscriber-ID option is supported. - *The Dynamic Host Configuration Protocol (DHCP) Client Fully Qualified Domain Name (FQDN) Option*, `RFC 4702 `__: The Kea server is able to handle the Client FQDN option. Also, it is able to use the ``kea-dhcp-ddns`` component to initiate appropriate DNS Update operations. - *Resolution of Fully Qualified Domain Name (FQDN) Conflicts among Dynamic Host Configuration Protocol (DHCP) Clients*, `RFC 4703 `__: The DHCPv6 server uses a DHCP-DDNS server to resolve conflicts. - *Client Identifier Option in DHCP Server Replies*, `RFC 6842 `__: The server by default sends back the ``client-id`` option. That capability may be disabled. See :ref:`dhcp4-echo-client-id` for details. - *Generalized UDP Source Port for DHCP Relay*, `RFC 8357 `__: The Kea server handles the Relay Agent Information Source Port sub-option in a received message, remembers the UDP port, and sends back reply to the same relay agent using this UDP port. - *IPv6-Only Preferred Option for DHCPv4*, `RFC 8925 `__: The Kea server is able to designate its pools and subnets as IPv6-Only Preferred and send back the ``v6-only-preferred`` option to clients that requested it. Known RFC Violations -------------------- In principle, Kea aspires to be a reference implementation and aims to implement 100% of the RFC standards. However, in some cases there are practical aspects that prevent Kea from completely adhering to the text of the RFC documents. - `RFC 2131 `__, page 30, says that if the incoming DHCPREQUEST packet has no `requested IP address` option and ``ciaddr`` is not set, the server is supposed to respond with NAK. However, broken clients exist that will always send a DHCPREQUEST without those indicated. In that event, Kea accepts the DHCPREQUEST, assigns an address, and responds with an ACK. - `RFC 2131 `__, table 5, says that messages of type DHCPDECLINE or DHCPRELEASE must have the server identifier set and should be dropped if that option is missing. However, ISC DHCP does not enforce this, presumably as a compatibility effort for broken clients, and the Kea team decided to follow suit. .. _dhcp4-limit: DHCPv4 Server Limitations ========================= These are the current known limitations of the Kea DHCPv4 server software. Most of them are reflections of the current stage of development and should be treated as “not implemented yet,” rather than as actual limitations. However, some of them are implications of the design choices made. Those are clearly marked as such. - On the Linux and BSD system families, DHCP messages are sent and received over raw sockets (using LPF and BPF) and all packet headers (including data link layer, IP, and UDP headers) are created and parsed by Kea, rather than by the system kernel. Currently, Kea can only parse the data-link layer headers with a format adhering to the IEEE 802.3 standard, and assumes this data link layer header format for all interfaces. Thus, Kea does not work on interfaces which use different data-link layer header formats (e.g. Infiniband). - The DHCPv4 server does not verify that an assigned address is unused. According to `RFC 2131 `__, the allocating server should verify that an address is not used by sending an ICMP echo request. .. _dhcp4-srv-examples: Kea DHCPv4 Server Examples ========================== A collection of simple-to-use examples for the DHCPv4 component of Kea is available with the source files, located in the `doc/examples/kea4` directory. .. _dhcp4-cb: Configuration Backend in DHCPv4 =============================== In the :ref:`config-backend` section we have described the Configuration Backend (CB) feature, its applicability, and its limitations. This section focuses on the usage of the CB with the Kea DHCPv4 server. It lists the supported parameters, describes limitations, and gives examples of DHCPv4 server configurations to take advantage of the CB. Please also refer to the corresponding section :ref:`dhcp6-cb` for DHCPv6-specific usage of the CB. .. _dhcp4-cb-parameters: Supported Parameters -------------------- The ultimate goal for the CB is to serve as a central configuration repository for one or multiple Kea servers connected to a database. In currently supported Kea versions, only a subset of the DHCPv4 server parameters can be configured in the database. All other parameters must be specified in the JSON configuration file, if required. The following table lists DHCPv4-specific parameters supported by the Configuration Backend, with an indication of the level of the hierarchy at which it is currently supported. "n/a" marks cases when a given parameter is not applicable at the particular level of the hierarchy or in cases when the server does not support the parameter at this level of the hierarchy. "no" is used when a parameter is supported at the given level of the hierarchy but is not configurable via the Configuration Backend. All supported parameters can be configured via the ``cb_cmds`` hook library described in the :ref:`cb-cmds-library` section. The general rule is that scalar global parameters are set using ``remote-global-parameter4-set``; shared network-specific parameters are set using ``remote-network4-set``; and subnet- and pool-level parameters are set using ``remote-subnet4-set``. Whenever there is an exception to this general rule, it is highlighted in the table. Non-scalar global parameters have dedicated commands; for example, the global DHCPv4 options (``option-data``) are modified using ``remote-option4-global-set``. Client classes, together with class-specific option definitions and DHCPv4 options, are configured using the ``remote-class4-set`` command. The :ref:`cb-sharing` section explains the concept of shareable and non-shareable configuration elements and the limitations for sharing them between multiple servers. In the DHCP configuration (both DHCPv4 and DHCPv6), the shareable configuration elements are subnets and shared networks. Thus, they can be explicitly associated with multiple server tags. The global parameters, option definitions, and global options are non-shareable and can be associated with only one server tag. This rule does not apply to the configuration elements associated with `"all"` servers. Any configuration element associated with `"all"` servers (using the `"all"` keyword as a server tag) is used by all servers connecting to the configuration database. .. table:: List of DHCPv4 Parameters Supported by the Configuration Backend +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | Parameter | Global | Client | Shared | Subnet | Pool | | | | Class | Network | | | +=============================+============================+==============+=============+=============+=============+ | 4o6-interface | n/a | n/a | n/a | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | 4o6-interface-id | n/a | n/a | n/a | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | 4o6-subnet | n/a | n/a | n/a | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | boot-file-name | yes | yes | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | cache-max-age | yes | n/a | todo | todo | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | cache-threshold | yes | n/a | todo | todo | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | calculate-tee-times | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | client-class | n/a | n/a | yes | yes | yes | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | ddns-send-update | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | ddns-override-no-update | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | ddns-override-client-update | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | ddns-replace-client-name | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | ddns-generated-prefix | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | ddns-qualifying-suffix | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | decline-probation-period | yes | n/a | n/a | n/a | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | dhcp4o6-port | yes | n/a | n/a | n/a | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | echo-client-id | yes | n/a | n/a | n/a | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | hostname-char-set | no | n/a | no | no | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | hostname-char-replacement | no | n/a | no | no | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | interface | n/a | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | match-client-id | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | min-valid-lifetime | yes | yes | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | max-valid-lifetime | yes | yes | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | next-server | yes | yes | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | option-data | yes (via | yes | yes | yes | yes | | | remote-option4-global-set) | | | | | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | option-def | yes (via | yes | n/a | n/a | n/a | | | remote-option-def4-set) | | | | | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | rebind-timer | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | renew-timer | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | server-hostname | yes | yes | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | valid-lifetime | yes | yes | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | relay | n/a | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | require-client-classes | no | n/a | yes | yes | yes | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | reservation-mode | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | reservations-global | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | reservations-in-subnet | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | reservations-out-of-pool | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | t1-percent | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ | t2-percent | yes | n/a | yes | yes | n/a | +-----------------------------+----------------------------+--------------+-------------+-------------+-------------+ .. _dhcp4-cb-json: Enabling the Configuration Backend ---------------------------------- Consider the following configuration snippet: :: "Dhcp4": { "server-tag": "my DHCPv4 server", "config-control": { "config-databases": [{ "type": "mysql", "name": "kea", "user": "kea", "password": "kea", "host": "192.0.2.1", "port": 3302 }], "config-fetch-wait-time": 20 }, "hooks-libraries": [{ "library": "/usr/local/lib/kea/hooks/libdhcp_mysql_cb.so" }, { "library": "/usr/local/lib/kea/hooks/libdhcp_cb_cmds.so" }], } The ``config-control`` command contains two parameters. ``config-databases`` is a list which contains one element comprising database type, location, and the credentials to be used to connect to this database. (Note that the parameters specified here correspond to the database specification for the lease database backend and hosts database backend.) Currently only one database connection can be specified on the ``config-databases`` list. The server connects to this database during startup or reconfiguration, and fetches the configuration available for this server from the database. This configuration is merged into the configuration read from the configuration file. .. note:: Whenever there is a conflict between the parameters specified in the configuration file and the database, the parameters from the database take precedence. We strongly recommend avoiding the duplication of parameters in the file and the database, but this recommendation is not enforced by the Kea servers. In particular, if the subnets' configuration is sourced from the database, we recommend that all subnets be specified in the database and that no subnets be specified in the configuration file. It is possible to specify the subnets in both places, but the subnets in the configuration file with overlapping IDs and/or prefixes with the subnets from the database will be superseded by those from the database. Once the Kea server is configured, it starts periodically polling the database for configuration changes. The polling frequency is controlled by the ``config-fetch-wait-time`` parameter, expressed in seconds; it is the period between the time when the server completed its last poll (and possibly the local configuration update) and the time when it will begin polling again. In the example above, this period is set to 20 seconds. This means that after adding a new configuration into the database (e.g. adding a new subnet), it will take up to 20 seconds (plus the time needed to fetch and apply the new configuration) before the server starts using this subnet. The lower the ``config-fetch-wait-time`` value, the shorter the time for the server to react to incremental configuration updates in the database. On the other hand, polling the database too frequently may impact the DHCP server's performance, because the server needs to make at least one query to the database to discover any pending configuration updates. The default value of ``config-fetch-wait-time`` is 30 seconds. The ``config-backend-pull`` command can be used to force the server to immediately poll any configuration changes from the database and avoid waiting for the next fetch cycle. (This command was added in Kea release 1.7.1 for both DHCPv4 and DHCPv6 servers.) Finally, in the configuration example above, two hook libraries are loaded. The first, ``libdhcp_mysql_cb.so``, is the implementation of the Configuration Backend for MySQL. It must be always present when the server uses MySQL as the configuration repository. Failing to load this library will result in an error during the server configuration if the `"mysql"` database is selected with the ``config-control`` parameter. The second hook library, ``libdhcp_cb_cmds.so``, is optional. It should be loaded when the Kea server instance is to be used to manage the configuration in the database. See the :ref:`cb-cmds-library` section for details. This hook library is only available to ISC customers with a paid support contract. .. _dhcp4-compatibility: Kea DHCPv4 Compatibility Configuration Parameters ================================================= ISC's intention is for Kea to follow the RFC documents to promote better standards compliance. However, many buggy DHCP implementations already exist that cannot be easily fixed or upgraded. Therefore, Kea provides an easy-to-use compatibility mode for broken or non-compliant clients. For that purpose, flags must be enabled to enable uncommon practices: .. code-block:: json { "Dhcp4": { "compatibility": { } } } Lenient Option Parsing ---------------------- By default, tuple fields defined in custom options are parsed as a set of length-value pairs. With ``lenient-option-parsing: "true"``, if a length ever exceeds the rest of the option's buffer, previous versions of Kea returned a log message ``unable to parse the opaque data tuple, the buffer length is x, but the tuple length is y`` with ``x < y``; this no longer occurs. Instead, the value is considered to be the rest of the buffer, or in terms of the log message above, the tuple length ``y`` becomes ``x``. .. code-block:: json { "Dhcp4": { "compatibility": { "lenient-option-parsing": true } } } kea-2.0.2/doc/sphinx/arm/congestion-handling.rst0000644000175000017500000001303214206773363016527 00000000000000.. _congestion-handling: ******************* Congestion Handling ******************* .. _congestion-handling-background: What is Congestion? =================== Congestion occurs when servers are subjected to client queries faster than those queries can be processed. As a result, the servers begin accumulating a backlog of pending queries. The longer the high rate of traffic continues, the farther behind the servers fall. Depending on the client implementations, those that fail to get leases either give up or simply continue to retry forever. In the former case, the server may eventually recover, but the latter case is a vicious cycle from which the server is unable to escape. In a well-planned deployment, the number and capacity of servers is matched to the maximum client loads expected. As long as capacity is matched to load, congestion does not occur. If the load is routinely too heavy, then the deployment needs to be re-evaluated. Congestion typically occurs when there is a network event that causes overly large numbers of clients to simultaneously need leases, such as recovery after a network outage. The goal of congestion handling is to help servers mitigate the peak in traffic by fulfilling as many of the most relevant requests as possible until the congestion subsides. Prior to Kea 1.5, kea-dhcp4 and kea-dhcp6 read inbound packets directly from the interface sockets in the main application thread, which meant that packets waiting to be processed were held in socket buffers themselves. Once these buffers filled, any new packets were discarded. Under swamped conditions, the servers ended up processing client packets that were no longer relevant, or worse, were redundant. In other words, the packets waiting in the FIFO socket buffers became increasingly stale. .. _congestion-handling-solution: Configuring Congestion Handling =============================== Kea 1.5 introduced the Congestion Handling feature. Congestion handling offers the ability to configure the server to use a separate thread to read packets from the interface socket buffers. As the thread reads packets from the buffers, they are added to an internal packet queue, and the server's main application thread processes packets from this queue rather than from the socket buffers. By structuring it this way, a configurable layer has been introduced which can make decisions on which packets to process, how to store them, and the order in which they are processed by the server. The default packet queue implementation for both kea-dhcp4 and kea-dhcp6 is a simple ring buffer. Once it reaches capacity, new packets get added to the back of the queue by discarding packets from the front of the queue. Rather than always discarding the newest packets, Kea now always discards the oldest packets. The capacity of the buffer, i.e. the maximum number of packets the buffer can contain, is configurable. A reasonable starting point would be to match the capacity to the number of leases per second a specific installation of Kea can handle. Please note that this figure varies widely depending on the specifics of an individual deployment. As there is no one algorithm that will best handle the dynamics of all sites, and because over time new approaches will evolve, the packet queue is implemented as a plug-in, which can be replaced by a custom queue implementation via a hook library. This should make it straightforward for interested parties to experiment with their own solutions. (Developers can refer to isc::dhcp::PacketQueue and isc::dhcp::PacketQueueMgr, described in the `Kea Developer's Guide `__.) Packet queue behavior is configured in both kea-dhcp4 and kea-dhcp6 servers through an optional, top-level, configuration element, ``dhcp-queue-control``. Omitting this element disables packet queueing: :: "dhcp-queue-control": { "enable-queue": true|false, "queue-type": "queue type", "capacity" : n } where: - ``enable-queue`` true|false - enables or disables packet queueing. When true, the server processes packets from the packet queue, which is filled by a separate thread. When false, the server processes packets directly from the socket buffers in the main thread. It is disabled by default. - ``queue-type`` - name of the queue implementation to use. This value exists so that custom implementations can be registered (via a hook library) and then selected. There is a default packet queue implementation that is pre-registered during server start up: "kea-ring4" for kea-dhcp4 and "kea-ring6" for kea-dhcp6. - ``capacity`` = n [packets] - this is the maximum number of packets the queue can hold before packets are discarded. The optimal value for this is extremely site-dependent. The default value is 64 for both kea-ring4 and kea-ring6. The following example enables the default packet queue for kea-dhcp4, with a queue capacity of 250 packets: :: "Dhcp4": { ... "dhcp-queue-control": { "enable-queue": true, "queue-type": "kea-ring4", "capacity" : 250 }, ... } The following example enables the default packet queue for kea-dhcp6, with a queue capacity of 300 packets: :: "Dhcp6": { ... "dhcp-queue-control": { "enable-queue": true, "queue-type": "kea-ring6", "capacity" : 300 }, ... } .. note: Currently the congestion handling is incompatible with multi-threading: when both are enabled the congestion handling is silently disabled. kea-2.0.2/doc/sphinx/arm/lease-expiration.rst0000644000175000017500000003466414206773363016064 00000000000000.. _lease-expiration: **************** Lease Expiration **************** The primary role of the DHCP server is to assign addresses and/or delegate prefixes to DHCP clients. These addresses and prefixes are often referred to as "leases." Leases are typically assigned to clients for a finite amount of time, known as the "valid lifetime." DHCP clients who wish to continue using their assigned leases periodically renew them by sending the appropriate message to the DHCP server. The DHCP server records the time when these leases are renewed and calculates new expiration times for them. If the client does not renew a lease before its valid lifetime elapses, the lease is considered expired. There are many situations when the client may cease lease renewals; a common scenario is when the machine running the client shuts down for an extended period of time. The process through which the DHCP server makes expired leases available for reassignment is referred to as "lease reclamation" and expired leases returned to availability through this process are referred to as "reclaimed." The DHCP server attempts to reclaim an expired lease as soon as it detects that it has expired. The server has several possible ways to detect expiration: it may attempt to allocate a lease to a client but find this lease already present in the database and expired; or it can periodically query the lease database for expired leases. Regardless of how an expired lease is detected, it must be reclaimed before it can be assigned to a client. This chapter explains how to configure the server to periodically query for the expired leases, and how to minimize the impact of the periodic lease reclamation process on the server's responsiveness. Finally, it explains "lease affinity," which provides the means to assign the same lease to a returning client after its lease has expired. Although all configuration examples in this section are provided for the DHCPv4 server, the same parameters may be used for DHCPv6 server configuration. .. _lease-reclamation: Lease Reclamation ================= Lease reclamation is the process through which an expired lease becomes available for assignment to the same or a different client. This process involves the following steps for each reclaimed lease: - Invoke callouts for the ``lease4_expire`` or ``lease6_expire`` hook points, if hook libraries supporting those callouts are currently loaded. - Update the DNS, i.e. remove any DNS entries associated with the expired lease. - Update lease information in the lease database to indicate that the lease is now available for re-assignment. - Update counters on the server, a process that includes increasing the number of reclaimed leases and decreasing the number of assigned addresses or delegated prefixes. Please refer to :ref:`dhcp-ddns-server` to see how to configure DNS updates in Kea, and to :ref:`hooks-libraries` for information about using hooks libraries. .. _lease-reclamation-defaults: Lease Reclamation Configuration Parameters ========================================== The following list presents all configuration parameters pertaining to processing expired leases with their default values: - ``reclaim-timer-wait-time`` - this parameter governs intervals between the completion of the previous reclamation cycle and the start of the next one. Specified in seconds. The default value is 10 [seconds]. - ``flush-reclaimed-timer-wait-time`` - this parameter controls how often the server initiates the lease reclamation procedure. Expressed in seconds. The default value is 25 [seconds]. - ``hold-reclaimed-time`` - this parameter governs how long the lease should be kept after it is reclaimed. This enables lease affinity when set to a non-zero value. Expressed in seconds. The default value is 3600 [seconds]. - ``max-reclaim-leases`` - this parameter specifies the maximum number of reclaimed leases that can be processed at one time. Zero means unlimited (i.e. process all reclaimed leases). The default value is 100. - ``max-reclaim-time`` - this parameter specifies an upper limit to the length of time a lease reclamation procedure can take. Zero means no time limit. Expressed in milliseconds. The default value is 250 [milliseconds]. - ``unwarned-reclaim-cycles`` - if lease reclamation limits are specified (``max-reclaim-leases`` and/or ``max-reclaim-time``), then under certain circumstances the server may not be able to deal with the leases to be reclaimed fast enough. This parameter specifies how many consecutive clean-up cycles must end with remaining leases to be processed before a warning is printed. The default is 5 [cycles]. The parameters are explained in more detail in the rest of this chapter. The default value for any parameter is used when the parameter is not explicitly specified in the configuration. If the ``expired-leases-processing`` map is omitted entirely in the configuration, the default values are used for all parameters listed above. .. _lease-reclaim-config: Configuring Lease Reclamation ============================= Kea can be configured to periodically detect and reclaim expired leases. During this process the lease entries in the database are modified or removed. While this is happening, the server will not process incoming DHCP messages to avoid issues with concurrent access to database information. As a result, the server will be unresponsive while lease reclamation is performed and DHCP queries will accumulate; responses will be sent once the lease-reclamation cycle is complete. In deployments where response time is critical, administrators may wish to minimize the interruptions in service caused by lease reclamation. To this end, Kea provides configuration parameters to control the frequency of lease reclamation cycles, the maximum number of leases processed in a single reclamation cycle, and the maximum amount of time a single reclamation cycle is allowed to run before being interrupted. The following examples demonstrate how these parameters can be used: :: "Dhcp4": { ... "expired-leases-processing": { "reclaim-timer-wait-time": 5, "max-reclaim-leases": 0, "max-reclaim-time": 0, }, ... } The first parameter is expressed in seconds and specifies an interval between the two consecutive lease reclamation cycles. This is explained by the following diagram: :: | c1 | | c2 | |c3| | c4 | |<---->|<---------->|<-->|<---------->|<>|<---------->|<-->|<-- ------------------------------------------------------------------> | | 5s | | 5s | | 5s | | time This diagram shows four lease-reclamation cycles (c1 through c4) of variable duration. Note that the duration of the reclamation cycle depends on the number of expired leases detected and processed in the particular cycle. This duration is usually significantly shorter than the interval between the cycles. According to the ``reclaim-timer-wait-time``, the server keeps fixed intervals of five seconds between the end of one cycle and the start of the next cycle. This guarantees the presence of 5-second-long periods during which the server remains responsive to DHCP queries and does not perform lease reclamation. The ``max-reclaim-leases`` and ``max-reclaim-time`` are set to 0, which sets no restriction on the maximum number of leases reclaimed in the particular cycle, or on the maximum duration of each cycle. In deployments with high lease-pool utilization, relatively short valid lifetimes, and frequently disconnecting clients which allow leases to expire, the number of expired leases requiring reclamation at any given time may rise significantly. In this case, it is often desirable to apply restrictions to the maximum duration of a reclamation cycle or the maximum number of leases reclaimed in a cycle. The following configuration demonstrates how this can be done: :: "Dhcp4": { ... "expired-leases-processing": { "reclaim-timer-wait-time": 3, "max-reclaim-leases": 100, "max-reclaim-time": 50, "unwarned-reclaim-cycles": 10, }, ... } The ``max-reclaim-leases`` parameter limits the number of leases reclaimed in a single cycle to 100. The ``max-reclaim-time`` limits the maximum duration of each cycle to 50ms. The lease-reclamation cycle will be interrupted if either of these limitations is reached. The reclamation of any unreclaimed leases will be attempted in subsequent cycles. The following diagram illustrates the behavior of the system in the presence of many expired leases, when the limits are applied for the reclamation cycles: :: | c1 | | c2 | | c3 | | c4 | |<-->|<-------------->|<-->|<-------------->|<-->|<-------------->|<-->|<-- ------------------------------------------------------------------------------> |50ms| 3s |50ms| 3s |50ms| 3s |50ms| time This diagram demonstrates the case when each reclamation cycle takes more than 50ms, and thus is interrupted according to the value of the ``max-reclaim-time``. This results in equal durations of all reclamation cycles over time. Note that in this example the limitation of the maximum 100 leases is not reached. This may be the case when database transactions or callouts in the hook libraries attached to the server are slow. Regardless, the chosen values for either the maximum number of leases or a maximum cycle time strongly depend on the particular deployment, the lease database backend being used, and any hooks libraries, etc. Administrators may need to experiment to tune the system to suit the dynamics of their deployment. It is important to realize that with the use of these limits, there is a risk that expired leases will accumulate faster than the server can reclaim them. This should not be a problem if the server is dealing with a temporary burst of expirations, because it should be able to eventually deal with them over time. However, if leases expire at a high rate for a long period of time, the unreclaimed leases will pile up in the database. To notify the administrator that the current configuration does not satisfy the needs for reclamation of expired leases, the server issues a warning message in the log if it is unable to reclaim all leases within several reclamation cycles. The number of cycles after which such a warning is issued is specified with the ``unwarned-reclaim-cycles`` configuration parameter. Setting the ``reclaim-timer-wait-time`` to 0 disables periodic reclamation of the expired leases. .. _lease-affinity: Configuring Lease Affinity ========================== Suppose that a laptop goes into sleep mode after a period of user inactivity. While the laptop is in sleep mode, its DHCP client will not renew leases obtained from the server and these leases will eventually expire. When the laptop wakes up, it is often desirable for it to continue using its previous assigned IP addresses. To facilitate this, the server needs to correlate returning clients with their expired leases. When the client returns, the server will first check for those leases and re-assign them if they have not been assigned to another client. The ability of the server to re-assign the same lease to a returning client is referred to as "lease affinity." When lease affinity is enabled (i.e. when ``hold-reclaimed-time`` is configured to a value greater than zero), the server will still reclaim leases according to the parameters described in :ref:`lease-reclaim-config`, but the reclaimed leases will be held in the database for a specified amount of time rather than removed. When the client returns, the server will first verify whether there are any reclaimed leases associated with this client and will re-assign them if possible. However, it is important to note that any reclaimed lease may be assigned to another client if that client specifically asks for it. Therefore, lease affinity does not guarantee that the reclaimed lease will be available for the client who used it before; it merely increases the chances of the client being assigned the same lease. If the lease pool is small - namely, in DHCPv4, for which address space is small - there is an increased likelihood that the expired lease will be assigned to another client. Consider the following configuration: :: "Dhcp4": { ... "expired-leases-processing": { "reclaim-timer-wait-time": 3, "hold-reclaimed-time": 1800, "flush-reclaimed-timer-wait-time": 5 }, ... } The ``hold-reclaim-time`` specifies how many seconds after an expiration a reclaimed lease should be held in the database for re-assignment to the same client. In the example given above, reclaimed leases will be held for 30 minutes (1800s) after their expiration. During this time, the server will likely be able to re-assign the same lease to the returning client, unless another client specifically requests this lease and the server assigns it. The server must periodically remove reclaimed leases for which the time indicated by ``hold-reclaim-time`` has elapsed. The ``flush-reclaimed-timer-wait-time`` parameter controls how often the server removes such leases. In the example provided above, the server will initiate removal of such leases five seconds after the previous removal attempt was completed. Setting this value to 0 disables lease affinity, meaning leases will be removed from the lease database when they are reclaimed. If lease affinity is enabled, it is recommended that ``hold-reclaim-time`` be set to a value significantly higher than the ``reclaim-timer-wait-time``, as timely removal of expired-reclaimed leases is less critical than the removal process, which may impact server responsiveness. There is no guarantee that lease affinity will work every time; if a server is running out of addresses, it will reassign expired addresses to new clients. Also, clients can request specific addresses and the server will try to honor such requests if possible. Administrators who want to ensure a client keeps its address, even after periods of inactivity, should consider using host reservations or leases with very long lifetimes. .. _leases-reclamation-using-command: Reclaiming Expired Leases via Command ===================================== The ``leases-reclaim`` command can be used to trigger lease reclamation at any time. Please consult the :ref:`command-leases-reclaim` section for details about using this command. kea-2.0.2/doc/sphinx/arm/hooks-ha.rst0000644000175000017500000031420114206773363014310 00000000000000.. _high-availability-library: ha: High Availability ===================== This section describes the High Availability hooks library, which can be loaded on a pair of DHCPv4 or DHCPv6 servers to increase the reliability of the DHCP service in the event of an outage of one of the servers. This library was previously only available to ISC's paid subscribers, but is now part of the open source Kea, available to all users. .. note:: This library may only be loaded by the ``kea-dhcp4`` or ``kea-dhcp6`` process. High Availability (HA) of the DHCP service is provided by running multiple cooperating server instances. If any of these instances becomes unavailable for any reason (DHCP software crash, Control Agent software crash, power outage, hardware failure), a surviving server instance can continue providing reliable service to clients. Many DHCP server implementations include the "DHCP Failover" protocol, whose most significant features are communication between the servers, partner failure detection, and lease synchronization between the servers. However, the DHCPv4 failover standardization process was never completed by the IETF. The DHCPv6 failover standard (RFC 8156) was published, but it is complex, difficult to use, has significant operational constraints, and is different than its v4 counterpart. Although it may be useful for some users to use a "standard" failover protocol, it seems that most Kea users are simply interested in a working solution which guarantees high availability of the DHCP service. Therefore, the Kea HA hook library derives major concepts from the DHCP Failover protocol but uses its own solutions for communication and configuration. It offers its own state machine, which greatly simplifies its implementation and generally fits better into Kea, and it provides the same features in both DHCPv4 and DHCPv6. This document intentionally uses the term "High Availability" rather than "Failover" to emphasize that it is not the Failover protocol implementation. The following sections describe the configuration and operation of the Kea HA hook library. .. _ha-supported-configurations: Supported Configurations ~~~~~~~~~~~~~~~~~~~~~~~~ The Kea HA hook library supports three configurations, also known as HA modes: load-balancing, hot-standby and passive-backup. In the load-balancing mode, two servers respond to DHCP requests. The load-balancing function is implemented as described in `RFC 3074 `__, with each server responding to half the received DHCP queries. When one of the servers allocates a lease for a client, it notifies the partner server over the control channel (the RESTful API), so the partner can save the lease information in its own database. If the communication with the partner is unsuccessful, the DHCP query is dropped and the response is not returned to the DHCP client. If the lease update is successful, the response is returned to the DHCP client by the server which has allocated the lease. By exchanging lease updates, both servers get a copy of all leases allocated by the entire HA setup, and either server can be switched to handle the entire DHCP traffic if its partner becomes unavailable. In the load-balancing configuration, one of the servers must be designated as ``"primary"`` and the other as ``"secondary."`` Functionally, there is no difference between the two during normal operation. This distinction is required when the two servers are started at (nearly) the same time and have to synchronize their lease databases. The primary server synchronizes the database first. The secondary server waits for the primary server to complete the lease database synchronization before it starts the synchronization. In the hot-standby configuration, one of the servers is designated as "primary" and the other as "standby." However, during normal operation, the primary server is the only one that responds to DHCP requests. The standby server receives lease updates from the primary over the control channel; however, it does not respond to any DHCP queries as long as the primary is running or, more accurately, until the standby considers the primary to be offline. If the standby server detects the failure of the primary, it starts responding to all DHCP queries. .. note:: Operators often wonder whether to use ``load-balancing`` or ``hot-standby`` mode. The ``load-balancing`` has the benefit of splitting the DHCP load between two instances, reducing the traffic processed by each of them. However, it is not always clear to the operators that using the ``load-balancing`` mode requires manually splitting the address pools between two Kea instances using client classification. It precludes two servers from allocating the same address to different clients. Such a split is not needed in the ``hot-standby`` mode. Thus, the benefit of using the ``hot-standby`` over the ``load-balancing`` mode is that the former has a simpler configuration. Conversely, ``load-balancing`` has higher performance potential at the cost of more complex configuration. See :ref:`ha-load-balancing-config` for details on how to split the pools using client classification. In the configurations described above, the primary and secondary/standby are referred to as ``"active"`` servers, because they receive lease updates and can automatically react to the partner's failures by responding to the DHCP queries which would normally be handled by the partner. The HA hook library supports another server type/role: ``"backup"`` server. The use of a backup server is optional, and can be implemented in both load-balancing and hot-standby setup, in addition to the active servers. There is no limit on the number of backup servers in the HA setup; however, the presence of backup servers may increase the latency of DHCP responses, because not only do active servers send lease updates to each other, but also to the backup servers. As of Kea 1.7.8 the active servers no longer expect the acknowledgments from the backup servers before responding to the DHCP clients, therefore the overhead of sending the lease updates to the backup servers is minimized compared to the earlier Kea versions. The last supported configuration, passive-backup, has been introduced in Kea release 1.7.8. In this configuration there is only one active server and typically one or more backup servers. A passive-backup configuration with no backup servers is also accepted but it is no different than running a single server with no HA function at all. The passive-backup configuration is used in situations when an administrator wants to take advantage of the backup servers as an additional storage for leases without a need for running the fully blown failover setup. In this case, if the primary server fails, the DHCP service is lost and it requires that the administrator manually starts the primary to resume the DHCP service. The administrator may also configure one of the backup servers to provide the DHCP service to the clients as these servers should have accurate or nearly accurate information about the allocated leases. The major advantage of the passive-backup mode is that it provides some redundancy of the lease information but with better performance of the primary server responding to the DHCP queries. Since Kea release 1.7.8, the primary server does not have to wait for the acknowledgments to the lease updates from the backup servers before it sends a response to the DHCP client. This reduces the response time comparing to the load-balancing and hot-standby cases in which the server responding to the DHCP query has to wait for the acknowledgment from the other active server before it can respond to the client. .. note:: An interesting use case for a single active server running in the passive-backup mode is a notification service in which a software pretending to be a backup server receives live notifications about allocated and deleted leases from the primary server and can display them on the monitoring screen, trigger alerts etc. Clocks on Active Servers ~~~~~~~~~~~~~~~~~~~~~~~~ Synchronized clocks are essential for the HA setup to operate reliably. The servers share lease information via lease updates and during synchronization of the databases. The lease information includes the time when the lease was allocated and when it expires. Some clock skew between the servers participating in the HA setup usually exists; this is acceptable as long as the clock skew is relatively low, compared to the lease lifetimes. However, if the clock skew becomes too high, the different lease expiration times on different servers may cause the HA system to malfunction. For example, one server may consider a lease to be expired when it is actually still valid. The lease reclamation process may remove a name associated with this lease from the DNS, causing problems when the client later attempts to renew the lease. Each active server monitors the clock skew by comparing its current time with the time returned by its partner in response to the heartbeat command. This gives a good approximation of the clock skew, although it doesn't take into account the time between sending the response by the partner and receiving this response by the server which sent the heartbeat command. If the clock skew exceeds 30 seconds, a warning log message is issued. The administrator may correct this problem by synchronizing the clocks (e.g. using NTP); the servers should notice the clock skew correction and stop issuing the warning. If the clock skew is not corrected and exceeds 60 seconds, the HA service on each of the servers is terminated, i.e. the state machine enters the ``terminated`` state. The servers will continue to respond to DHCP clients (as in the load-balancing or hot-standby mode), but will exchange neither lease updates nor heartbeats and their lease databases will diverge. In this case, the administrator should synchronize the clocks and restart the servers. .. note:: Prior to Kea 1.7.8 release, in order to recover from the terminated state, the administrator had to shutdown both servers and then start both of them. Since Kea 1.7.8 release it is allowed to restart the servers sequentially, i.e. restart one server and then restart another one. The clocks must be in sync before restarting the servers. .. note:: The clock skew is only assessed between two active servers and only the active servers may enter the terminated state if it is too high. As of Kea 1.7.8, the clock skew between the active and the backup servers is not assessed because the active servers do not exchange heartbeat messages with the backup servers. .. _ha-https-support: HTTPS Support ~~~~~~~~~~~~~ Since version 1.9.7 HTTPS is supported by the High Availability hooks library using the TLS/HTTPS support described in :ref:`tls` where more details can be found. The HTTPS configuration parameters are: - the ``trust-anchor`` parameter specifies the name of a file or directory where the certification authority (CA) certificate of a Control Agent can be found. - the ``cert-file`` parameter specifies the name of the file containing the end-entity certificate to use. - the ``key-file`` parameter specifies the private key of the end-entity certificate to use. These parameters can be configured at the global level and at the peer level. When configured at both levels the peer value is used allowing to share common values with possible exceptions. The three parameters must be either all not specified (HTTPS disabled) or all specified (HTTPS enabled). Configure to the empty string is considered as not specified: this can be used for instance to disable HTTPS for a particular peer when it is enabled at the global level. As the High Availability hooks library is a HTTPS client there is no ``cert-required`` parameter: it is configured at the Control Agent side. .. _ha-server-states: Server States ~~~~~~~~~~~~~ A DHCP server operating within an HA setup runs a state machine, and the state of the server can be retrieved by its peers using the ``ha-heartbeat`` command sent over the RESTful API. If the partner server doesn't respond to the ``ha-heartbeat`` command within the specified amount of time, the communication is considered interrupted and the server may, depending on the configuration, use additional measures (described later in this document) to verify that the partner is still operating. If it finds that the partner is not operating, the server transitions to the ``partner-down`` state to handle all the DHCP traffic directed to the system. In this case, the surviving server continues to send the ``ha-heartbeat`` command to detect when the partner wakes up. At that time, the partner synchronizes the lease database and when it is again ready to operate, the surviving server returns to normal operation, i.e. the ``load-balancing`` or ``hot-standby`` state. The following is the list of all possible server states: - ``backup`` - normal operation of the backup server. In this state it receives lease updates from the active servers. - ``communication-recovery`` - an active server running in load-balancing mode may transition to this state when it experiences communication issues with a partner server over the control channel. This is an intermediate state between the ``load-balancing`` and ``partner-down`` states. In this state the server continues to respond to DHCP queries but does not send lease updates to the partner. The lease updates are queued and will be sent when the communication is resumed. If the communication is not resumed the server may transition to the ``partner-down`` state. The ``communication-recovery`` state was introduced to ensure reliable DHCP service when both active servers remain operational but the communication between them is interrupted for a prolonged period of time. The server can be configured to never enter this state by setting the ``delayed-updates-limit`` to 0. Disabling entry into this state will cause the server to begin testing for and possibly enter ``partner-down`` state when the server is unable to communicate with its partner. - ``hot-standby`` - normal operation of the active server running in the hot-standby mode; both the primary and the standby server are in this state during their normal operation. The primary server responds to DHCP queries and sends lease updates to the standby server and to any backup servers that are present. - ``load-balancing`` - normal operation of the active server running in the load-balancing mode; both the primary and the secondary server are in this state during their normal operation. Both servers respond to DHCP queries and send lease updates to each other and to any backup servers that are present. - ``in-maintenance`` - an active server transitions to this state as a result of being notified by its partner that the administrator requested maintenance of the HA setup. The administrator requests the maintenance by sending the ``ha-maintenance-start`` to the server which is supposed to take over the responsibility for responding to the DHCP clients while the other server is taken offline for maintenance. If the server is in the ``in-maintenance`` state it can be safely shut down. The partner is in the ``partner-in-maintenance`` state from which it will transition to the ``partner-down`` state immediately after it finds that the server in maintenance was shut down. - ``partner-down`` - an active server transitions to this state after detecting that its partner (another active server) is offline. The server does not transition to this state if only a backup server is unavailable. In the ``partner-down`` state the active server responds to all DHCP queries, including those queries which are normally handled by the server that is now unavailable. - ``partner-in-maintenance`` - an active server transitions to this state after receiving a ``ha-maintenance-start`` command from the administrator. The server in this state becomes responsible for responding to all DHCP requests. The server sends a ``ha-maintenance-notify`` command to the partner, which should enter the ``in-maintenance`` state. The server remaining in the ``partner-in-maintenance`` state keeps sending lease updates to the partner until it finds that the partner has stopped responding to those lease updates, heartbeats, or any other commands. In this case, the server in the ``partner-in-maintenance`` state transitions to the ``partner-down`` state and keeps responding to the queries, but no longer sends lease updates. - ``passive-backup`` - a primary server running in the passive-backup HA mode transitions to this state immediately after it is booted up. The primary server being in this state responds to the entire DHCP traffic and sends lease updates to the backup servers it is connected to. By default, the primary server does not wait for the acknowledgments from the backup servers and responds to the DHCP query right after sending the lease updates to all backup servers. If any of the lease updates fail, a backup server misses the lease update but the DHCP client is still provisioned. This default configuration can be changed by setting the ``wait-backup-ack`` configuration parameter to ``true``, in which case the primary server always waits for the acknowledgements and drops the DHCP query if sending any of the corresponding lease updates fails. This improves lease database consistency between the primary and the secondary. However, if a communication failure between the active server and any of the backups occurs, it effectively causes the failure of the DHCP service from the DHCP clients' perspective. - ``ready`` - an active server transitions to this state after synchronizing its lease database with an active partner. This state indicates to the partner - which may be in the ``partner-down`` state - that it should return to normal operation. If and when it does, the server in the ``ready`` state will also start normal operation. - ``syncing`` - an active server transitions to this state to fetch leases from the active partner and update the local lease database. When in this state, the server issues the ``dhcp-disable`` command to disable the DHCP service of the partner from which the leases are fetched. The DHCP service is disabled for a maximum time of 60 seconds, after which it is automatically re-enabled, in case the syncing partner was unable to re-enable the service. If the synchronization is completed successfully, the synchronizing server issues the ``ha-sync-complete-notify`` command to notify the partner. In most states, the partner re-enables its DHCP service to continue responding to the DHCP queries. In the ``partner-down`` state, the partner first ensures that the communication between the servers is re-established before enabling the DHCP service. The syncing operation is synchronous; the server waits for an answer from the partner and does nothing else while the lease synchronization takes place. A server that is configured not to synchronize the lease database with its partner, i.e. when the ``sync-leases`` configuration parameter is set to ``false``, will never transition to this state. Instead, it will transition directly from the ``waiting`` state to the ``ready`` state. - ``terminated`` - an active server transitions to this state when the High Availability hooks library is unable to further provide reliable service and a manual intervention of the administrator is required to correct the problem. Various issues with the HA setup may cause the server to transition to this state. While in this state, the server continues responding to DHCP clients based on the HA mode selected (load-balancing or hot-standby), but the lease updates are not exchanged and the heartbeats are not sent. Once a server has entered the "terminated" state, it will remain in this state until it is restarted. The administrator must correct the issue which caused this situation prior to restarting the server (e.g. synchronize the clocks); otherwise, the server will return to the "terminated" state once it finds that the issue persists. - ``waiting`` - each started server instance enters this state. The backup server transitions directly from this state to the ``backup`` state. An active server sends a heartbeat to its partner to check its state; if the partner appears to be unavailable, the server transitions to the ``partner-down`` state. If the partner is available, the server transitions to the ``syncing`` or ``ready`` state, depending on the setting of the ``sync-leases`` configuration parameter. If both servers appear to be in the ``waiting`` state (concurrent startup), the primary server transitions to the next state first. The secondary or standby server remains in the ``waiting`` state until the primary transitions to the ``ready`` state. .. .. note:: Currently, restarting the HA service from the ``terminated`` state requires restarting the DHCP server or reloading its configuration. Whether the server responds to the DHCP queries and which queries it responds to is a matter of the server's state, if no administrative action is performed to configure the server otherwise. The following table provides the default behavior for various states. The ``DHCP Server Scopes`` denote what group of received DHCP queries the server responds to in the given state. An in-depth explanation of the scopes can be found below. .. table:: Default Behavior of the Server in Various HA States +------------------------+-----------------+-----------------+-----------------+ | State | Server Type | DHCP Service | DHCP Service | | | | | Scopes | +========================+=================+=================+=================+ | backup | backup server | disabled | none | +------------------------+-----------------+-----------------+-----------------+ | communication-recovery | primary or | enabled | ``HA_server1`` | | | secondary | | or | | | (load-balancing | | ``HA_server2`` | | | mode only) | | | +------------------------+-----------------+-----------------+-----------------+ | hot-standby | primary or | enabled | ``HA_server1`` | | | standby | | if primary, | | | (hot-standby | | none otherwise | | | mode) | | | +------------------------+-----------------+-----------------+-----------------+ | load-balancing | primary or | enabled | ``HA_server1`` | | | secondary | | or | | | (load-balancing | | ``HA_server2`` | | | mode) | | | +------------------------+-----------------+-----------------+-----------------+ | in-maintenance | active server | disabled | none | +------------------------+-----------------+-----------------+-----------------+ | partner-down | active server | enabled | all scopes | +------------------------+-----------------+-----------------+-----------------+ | partner-in-maintenance | active server | enabled | all scopes | +------------------------+-----------------+-----------------+-----------------+ | passive-backup | active server | enabled | all scopes | +------------------------+-----------------+-----------------+-----------------+ | ready | active server | disabled | none | +------------------------+-----------------+-----------------+-----------------+ | syncing | active server | disabled | none | +------------------------+-----------------+-----------------+-----------------+ | terminated | active server | enabled | same as in the | | | | | load-balancing | | | | | or hot-standby | | | | | state | +------------------------+-----------------+-----------------+-----------------+ | waiting | any server | disabled | none | +------------------------+-----------------+-----------------+-----------------+ The DHCP service scopes require some explanation. The HA configuration must specify a unique name for each server within the HA setup. This document uses the following convention within the provided examples: ``server1`` for a primary server, ``server2`` for the secondary or standby server, and ``server3`` for the backup server. In real life any names can be used as long as they remain unique. In the load-balancing mode there are two scopes specified for the active servers: ``HA_server1`` and ``HA_server2``. The DHCP queries load-balanced to ``server1`` belong to the ``HA_server1`` scope and the queries load-balanced to ``server2`` belong to the ``HA_server2`` scope. If either of the servers is in the ``partner-down`` state, the active partner is responsible for serving both scopes. In the hot-standby mode, there is only one scope - ``HA_server1`` - because only ``server1`` is responding to DHCP queries. If that server becomes unavailable, ``server2`` becomes responsible for this scope. The backup servers do not have their own scopes. In some cases they can be used to respond to queries belonging to the scopes of the active servers. Also, a server which is neither in the partner-down state nor in normal operation serves no scopes. The scope names can be used to associate pools, subnets, and networks with certain servers, so only these servers can allocate addresses or prefixes from those pools, subnets, or networks. This is done via the client classification mechanism (see :ref:`ha-load-balancing-advanced-config` for more details). .. _ha-scope-transition: Scope Transition in a Partner-Down Case ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When one of the servers finds that its partner is unavailable, it starts serving clients from both its own scope and the scope of the unavailable partner. This is straightforward for new clients, i.e. those sending DHCPDISCOVER (DHCPv4) or Solicit (DHCPv6), because those requests are not sent to any particular server. The available server will respond to all such queries when it is in the ``partner-down`` state. When a client renews a lease, it sends its DHCPREQUEST (DHCPv4) or Renew (DHCPv6) message directly to the server which has allocated the lease being renewed. If this server is no longer available, the client will get no response. In that case, the client continues to use its lease and attempts to renew until the rebind timer (T2) elapses. The client then enters the rebinding phase, in which it sends a DHCPREQUEST (DHCPv4) or Rebind (DHCPv6) message to any available server. The surviving server will receive the rebinding request and will typically extend the lifetime of the lease. The client then continues to contact that new server to renew its lease as appropriate. If and when the other server once again becomes available, both active servers will eventually transition to the ``load-balancing`` or ``hot-standby`` state, in which they will again be responsible for their own scopes. Some clients belonging to the scope of the restarted server will try to renew their leases via the surviving server, but this server will not respond to them anymore; the client will eventually transition back to the correct server via the rebinding mechanism. .. _ha-load-balancing-config: Load-Balancing Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following is the configuration snippet to enable high availability on the primary server within the load-balancing configuration. The same configuration should be applied on the secondary and backup servers, with the only difference that ``this-server-name`` should be set to ``server2`` and ``server3`` on those servers, respectively. .. note:: Remember! The ``load-balancing`` mode requires that the address pools and delegated prefix pools are split between the active servers. During normal operation, the servers use non-overlapping pools to avoid allocating the same lease to different clients by both instances. A server will only use the pool fragments owned by the partner when the partner is not running. See the notes in the :ref:`ha-supported-configurations` highlighting differences between the ``load-balancing`` and ``hot-standby`` modes. The semantics of the pool partitioning is explained further in this section. The :ref:`ha-load-balancing-advanced-config` provides advanced pools partitioning examples. :: "Dhcp4": { "hooks-libraries": [{ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so", "parameters": { } }, { "library": "/usr/lib/kea/hooks/libdhcp_ha.so", "parameters": { "high-availability": [{ "this-server-name": "server1", "mode": "load-balancing", "heartbeat-delay": 10000, "max-response-delay": 10000, "max-ack-delay": 5000, "max-unacked-clients": 5, "delayed-updates-limit": 100, "peers": [{ "name": "server1", "url": "http://192.168.56.33:8000/", "role": "primary", "auto-failover": true }, { "name": "server2", "url": "http://192.168.56.66:8000/", "role": "secondary", "auto-failover": true }, { "name": "server3", "url": "http://192.168.56.99:8000/", "role": "backup", "basic-auth-user": "foo", "basic-auth-password": "bar", "auto-failover": false }] }] } }], "subnet4": [{ "subnet": "192.0.3.0/24", "pools": [{ "pool": "192.0.3.100 - 192.0.3.150", "client-class": "HA_server1" }, { "pool": "192.0.3.200 - 192.0.3.250", "client-class": "HA_server2" }], "option-data": [{ "name": "routers", "data": "192.0.3.1" }], "relay": { "ip-address": "10.1.2.3" } }] } Two hooks libraries must be loaded to enable HA: ``libdhcp_lease_cmds.so`` and ``libdhcp_ha.so``. The latter implements the HA feature, while the former enables control commands required by HA to fetch and manipulate leases on the remote servers. In the example provided above, it is assumed that Kea libraries are installed in the ``/usr/lib`` directory. If Kea is not installed in the /usr directory, the hooks libraries locations must be updated accordingly. The HA configuration is specified within the scope of ``libdhcp_ha.so``. Note that the top-level parameter ``high-availability`` is a list, even though it currently contains only one entry. The following are the global parameters which control the server's behavior with respect to HA: - ``this-server-name`` - is a unique identifier of the server within this HA setup. It must match with one of the servers specified within the ``peers`` list. - ``mode`` - specifies an HA mode of operation. Currently supported modes are ``load-balancing`` and ``hot-standby``. - ``heartbeat-delay`` - specifies a duration in milliseconds between sending the last heartbeat (or other command sent to the partner) and the next heartbeat. The heartbeats are sent periodically to gather the status of the partner and to verify whether the partner is still operating. The default value of this parameter is 10000 ms. - ``max-response-delay`` - specifies a duration in milliseconds since the last successful communication with the partner, after which the server assumes that communication with the partner is interrupted. This duration should be greater than the ``heartbeat-delay``. Usually it is greater than the duration of multiple ``heartbeat-delay`` values. When the server detects that communication is interrupted, it may transition to the ``partner-down`` state (when ``max-unacked-clients`` is 0) or trigger the failure-detection procedure using the values of the two parameters below. The default value of this parameter is 60000 ms. - ``max-ack-delay`` - is one of the parameters controlling partner failure-detection. When communication with the partner is interrupted, the server examines the values of the ``secs`` field (DHCPv4) or ``Elapsed Time`` option (DHCPv6), which denote how long the DHCP client has been trying to communicate with the DHCP server. This parameter specifies the maximum time in milliseconds for the client to try to communicate with the DHCP server, after which this server assumes that the client failed to communicate with the DHCP server (is "unacked"). The default value of this parameter is 10000. - ``max-unacked-clients`` - specifies how many "unacked" clients are allowed (see ``max-ack-delay``) before this server assumes that the partner is offline and transitions to the ``partner-down`` state. The special value of 0 is allowed for this parameter, which disables the failure-detection mechanism. In this case, a server that can't communicate with its partner over the control channel assumes that the partner server is down and transitions to the ``partner-down`` state immediately. The default value of this parameter is 10. - ``delayed-updates-limit`` - specifies a maximum number of lease updates which can be queued while the server is in the ``communication-recovery`` state. This parameter was introduced in Kea release 1.9.4. The special value of 0 configures the server to never transition to the ``communication-recovery`` state and the server behaves as in earlier Kea versions. The default value of this parameter is 100. The values of ``max-ack-delay`` and ``max-unacked-clients`` must be selected carefully, taking into account the specifics of the network in which the DHCP servers are operating. Note that the server in question may not respond to some DHCP clients because these clients are not to be serviced by this server according to administrative policy. The server may also drop malformed queries from clients. Therefore, selecting too low a value for the ``max-unacked-clients`` parameter may result in a transition to the ``partner-down`` state even though the partner is still operating. On the other hand, selecting too high a value may result in never transitioning to the ``partner-down`` state if the DHCP traffic in the network is very low (e.g. at night), because the number of distinct clients trying to communicate with the server could be lower than the ``max-unacked-clients`` setting. In some cases it may be useful to disable the failure-detection mechanism altogether, if the servers are located very close to each other and network partitioning is unlikely, i.e. failure to respond to heartbeats is only possible when the partner is offline. In such cases, set the ``max-unacked-clients`` to 0. The ``delayed-updates-limit`` parameter was introduced in Kea 1.9.4. It is used to enable or disable the use of the communication recovery procedure and controls server's behavior in the ``communication-recovery`` state which was introduced in the same release. This parameter may only be used in the load balancing mode. .. note:: In Kea 1.9.4, with the introduction of the ``delayed-updates-limit``, the default server's behavior has changed when the server runs in the load balancing mode. When the server experiences communication issues with its partner, it enters ``communication-recovery`` state and queues lease updates until communication is resumed. Before Kea 1.9.4 the server would begin the failover procedure in the ``load-balancing`` state and possibly transition straight to the ``partner-down`` state when communication is not resumed. If the server is in the ``load-balancing`` state and it experiences communication issues with its partner (heartbeat or lease update fail), the server transitions to the ``communication-recovery`` state. In this state the server keeps responding to DHCP queries but it does not send lease updates to the partner. The lease updates are queued until the communication is re-established. This ensures that the DHCP service remains available even in the event of the communication loss between the partners. Note that the communication loss may appear both when one of the servers terminated or when both servers remain available but can't communicate. In the former case, the surviving server will follow the normal failover procedure and should eventually transition to the ``partner-down`` state. In the latter case both servers should transition to the ``communication-recovery`` state and should never transition to the ``partner-down`` state (if ``max-unacked-clients`` is set to non zero value), because all DHCP queries are responded and servers would not see any unacked DHCP queries. Introduction of the communication recovery procedure was mostly motivated by issues which may appear when two servers remain online but the communication between them remains interrupted for a long period of time. In earlier Kea versions, the servers having communication issues used to drop DHCP packets before transitioning to the ``partner-down`` state. In some cases they both transitioned to the ``partner-down`` state which could potentially result in allocations of the same IP addresses or delegated prefixes to different clients by respective servers. By entering the intermediate ``communication-recovery`` state these problems are avoided. If a server in the ``communication-recovery`` state re-establishes communication with its partner, it will try to send the partner all of the outstanding lease updates the server has queued. This is done synchronously and may take a considerable amount of time before the server transitions to the ``load-balancing`` state and resumes normal operation. The maximum number of lease updates which can be queued in the ``communication-recovery`` state is controlled by the ``delayed-updates-limit``. If the limit is exceeded, the server stops queuing lease updates and will perform full database synchronization after re-establishing the connection with the partner instead of sending outstanding lease updates before transitioning to ``load-balancing`` state. Even if the limit is exceeded, the server in the ``communication-recovery`` state remains responsive to the DHCP clients. It may be preferable to set higher values of ``delayed-updates-limit`` when there is a risk of prolonged communication interruption between the servers and the lease database is large. This would avoid costly lease database synchronization. On the other hand, if the lease database is small the time required to send outstanding lease updates may be longer than lease database synchronization. In such cases it may be better to use a lower value, e.g. 10. The default value is 100 which seems to be a reasonable compromise and should work well in most deployments with moderate traffic. .. note:: This parameter is new and values for it that work well in some environments may not work well in others. Feedback from users will help us build a better working set of recommendations. The ``peers`` parameter contains a list of servers within this HA setup. This configuration must contain at least one primary and one secondary server. It may also contain an unlimited number of backup servers. In this example, there is one backup server which receives lease updates from the active servers. Since Kea version 1.9.0 the basic HTTP authentication is available to protect the Kea control agent against local attackers. These are the parameters specified for each of the peers within this list: - ``name`` - specifies a unique name for the server. - ``url`` - specifies the URL to be used to contact this server over the control channel. Other servers use this URL to send control commands to that server. - ``basic-auth-user`` - specifies the user id for basic HTTP authentication. If not specified or specified as an empty string no authentication header will be added to HTTP transactions. Must not contain the colon (:) character. - ``basic-auth-password`` - specifies the password for basic HTTP authentication. Ignored when the user id is not specified or empty. The password is optional: if not specified an empty password is used. - ``role`` - denotes the role of the server in the HA setup. The following roles are supported in the load-balancing configuration: ``primary``, ``secondary``, and ``backup``. There must be exactly one primary and one secondary server in the load-balancing setup. - ``auto-failover`` - a boolean value which denotes whether a server detecting a partner's failure should automatically start serving the partner's clients. The default value of this parameter is true. In our example configuration, both active servers can allocate leases from the subnet "192.0.3.0/24". This subnet contains two address pools: "192.0.3.100 - 192.0.3.150" and "192.0.3.200 - 192.0.3.250", which are associated with HA server scopes using client classification. When ``server1`` processes a DHCP query, it uses the first pool for lease allocation. Conversely, when ``server2`` processes a DHCP query it uses the second pool. When either of the servers is in the ``partner-down`` state, it can serve leases from both pools and it selects the pool which is appropriate for the received query. In other words, if the query would normally be processed by ``server2`` but this server is not available, ``server1`` will allocate the lease from the pool of "192.0.3.200 - 192.0.3.250". The Kea control agent in front of the ``server3`` requires basic HTTP authentication and authorizes the user id "foo" with the password "bar". .. note:: The ``url`` schema can be ``http`` or ``https`` but since Kea version 1.9.6 the ``https`` schema requires a TLS setup which should be implemented for Kea version 1.9.7. The hostname part must be an IPv4 address or an IPv6 address between square brackets, e.g. ``http://[2001:db8::1]:8080/``. Names are not accepted. .. _ha-load-balancing-advanced-config: Load Balancing with Advanced Classification ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In the previous section, we provided an example of a load-balancing configuration with client classification limited to the ``HA_server1`` and ``HA_server2`` classes, which are dynamically assigned to the received DHCP queries. In many cases, HA will be needed in deployments which already use some other client classification. Suppose there is a system which classifies devices into two groups: phones and laptops, based on some classification criteria specified in the Kea configuration file. Both types of devices are allocated leases from different address pools. Introducing HA in the load-balancing mode results in a further split of each of those pools, as each server allocates leases for some phones and some laptops. This requires each of the existing pools to be split between ``HA_server1`` and ``HA_server2``, so we end up with the following classes: - phones_server1 - laptops_server1 - phones_server2 - laptops_server2 The corresponding server configuration using advanced classification (and the ``member`` expression) is provided below. For brevity's sake, the HA hook library configuration has been removed from this example. :: "Dhcp4": { "client-classes": [{ "name": "phones", "test": "substring(option[60].hex,0,6) == 'Aastra'", }, { "name": "laptops", "test": "not member('phones')" }, { "name": "phones_server1", "test": "member('phones') and member('HA_server1')" }, { "name": "phones_server2", "test": "member('phones') and member('HA_server2')" }, { "name": "laptops_server1", "test": "member('laptops') and member('HA_server1')" }, { "name": "laptops_server2", "test": "member('laptops') and member('HA_server2')" }], "hooks-libraries": [{ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so", "parameters": { } }, { "library": "/usr/lib/kea/hooks/libdhcp_ha.so", "parameters": { "high-availability": [{ ... }] } }], "subnet4": [{ "subnet": "192.0.3.0/24", "pools": [{ "pool": "192.0.3.100 - 192.0.3.125", "client-class": "phones_server1" }, { "pool": "192.0.3.126 - 192.0.3.150", "client-class": "laptops_server1" }, { "pool": "192.0.3.200 - 192.0.3.225", "client-class": "phones_server2" }, { "pool": "192.0.3.226 - 192.0.3.250", "client-class": "laptops_server2" }], "option-data": [{ "name": "routers", "data": "192.0.3.1" }], "relay": { "ip-address": "10.1.2.3" } }], } The configuration provided above splits the address range into four pools: two pools dedicated to server1 and two to server2. Each server can assign leases to both phones and laptops. Both groups of devices are assigned addresses from different pools. The ``HA_server1`` and ``HA_server2`` classes are built-in (see :ref:`classification-using-vendor`) and do not need to be declared. They are assigned dynamically by the HA hook library as a result of the load-balancing algorithm. ``phones_*`` and ``laptop_*`` evaluate to "true" when the query belongs to a given combination of other classes, e.g. ``HA_server1`` and ``phones``. The pool is selected accordingly as a result of such an evaluation. Consult :ref:`classify` for details on how to use the ``member`` expression and class dependencies. .. _ha-hot-standby-config: Hot-Standby Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~ The following is an example configuration of the primary server in the hot-standby configuration: :: "Dhcp4": { "hooks-libraries": [{ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so", "parameters": { } }, { "library": "/usr/lib/kea/hooks/libdhcp_ha.so", "parameters": { "high-availability": [{ "this-server-name": "server1", "mode": "hot-standby", "heartbeat-delay": 10000, "max-response-delay": 10000, "max-ack-delay": 5000, "max-unacked-clients": 5, "peers": [{ "name": "server1", "url": "http://192.168.56.33:8000/", "role": "primary", "auto-failover": true }, { "name": "server2", "url": "http://192.168.56.66:8000/", "role": "standby", "auto-failover": true }, { "name": "server3", "url": "http://192.168.56.99:8000/", "basic-auth-user": "foo", "basic-auth-password": "bar", "role": "backup", "auto-failover": false }] }] } }], "subnet4": [{ "subnet": "192.0.3.0/24", "pools": [{ "pool": "192.0.3.100 - 192.0.3.250", "client-class": "HA_server1" }], "option-data": [{ "name": "routers", "data": "192.0.3.1" }], "relay": { "ip-address": "10.1.2.3" } }] } This configuration is very similar to the load-balancing configuration described in :ref:`ha-load-balancing-config`, with a few notable differences. The ``mode`` is now set to ``hot-standby``, in which only one server responds to DHCP clients. If the primary server is online, it responds to all DHCP queries. The ``standby`` server takes over all DHCP traffic if it discovers that the primary is unavailable. In this mode, the non-primary active server is called ``standby`` and that is its role. Finally, because there is always one server responding to DHCP queries, there is only one scope - ``HA_server1`` - in use within pool definitions. In fact, the ``client-class`` parameter could be removed from this configuration without harm, because there can be no conflicts in lease allocations by different servers as they do not allocate leases concurrently. The ``client-class`` remains in this example mostly for demonstration purposes, to highlight the differences between the hot-standby and load-balancing modes of operation. .. _ha-passive-backup-config: Passive-Backup Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following is an example configuration of the primary server in the passive-backup configuration: :: "Dhcp4": { "hooks-libraries": [{ "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so", "parameters": { } }, { "library": "/usr/lib/kea/hooks/libdhcp_ha.so", "parameters": { "high-availability": [{ "this-server-name": "server1", "mode": "passive-backup", "wait-backup-ack": false, "peers": [{ "name": "server1", "url": "http://192.168.56.33:8000/", "role": "primary" }, { "name": "server2", "url": "http://192.168.56.66:8000/", "role": "backup" }, { "name": "server3", "url": "http://192.168.56.99:8000/", "basic-auth-user": "foo", "basic-auth-password": "bar", "role": "backup" }] }] } }], "subnet4": [{ "subnet": "192.0.3.0/24", "pools": [{ "pool": "192.0.3.100 - 192.0.3.250", }], "option-data": [{ "name": "routers", "data": "192.0.3.1" }], "relay": { "ip-address": "10.1.2.3" } }] } The configurations of three peers are included, one for the primary and two for the backup servers. Many of the parameters present in the load-balancing and hot-standby configuration examples are not relevant in the passive-backup mode, thus they are not specified here. For example: ``heartbeat-delay``, ``max-unacked-clients`` and others related to the automatic failover mechanism should not be specified in the passive-backup mode. The ``wait-backup-ack`` is a boolean parameter not present in previous examples. It defaults to ``false`` and must not be modified in the load-balancing and hot-standby modes. In the passive-backup mode this parameter can be set to ``true``, which causes the primary server to expect acknowledgments to the lease updates from the backup servers prior to responding to the DHCP client. It ensures that the lease has propagated to all servers before the client is given the lease, but it poses a risk of losing a DHCP service if there is a communication problem with one of the backup servers. This setting also increases the latency of the DHCP response, because of the time that the primary spends waiting for the acknowledgements. We recommend that the ``wait-backup-ack`` setting be left at its default value, if the DHCP service reliability is more important than consistency of the lease information between the primary and the backups, and in all cases when the DHCP service latency should be minimal. .. note:: Currently, active servers place lease updates to be sent to peers onto internal queues (one queue per peer/URL). In passive-backup mode, active servers do not wait for lease udpates to be acknowledged thus during times of heavy client traffic it is possible for the number of lease updates queued for transimission to accumulate faster than they can be delivered. As client traffic lessens the queues begin to empty. As of Kea 2.0.0, active servers monitor the size of these queues and will emit periodic warnings (see HTTP_CILENT_QUEUE_SIZE_GROWING in :ref:`kea-messages`) if they perceive a queue as growing too quickly. The warnings will cease once the queue size begins to shrink. These messages are intended as a bell-weather and seeing them sporadically during times of heavy traffic load does not necessarily indicate a problem. If, however, they occur continually during times of routine traffic load they likely indicate potential mismatches in server capibilities and/or configuration and this should be investigated as the size of the queues may eventually impair an active server's ability to respond to clients in a timely manner. .. _ha-sharing-lease-info: Lease Information Sharing ~~~~~~~~~~~~~~~~~~~~~~~~~ An HA-enabled server informs its active partner about allocated or renewed leases by sending appropriate control commands, and the partner updates the lease information in its own database. When the server starts up for the first time or recovers after a failure, it synchronizes its lease database with its partner. These two mechanisms guarantee consistency of the lease information between the servers and allow the designation of one of the servers to handle the entire DHCP traffic load if the other server becomes unavailable. In some cases, though, it is desirable to disable lease updates and/or database synchronization between the active servers, if the exchange of information about the allocated leases is performed using some other mechanism. Kea supports various database types that can be used to store leases, including MySQL, PostgreSQL, and Cassandra. Those databases include built-in solutions for data replication which are often used by Kea administrators to provide redundancy. The HA hook library supports such scenarios by disabling lease updates over the control channel and/or lease database synchronization, leaving the server to rely on the database replication mechanism. This is controlled by the two boolean parameters ``send-lease-updates`` and ``sync-leases``, whose values default to true: :: { "Dhcp4": { ... "hooks-libraries": [ { "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so", "parameters": { } }, { "library": "/usr/lib/kea/hooks/libdhcp_ha.so", "parameters": { "high-availability": [ { "this-server-name": "server1", "mode": "load-balancing", "send-lease-updates": false, "sync-leases": false, "peers": [ { "name": "server1", "url": "http://192.168.56.33:8000/", "role": "primary" }, { "name": "server2", "url": "http://192.168.56.66:8000/", "role": "secondary" } ] } ] } } ], ... } In the most typical use case, both parameters are set to the same value, i.e. both are ``false`` if database replication is in use, or both are ``true`` otherwise. Introducing two separate parameters to control lease updates and lease-database synchronization is aimed at possible special use cases; for example, when synchronization is performed by copying a lease file (therefore ``sync-leases`` is set to ``false``), but lease updates should be conducted as usual (``send-lease-updates`` is set to ``true``). It should be noted that Kea does not natively support such use cases, but users may develop their own scripts and tools around Kea to provide such mechanisms. The HA hooks library configuration is designed to maximize flexibility of administration. .. _ha-syncing-page-limit: Controlling Lease-Page Size Limit ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ An HA-enabled server initiates synchronization of the lease database after downtime or upon receiving the ``ha-sync`` command. The server uses commands described in :ref:`command-lease4-get-page` and :ref:`command-lease6-get-page` to fetch leases from its partner server (lease queries). The size of the results page (the maximum number of leases to be returned in a single response to one of these commands) can be controlled via configuration of the HA hooks library. Increasing the page size decreases the number of lease queries sent to the partner server, but it causes the partner server to generate larger responses, which lengthens transmission time as well as increases memory and CPU utilization on both servers. Decreasing the page size helps to decrease resource utilization, but requires more lease queries to be issued to fetch the entire lease database. The default value of the ``sync-page-limit`` command controlling the page size is 10000. This means that the entire lease database can be fetched with a single command if the size of the database is equal to or less than 10000 lines. .. _ha-syncing-timeouts: Timeouts ~~~~~~~~ In deployments with a large number of clients connected to the network, lease-database synchronization after a server failure may be a time-consuming operation. The synchronizing server must gather all leases from its partner, which yields a large response over the RESTful interface. The server receives leases using the paging mechanism described in :ref:`ha-syncing-page-limit`. Before the page of leases is fetched, the synchronizing server sends a ``dhcp-disable`` command to disable the DHCP service on the partner server. If the service is already disabled, this command will reset the timeout for the DHCP service being disabled. This timeout value is by default set to 60 seconds. If fetching a single page of leases takes longer than the specified time, the partner server will assume that the synchronizing server died and will resume its DHCP service. The connection of the synchronizing server with its partner is also protected by the timeout. If the synchronization of a single page of leases takes longer than the specified time, the synchronizing server terminates the connection and the synchronization fails. Both timeout values are controlled by a single configuration parameter, ``sync-timeout``. The following configuration snippet demonstrates how to modify the timeout for automatic re-enabling of the DHCP service on the partner server and how to increase the timeout for fetching a single page of leases from 60 seconds to 90 seconds: :: { "Dhcp4": { ... "hooks-libraries": [ { "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so", "parameters": { } }, { "library": "/usr/lib/kea/hooks/libdhcp_ha.so", "parameters": { "high-availability": [ { "this-server-name": "server1", "mode": "load-balancing", "sync-timeout": 90000, "peers": [ { "name": "server1", "url": "http://192.168.56.33:8000/", "role": "primary" }, { "name": "server2", "url": "http://192.168.56.66:8000/", "role": "secondary" } ] } ] } } ], ... } It is important to note that extending this ``sync-timeout`` value may sometimes be insufficient to prevent issues with timeouts during lease-database synchronization. The control commands travel via the Control Agent, which also monitors incoming (with a synchronizing server) and outgoing (with a DHCP server) connections for timeouts. The DHCP server also monitors the connection from the Control Agent for timeouts. Those timeouts cannot currently be modified via configuration; extending these timeouts is only possible by modifying them in the Kea code and recompiling the server. The relevant constants are located in the Kea source at: ``src/lib/config/timeouts.h``. .. _ha-pause-state-machine: Pausing the HA State Machine ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The high-availability state machine includes many different states described in detail in :ref:`ha-server-states`. The server enters each state when certain conditions are met, most often taking into account the partner server's state. In some states the server performs specific actions, e.g. synchronization of the lease database in the ``syncing`` state or responding to DHCP queries according to the configured mode of operation in the ``load-balancing`` and ``hot-standby`` states. By default, transitions between the states are performed automatically and the server administrator has no direct control when the transitions take place; in most cases, the administrator does not need such control. In some situations, however, the administrator may want to "pause" the HA state machine in a selected state to perform some additional administrative actions before the server transitions to the next state. Consider a server failure which results in the loss of the entire lease database. Typically, the server will rebuild its lease database when it enters the ``syncing`` state by querying the partner server for leases, but it is possible that the partner was also experiencing a failure and lacks lease information. In this case, it may be required to reconstruct lease databases on both servers from some external source, e.g. a backup server. If the lease database is to be reconstructed via the RESTful API, the servers should be started in the initial, i.e. ``waiting``, state and remain in this state while leases are being added. In particular, the servers should not attempt to synchronize their lease databases nor start serving DHCP clients. The HA hooks library provides configuration parameters and a command to control when the HA state machine should be paused and resumed. The following configuration causes the HA state machine to pause in the ``waiting`` state after server startup. :: "Dhcp4": { ... "hooks-libraries": [ { "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so", "parameters": { } }, { "library": "/usr/lib/kea/hooks/libdhcp_ha.so", "parameters": { "high-availability": [ { "this-server-name": "server1", "mode": "load-balancing", "peers": [ { "name": "server1", "url": "http://192.168.56.33:8000/", "role": "primary" }, { "name": "server2", "url": "http://192.168.56.66:8000/", "role": "secondary" } ], "state-machine": { "states": [ { "state": "waiting", "pause": "once" } ] } } ] } } ], ... } The ``pause`` parameter value ``once`` denotes that the state machine should be paused upon the first transition to the ``waiting`` state; later transitions to this state will not cause the state machine to pause. Two other supported values of the ``pause`` parameter are ``always`` and ``never``. The latter is the default value for each state, which instructs the server never to pause the state machine. In order to "unpause" the state machine, the ``ha-continue`` command must be sent to the paused server. This command does not take any arguments. See :ref:`ha-control-commands` for details about commands specific to the HA hooks library. It is possible to configure the state machine to pause in more than one state. Consider the following configuration: :: "Dhcp4": { ... "hooks-libraries": [ { "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so", "parameters": { } }, { "library": "/usr/lib/kea/hooks/libdhcp_ha.so", "parameters": { "high-availability": [ { "this-server-name": "server1", "mode": "load-balancing", "peers": [ { "name": "server1", "url": "http://192.168.56.33:8000/", "role": "primary" }, { "name": "server2", "url": "http://192.168.56.66:8000/", "role": "secondary" } ], "state-machine": { "states": [ { "state": "ready", "pause": "always" }, { "state": "partner-down", "pause": "once" } ] } } ] } } ], ... } This configuration instructs the server to pause the state machine every time it transitions to the ``ready`` state and upon the first transition to the ``partner-down`` state. Refer to :ref:`ha-server-states` for a complete list of server states. The state machine can be paused in any of the supported states; however, it is not practical for the ``backup`` and ``terminated`` states because the server never transitions out of these states anyway. .. note:: In the ``syncing`` state the server is paused before it makes an attempt to synchronize the lease database with a partner. To pause the state machine after lease-database synchronization, use the ``ready`` state instead. .. .. note:: The state of the HA state machine depends on the state of the cooperating server. Therefore, it must be taken into account that pausing the state machine of one server may affect the operation of the partner server. For example: if the primary server is paused in the ``waiting`` state, the partner server will also remain in the ``waiting`` state until the state machine of the primary server is resumed and that server transitions to the ``ready`` state. .. _ha-ctrl-agent-config: Control Agent Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~ :ref:`kea-ctrl-agent` describes in detail the Kea daemon, which provides a RESTful interface to control the Kea servers. The same functionality is used by the High Availability hooks library to establish communication between the HA peers. Therefore, the HA library requires that the Control Agent (CA) be started for each DHCP instance within the HA setup. If the Control Agent is not started, the peers will not be able to communicate with the particular DHCP server (even if the DHCP server itself is online) and may eventually consider this server to be offline. The following is an example configuration for the CA running on the same machine as the primary server. This configuration is valid for both the load-balancing and the hot-standby cases presented in previous sections. :: { "Control-agent": { "http-host": "192.168.56.33", // If enabling HA and multi-threading, the 8000 port is used by the HA // hook library http listener. When using HA hook library with // multi-threading to function, make sure the port used by dedicated // listener is different (e.g. 8001) than the one used by CA. Note // the commands should still be sent via CA. The dedicated listener // is specifically for HA updates only. "http-port": 8000, "control-sockets": { "dhcp4": { "socket-type": "unix", "socket-name": "/tmp/kea-dhcp4-ctrl.sock" }, "dhcp6": { "socket-type": "unix", "socket-name": "/tmp/kea-dhcp6-ctrl.sock" } } } } Since Kea version 1.9.0 the basic HTTP authentication is supported. .. _ha-mt-config: Multi-threaded Configuration (HA+MT) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HA peer communication consists of specialized API commands sent between HA peers. Prior to Kea 1.9.7, each peer must be paired with a local instance of kea-ctrl-agent in order to exchange commands. The agent receives HA commands via HTTP, communicates via Linux socket with the local peer to carry out the command, and then sends the response back to the requesting peer via HTTP. To send HA commands, each peer opens its own HTTP client connection to the URL of each of its peers. As of Kea 1.9.7, it is possible to configure HA to use direct multi- threaded communication between peers. We refer to this mode as HA+MT. With HA+MT enabled each peer runs its own dedicated, internal HTTP listener (i.e. server) which receives and responds to commands directly, thus eliminating the need for an agent to carry out HA protocol between peers. In addition, both the listener and client components use multi- threading to support multiple, concurrent connections between peers. By eliminating the agent and executing multiple command exchanges in parallel, HA throughput between peers should improve considerably in most situations. The following parameters have been added to HA configuration, to support HA+MT operation: - ``enable-multi-threading`` - enables or disables multi-threading HA peer communication (HA+MT). Please note that Kea core multi-threading must be enabled in order for HA+MT to operate. When false (the default) the server will operate as before, relying on kea-ctrl-agent and using single-threaded HTTP client processing. - ``http-dedicated-listener`` - enables or disables the creation of a dedicated, internal HTTP listener through which the server receives HA messages from its peers. The internal listener replaces the role of kea-ctrl-agent traffic, allowing peers to send their HA commands directly to each other. The listener will listen on the peer's ``url``. When false (the default) the server will rely on kea-ctrl-agent. This parameter has been provided largely for flexibility and testing, running HA+MT without dedicated listeners enabled will substantially limit HA throughput. - ``http-listener-threads`` - maximum number of threads the dedicated listener should use. A value 0 instructs the server to use the same number of threads as Kea core is using for DHCP multi-threading. Defaults to 0. - ``http-client-threads`` - maximum number of threads that should be used to send HA messages to its peers. A value 0 instructs the server to use the same number of threads as Kea core is using for DHCP multi-threading. Defaults to 0. They are grouped together under a map element, ``multi-threading`` as illustrated below: :: "Dhcp4": { ... "hooks-libraries": [ { "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so", "parameters": { } }, { "library": "/usr/lib/kea/hooks/libdhcp_ha.so", "parameters": { "high-availability": [ { "this-server-name": "server1", ... "multi-threading": { "enable-multi-threading": true, "http-dedicated-listener": true, "http-listener-threads": 4, "http-client-threads": 4 }, ... "peers": [ // This is the configuration of this server instance. { "name": "server1", // This specifies the URL of our server instance. Since the // HA+MT uses direct connection, the DHCPv4 server open its own // socket. Note it must be different than the one used by the // CA (typically 8000). In this example, 8001 is used. "url": "http://192.0.2.1:8001/", // This server is primary. The other one must be secondary. "role": "primary" }, // This is the configuration of our HA peer. { "name": "server2", // This specifies the URL of our server instance. Since the // HA+MT uses direct connection, the DHCPv4 server open its own // socket. Note it must be different than the one used by the // CA (typically 8000). In this example, 8001 is used. "url": "http://192.0.2.2:8001/", // The partner is a secondary. Our is primary. "role": "secondary" } ... In the example above, HA+MT is enabled with four threads for the listener and four threads for the client. .. note:: It is essential to configure the ports correctly. One common mistake that is easy to miss is to configure CA to listen on port 8000 and configure dedicated listeners also to port 8000. In such configuration, the DHCP server will fail to bind sockets, but the communication will still work via CA, albeit slowly. Make sure your dedicated listeners use a different port (8001 is a suggested alternative). If you misconfigure ports or use the ports used by CA, the performance bottlenecks caused by single threaded nature of CA and the sequential nature of UNIX socket that connects CA to DHCP servers will nullify any performance gains offered by HA+MT. .. _ha-parked-packet-limit: Parked Packet Limit ~~~~~~~~~~~~~~~~~~~ Kea servers contain a mechanism by which the response to a client packet may be held, pending completion of hook library work. We refer to this as "parking" the packet. The HA hook library makes use of this mechanism. When an HA server needs to send a lease update to its peer(s) to notify it of the change to the lease, it will "park" the client response until the peer acknowledges the lease update. At that point, the server will "unpark" the response and send it to the client. This applies to client queries which cause lease changes such as DHCPREQUEST for DHCPv4 and REQUEST, RENEW, REBIND for DHCPv6. It does not apply to DHPCDISCOVERs (v4) or SOLICITs (v6). There is a global parameter, ``parked-packet-limit``, that may be used to limit the number of responses that may be parked at any given time. This acts as a form of congestion handling and protects the server from being swamped when the volume of client queries is outpacing the server's ability to respond. Once the limit is reached, the server will emit a log and drop any new responses until parking spaces are available. In general, smaller values for the parking lot limit are likely to cause more drops but with shorter response times. Larger values are likely to result in fewer drops but with longer response times. Currently, the default value for parked-packet-limit is 256. .. warning:: Using too small of a value may result in an unnecessarily high drop rate, while using too large of a value may lead to responses times that are simply too long to be useful. A value of 0, while allowed, disables the limit altogether but this is highly discouraged as it may lead to Kea servers becoming unresponsive to clients. Choosing the best value is very site specific so we recommend you leave it at the default value of 256 and observe how your system behaves over time with varying load conditions. :: "Dhcp6": { ... // Limit the number of concurrently parked packets to 128. "parked-packet-limit": 128, "hooks-libraries": [ { "library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so", "parameters": { } }, { "library": "/usr/lib/kea/hooks/libdhcp_ha.so", "parameters": { "high-availability": [ { "this-server-name": "server1", ... .. note:: While parked-packet-limit is not specifically tied to HA, currently HA is the only ISC hook that employs packet parking. .. _ha-maintenance: Controlled Shutdown and Maintenance of DHCP servers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Having a pair of servers providing High Availability allows for controlled shutdown and maintenance of those servers without disrupting the DHCP service. For example, an administrator can perform an upgrade of one of the servers while the other one continues to respond to the DHCP queries. When the upgraded server is back online, the upgrade can be performed for the second server. The typical problem reported for the earlier versions of the High Availability hooks library was that the administrator did not have direct control over the state of the DHCP server. Shutting down one of the servers for maintenance did not necessarily cause the other server to start responding to all DHCP queries, because the failure detection algorithm described in :ref:`ha-scope-transition` requires that the partner not respond for a configured period of time and, depending on the configuration, may also require that a number of DHCP requests are not responded to for a configured period of time. The maintenance procedure, however, requires that the administrator be able to instruct one of the servers to instantly start serving all DHCP clients, and the other server to instantly stop serving any DHCP clients, so it can be safely shut down. The maintenance feature of the High Availability hooks library addresses this problem. The ``ha-maintenance-start`` command was introduced to allow the administrator to put the pair of the active servers in states in which one of them is responding to all DHCP queries and the other one is awaiting a shutdown. Suppose that the HA setup includes two active servers, e.g. ``server1`` and ``server2`` and the latter needs to be shut down for maintenance. The administrator should send the ``ha-maintenance-start`` to server1, as this is the server which is going to handle the DHCP traffic while the other one is offline. The server1 may respond with an error if its state or the partner's state does not allow for the maintenance. For example, the maintenance is not supported for the backup server or the server being in the terminated state. Also, an error will be returned if the maintenance request was already sent to the other server. Upon receiving the ``ha-maintenance-start`` command, server1 will send the ``ha-maintenance-notify`` command to server2 to put this server in the ``in-maintenance`` state. If server2 confirms, server1 will transition to the ``partner-in-maintenance`` state. This is similar to the ``partner-down`` state, except that in the ``partner-in-maintenance`` state server1 continues to send lease updates to server2 until the administrator shuts down server2. Server1 now responds to all DHCP queries. The administrator may safely shut down server2 it being in the ``in-maintenance`` state and perform necessary maintenance actions. When server2 is offline, server1 will encounter communication issues with the partner and will immediately transition to the ``partner-down`` state in which it will continue to respond to all DHCP queries but will no longer send lease updates to server2. Starting server2 after the maintenance will trigger normal state negotiation, lease database synchronization and, ultimately, a transition to the load-balancing or hot-standby state. The maintenance can now be performed on server1. It should be initiated by sending the ``ha-maintenance-start`` to the server2. If the ``ha-maintenance-start`` command was sent to the server and the server has transitioned to the ``partner-in-maintenance`` state it is possible to transition it and its partner back to the previous states to resume the normal operation of the HA pair. This is achieved by sending the ``ha-maintenance-cancel`` command to the server being in the ``partner-in-maintenance`` state. However, if the server has already transitioned to the ``partner-down`` state as a result of detecting that the partner is offline, canceling the maintenance is no longer possible. Upgrading from Older HA Versions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The maintenance mechanism was first introduced in the Kea 1.7.4 release. In order to upgrade the HA hooks library from the older version the administrator must shut down one of the servers and rely on the failover mechanism to get the online server to transition to the partner-down state and start serving all DHCP clients. After the successful upgrade of one of the servers to the version supporting the maintenance mechanism it is possible to benefit from this mechanism during the upgrade of the second server. In such a case, shut down the server running the old version. Next, send the ``ha-maintenance-start`` to the server that has been upgraded and supports the maintenance mechanism. This server should immediately transition to the partner-down state as it cannot communicate with the partner being offline. In the partner-down state the server will be responding to all DHCP requests. .. note:: Do not send the ``ha-maintenance-start`` command while the server running the old version is still online. The server receiving this command will return an error seeing that the partner does not support the maintenance mechanism. .. _ha-control-commands: Control Commands for High Availability ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Even though the HA hook library is designed to automatically resolve issues with DHCP service interruptions by redirecting the DHCP traffic to a surviving server and synchronizing the lease database when required, it may be useful for the administrator to have more control over the server behavior. In particular, it may be useful to be able to trigger lease-database synchronization on demand. It may also be useful to manually set the HA scopes that are being served. Note that the backup server can sometimes be used to handle DHCP traffic if both active servers are down. The backup server does not perform the failover function automatically; thus, in order to use the backup server to respond to DHCP queries, the server administrator must enable this function manually. The following sections describe commands supported by the HA hooks library which are available for the administrator. .. _command-ha-sync: The ha-sync Command ------------------- The ``ha-sync`` command instructs the server to synchronize its local lease database with the selected peer. The server fetches all leases from the peer and updates those locally stored leases which are older than those fetched. It also creates new leases when any of those fetched do not exist in the local database. All leases that are not returned by the peer but are in the local database are preserved. The database synchronization is unidirectional; only the database on the server to which the command has been sent is updated. In order to synchronize the peer's database a separate ``ha-sync`` command must be issued to that peer. Database synchronization may be triggered for both active and backup server types. The ``ha-sync`` command has the following structure (DHCPv4 server case): :: { "command": "ha-sync", "service": [ "dhcp4 "], "arguments": { "server-name": "server2", "max-period": 60 } } When the server receives this command it first disables the DHCP service of the server from which it will be fetching leases, by sending the ``dhcp-disable`` command to that server. The ``max-period`` parameter specifies the maximum duration (in seconds) for which the DHCP service should be disabled. If the DHCP service is successfully disabled, the synchronizing server fetches leases from the remote server by issuing one or more ``lease4-get-page`` commands. When the lease-database synchronization is complete, the synchronizing server sends the ``dhcp-enable`` command to the peer to re-enable its DHCP service. The ``max-period`` value should be sufficiently long to guarantee that it does not elapse before the synchronization is completed. Otherwise, the DHCP server will automatically enable its DHCP function while the synchronization is still in progress. If the DHCP server subsequently allocates any leases during the synchronization, those new (or updated) leases will not be fetched by the synchronizing server, leading to database inconsistencies. .. _command-ha-scopes: The ha-scopes Command --------------------- This command allows modification of the HA scopes that the server is serving. Consult :ref:`ha-load-balancing-config` and :ref:`ha-hot-standby-config` to learn what scopes are available for different HA modes of operation. The ``ha-scopes`` command has the following structure (DHCPv4 server case): :: { "command": "ha-scopes", "service": [ "dhcp4" ], "arguments": { "scopes": [ "HA_server1", "HA_server2" ] } } This command configures the server to handle traffic from both the ``HA_server1`` and ``HA_server2`` scopes. To disable all scopes specify an empty list: :: { "command": "ha-scopes", "service": [ "dhcp4 "], "arguments": { "scopes": [ ] } } .. _command-ha-continue: The ha-continue Command ----------------------- This command is used to resume the operation of the paused HA state machine, as described in :ref:`ha-pause-state-machine`. It takes no arguments, so the command structure is as simple as: :: { "command": "ha-continue", "service": [ "dhcp4" ] } .. _command-ha-heartbeat: The ha-heartbeat Command ------------------------ The :ref:`ha-server-states` section describes how the ``ha-heartbeat`` command is used by the active HA servers to detect a failure of one of them. This command, however, can also be sent by the system administrator to one or both servers to check their state with regards to the HA relationship. This allows for hooking up a monitoring system to the HA enabled servers to periodically check if they are operational or if any manual intervention is required. The ``ha-heartbeat`` command takes no arguments, e.g.: :: { "command": "ha-heartbeat", "service": [ "dhcp4" ] } Upon successful communication with the server a response similar to this should be returned: :: { "result": 0, "text": "HA peer status returned.", "arguments": { "state": "partner-down", "date-time": "Thu, 07 Nov 2019 08:49:37 GMT", "scopes": [ "server1" ], "unsent-update-count": 123 } } The returned state value may be one of the values listed in :ref:`ha-server-states`. In the example above, the ``partner-down`` state is returned, which indicates that the server which responded to the command is assuming that its partner is offline; thus, it is serving all DHCP requests sent to the servers. In order to ensure that the partner is indeed offline, the administrator should send the ``ha-heartbeat`` command to the second server. If sending the command fails, e.g. due to inability to establish TCP connection to the Control Agent or the Control Agent reports issues with communication with the DHCP server, it is very likely that the server is not running. The ``date-time`` parameter conveys the server's notion of time. The ``unsent-update-count`` value is incremented by the partner sending the heartbeat response when it cannot send the lease update. Suppose it is a result of the temporary communication interruption. In that case, the partner receiving the heartbeat response tracks the value changes and can determine whether there are any new lease updates that it did not receive. When the communication is re-established, the server uses this value to decide whether or not it should synchronize its lease database. The value is set to 0 when the server is started. It is never reset to 0 during the server operation, even after the partner synchronizes the database. It is a cumulative count of all unsent lease updates since the server boot. A non-zero value itself is not an indication of any present issues with lease updates. Constantly incrementing value is. The typical response returned by one of the servers when both servers are operational is: :: { "result": 0, "text": "HA peer status returned.", "arguments": { "state": "load-balancing", "date-time": "Thu, 07 Nov 2019 08:49:37 GMT", "scopes": [ "server1" ], "unsent-update-count": 0 } } In most cases it is desired to send the ``ha-heartbeat`` command to both HA enabled servers to verify the state of the entire HA setup. In particular, if the response sent to one of the servers indicates that the server is in the ``load-balancing`` state, it merely means that this server is operating as if the partner is still functional. When the partner dies it actually takes some time for the surviving server to realize it. The :ref:`ha-scope-transition` section describes the algorithm which the surviving server follows before it transitions to the ``partner-down`` state. If the ``ha-heartbeat`` command is sent during the time window between the failure of one of the servers and the transition of the surviving server to the ``partner-down`` state, the response from the surviving server doesn't reflect the failure. Sending the command to the failing server allows for detecting the failure. .. note:: Remember! Always send the ``ha-heartbeat`` command to both active HA servers to check the state of the entire HA setup. Sending it to only one of the servers may not reflect issues with one of the servers that just began. .. _command-ha-status-get: The status-get Command ------------------------ ``status-get`` is a general-purpose command supported by several Kea daemons, not only DHCP servers. However, when sent to the DHCP server with HA enabled, it can be used to get insight into the details of the HA-specific status information of the servers used in the HA configuration. Not only does the response contain the status information of the server receiving this command, but also the information about its partner if it is available. The following is an example response to the ``status-get`` command, including the HA status of two load-balancing servers: :: { "result": 0, "text": "", "arguments": { "pid": 1234, "uptime": 3024, "reload": 1111, "high-availability": [ { "ha-mode": "load-balancing", "ha-servers": { "local": { "role": "primary", "scopes": [ "server1" ], "state": "load-balancing" }, "remote": { "age": 10, "in-touch": true, "role": "secondary", "last-scopes": [ "server2" ], "last-state": "load-balancing", "communication-interrupted": true, "connecting-clients": 2, "unacked-clients": 1, "unacked-clients-left": 2, "analyzed-packets": 8 } } } ], "multi-threading-enabled": true, "thread-pool-size": 4, "packet-queue-size": 64 } } The ``high-availability`` argument is a list which currently always comprises one element. There are plans to extend the HA implementation to facilitate multiple HA relationships for a single server instance. In that case, the returned list will comprise more elements, each describing the status of a different relationship in which the server participates. Currently, it is only one status. .. note:: In Kea 1.7.8 an incompatible change was introduced to the syntax of the ``status-get`` response. Previously, the HA status for a single relationship was returned within the ``arguments`` map. As of Kea 1.7.8, the returned status is enclosed in the list as described above. Any existing code relying on the previous syntax must be updated to work with the new Kea versions. The ``ha-servers`` map contains two structures: ``local`` and ``remote``. The former contains the status information of the server which received the command. The latter contains the status information known to the local server about the partner. The ``role`` of the partner server is gathered from the local configuration file, therefore it should always be available. The remaining status information such as ``last-scopes`` and ``last-state`` is not available until the local server communicates with the remote by successfully sending the ``ha-heartbeat`` command. If at least one such communication took place, the returned value of ``in-touch`` parameter is set to ``true``. By examining this value, the command sender can determine whether the information about the remote server is reliable. The ``last-scopes`` and ``last-state`` contain the information about the HA scopes served by the partner and its state. Note that this information is gathered during the heartbeat command exchange, so it may not be accurate if the communication problem occur between the partners and this status information is not refreshed. In such a case, it may be useful to send the ``status-get`` command to the partner server directly to check its current state. The ``age`` parameter specifies the number of seconds since the information from the partner was gathered (the age of this information). The ``communication-interrupted`` boolean value indicates if the server receiving the ``status-get`` command (local server) has been unable to communicate with the partner longer than the duration specified as ``max-response-delay``. In such a situation we say that active servers are in the communication interrupted state or that the communication between them is interrupted. At this point, the local server may start monitoring the DHCP traffic directed to the partner to see if the partner is responding to this traffic. More about the failover procedure can be found in :ref:`ha-load-balancing-config`. The ``connecting-clients``, ``unacked-clients``, ``unacked-clients-left`` and ``analyzed-packets`` parameters have been introduced together with the ``communication-interrupted`` parameter in the Kea 1.7.8 release and they convey useful information about the state of the DHCP traffic monitoring in the communication interrupted state. If the server leaves the communication interrupted state these parameters are all reset to 0. These parameters have the following meaning in the communication interrupted state: - ``connecting-clients`` - number of different clients which have attempted to get a lease from the remote server. The clients are differentiated by their MAC address and client identifier (in DHCPv4) or DUID (in DHCPv6). This number includes both "unacked" clients (for which "secs" field or "elapsed time" value exceeded the ``max-response-delay``). - ``unacked-clients`` - number of different clients which have been considered "unacked", i.e. the clients which have been trying to get the lease long enough, so as the value of the "secs" field or "elapsed time" exceeded the ``max-response-delay``. - ``unacked-clients-left`` - number of additional clients which have to be considered "unacked" before the server enters the partner-down state. This value decreases when the ``unacked-clients`` value increases. The local server will enter the ``partner-down`` state when this value decreases to 0. - ``analyzed-packets`` - total number of all packets directed to the partner server and analyzed by the local server since entering the communication interrupted state. It includes retransmissions from the same clients. Monitoring these values helps to predict when the local server will enter the partner-down state or why the server hasn't yet entered this state. The last parameter introduced in the Kea 1.7.8 release was the ``ha-mode``. It returns the HA mode of operation selected using the ``mode`` parameter in the configuration file. It can hold one of the following values: ``load-balancing``, ``hot-standby`` or ``passive-backup``. The ``status-get`` response has the format described above only in the ``load-balancing`` and ``hot-standby`` modes. In the ``passive-backup`` mode the ``remote`` map is not included in the response because in this mode there is only one active server (local). The response comprises no information about the status of the backup servers. .. _command-ha-maintenance-start: The ha-maintenance-start Command -------------------------------- This command is used to initiate transition of the server's partner into the ``in-maintenance`` state and the transition of the server receiving the command into the ``partner-in-maintenance`` state. See the :ref:`ha-maintenance` for the details. :: { "command": "ha-maintenance-start", "service": [ "dhcp4" ] } .. _command-ha-maintenance-cancel: The ha-maintenance-cancel Command --------------------------------- This command is used to cancel the maintenance previously initiated using the ``ha-maintenance-start`` command. The server receiving this command will first send the ``ha-maintenance-notify`` with the cancel flag set to true to its partner. Next, the server will revert from the ``partner-in-maintenance`` state to the previous state. See the :ref:`ha-maintenance` for the details. :: { "command": "ha-maintenance-cancel", "service": [ "dhcp4" ] } .. _command-ha-maintenance-notify: The ha-maintenance-notify Command --------------------------------- This command is sent by the server receiving the ``ha-maintenance-start`` or the ``ha-maintenance-cancel`` command to its partner to cause the partner to transition to the ``in-maintenance`` state or to revert from this state to a previous state. See the :ref:`ha-maintenance` for the details. :: { "command": "ha-maintenance-notify", "service": [ "dhcp4" ], "arguments": { "cancel": false } } .. warning:: The ``ha-maintenance-notify`` command is not meant to be used by the system administrators. It is used for internal communication between a pair of HA enabled DHCP servers. Direct use of this command is not supported and may produce unintended consequences. .. _command-ha-reset: The ha-reset Command -------------------- This command causes the server to reset its High Availability state machine by transitioning it to the waiting state. A partner in the ``communication-recovery`` state may send this command to cause the server to synchronize its lease database. The database synchronization is required when the partner has failed to send all lease database updates after re-establishing connection after a temporary connection failure. It is also required when the ``delayed-updates-limit`` is exceeded when the server is in the ``communication-recovery`` state. A server administrator may send the command to reset a misbehaving state machine. This command includes no arguments, e.g.: :: { "command": "ha-reset", "service": [ "dhcp4" ] } The response: :: { "result": 0, "text": "HA state machine reset." } If the server receiving this command is already in the waiting state, the command has no effect. .. _command-ha-sync-complete-notify: The ha-sync-complete-notify Command ----------------------------------- A server sends this command to its partner to notify that it has completed lease database synchronization. The partner may enable its DHCP service if it can allocate new leases in its current state. The partner does not enable the DHCP service in the partner-down state until it sends a successful heartbeat testing connection with the server. If the connection is still unavailable, the server in the partner-down state enables the DHCP service to continue responding to the clients. :: { "command": "ha-sync-complete-notify", "service": [ "dhcp4" ] } The response: :: { "result": 0, "text": "Server successfully notified about the synchronization completion." } .. warning:: The ``ha-sync-complete-notify`` command is not meant to be used by the system administrators. It is used for internal communication between a pair of HA enabled DHCP servers. Direct use of this command is not supported and may produce unintended consequences. kea-2.0.2/doc/sphinx/arm/hooks-lease-cmds.rst0000644000175000017500000007471314206773363015750 00000000000000.. _lease-cmds: lease_cmds: Lease Commands ========================== This section describes the hook library with commands used to manage leases. Kea provides a way to store lease information in several backends (memfile, MySQL, PostgreSQL, and Cassandra), and this library provides an interface that can manipulate leases in a unified, safe way. In particular, it allows things previously impossible: lease manipulation in memfile while Kea is running, sanity check changes, lease existence checks, and removal of all leases belonging to a specific subnet. The hook library can also catch more obscure errors, like an attempt to add a lease with a subnet-id that does not exist in the configuration, or configuring a lease to use an address that is outside of the subnet to which it is supposed to belong. The library also provides a non-programmatic way to manage user contexts associated with leases. .. note:: This library may only be loaded by the ``kea-dhcp4`` or the ``kea-dhcp6`` process. There are many use cases where an administrative command may be useful; for example, during migration between servers or different vendors, when a certain network is being retired, or when a device has been disconnected and the system administrator knows that it will not be coming back. The "get" queries may be useful for automating certain management and monitoring tasks. They can also act as preparatory steps for lease updates and removals. This library provides the following commands: - ``lease4-add`` - adds a new IPv4 lease. - ``lease6-add`` - adds a new IPv6 lease. - ``lease6-bulk-apply`` - creates, updates and/or deletes multiple IPv6 leases in a single transaction. - ``lease4-get`` - checks whether an IPv4 lease with the specified parameters exists and returns it if it does. - ``lease6-get`` - checks whether an IPv6 lease with the specified parameters exists and returns it if it does. - ``lease4-get-all`` - returns all IPv4 leases or all IPv4 leases for the specified subnets. - ``lease6-get-all`` - returns all IPv6 leases or all IPv6 leases for the specified subnets. - ``lease4-get-page`` - returns a set ("page") of leases from the list of all IPv4 leases in the database. By iterating through the pages it is possible to retrieve all the leases. - ``lease6-get-page`` - returns a set ("page") of leases from the list of all IPv6 leases in the database. By iterating through the pages it is possible to retrieve all the leases. - ``lease4-get-by-hw-address`` - return all IPv4 leases with the specified hardware address. - ``lease4-get-by-client-id`` - return all IPv4 leases with the specified client id. - ``lease6-get-by-duid`` - returns all IPv6 leases with the specified DUID. - ``lease4-get-by-hostname`` - return all IPv4 leases with the specified hostname. - ``lease6-get-by-hostname`` - return all IPv6 leases with the specified hostname. - ``lease4-del`` - deletes an IPv4 lease with the specified parameters. - ``lease6-del`` - deletes an IPv6 lease with the specified parameters. - ``lease4-update`` - updates an IPv4 lease. - ``lease6-update`` - updates an IPv6 lease. - ``lease4-wipe`` - removes all leases from a specific IPv4 subnet or from all subnets. - ``lease6-wipe`` - removes all leases from a specific IPv6 subnet or from all subnets. - ``lease4-resend-ddns`` - resend a request to update DNS entries for an existing lease. - ``lease6-resend-ddns`` - resend a request to update DNS entries for an existing lease. The lease commands library is part of the open source code and is available to every Kea user. All commands use JSON syntax and can be issued either using the control channel (see :ref:`ctrl-channel`) or Control Agent (see :ref:`kea-ctrl-agent`). The library can be loaded in the same way as other hook libraries, and it does not take any parameters. It supports both DHCPv4 and DHCPv6 servers. :: "Dhcp6": { "hooks-libraries": [ { "library": "/path/libdhcp_lease_cmds.so" } ... ] } .. _command-lease4-add: .. _command-lease6-add: The lease4-add, lease6-add Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``lease4-add`` and ``lease6-add`` commands allow for the creation of a new lease. Typically Kea creates a lease when it first sees a new device; however, sometimes it may be convenient to create the lease manually. The ``lease4-add`` command requires at least two parameters: an IPv4 address and an identifier, i.e. hardware (MAC) address. A third parameter, subnet-id, is optional. If the subnet-id is not specified or the specified value is 0, Kea will try to determine the value by running a subnet-selection procedure. If specified, however, its value must match the existing subnet. The simplest successful call might look as follows: :: { "command": "lease4-add", "arguments": { "ip-address": "192.0.2.202", "hw-address": "1a:1b:1c:1d:1e:1f" } } The ``lease6-add`` command requires three parameters: an IPv6 address, an IAID value (identity association identifier, a value sent by clients), and a DUID. As with lease4-add, the subnet-id parameter is optional. If the subnet-id is not specified or the provided value is 0, Kea will try to determine the value by running a subnet-selection procedure. If specified, however, its value must match the existing subnet. For example: :: { "command": "lease6-add", "arguments": { "subnet-id": 66, "ip-address": "2001:db8::3", "duid": "1a:1b:1c:1d:1e:1f:20:21:22:23:24", "iaid": 1234 } } ``lease6-add`` can also be used to add leases for IPv6 prefixes. In this case there are three additional parameters that must be specified: subnet-id, type (set to value of "IA_PD"), and prefix length. The actual prefix is set using the ip-address field. Note that Kea cannot guess subnet-id values for prefixes; they must be specified explicitly. For example, to configure a lease for prefix 2001:db8:abcd::/48, the following command can be used: :: { "command": "lease6-add", "arguments": { "subnet-id": 66, "type": "IA_PD", "ip-address": "2001:db8:abcd::", "prefix-len": 48, "duid": "1a:1b:1c:1d:1e:1f:20:21:22:23:24", "iaid": 1234 } } The commands can take several additional optional parameters: - ``valid-lft`` - specifies the lifetime of the lease, expressed in seconds. If not specified, the value configured in the subnet related to the specified subnet-id is used. - ``expire`` - creates a timestamp of the lease expiration time, expressed in UNIX format (seconds since 1 Jan 1970). If not specified, the default value is now + the lease lifetime (the value of valid-lft). - ``fqdn-fwd`` - specifies whether the lease should be marked as if a forward DNS update were conducted. Note this only affects the data stored in the lease database, and no DNS update will be performed. If configured, a DNS update to remove the A or AAAA records will be conducted when the lease is removed due to expiration or being released by a client. If not specified, the default value is false. The hostname parameter must be specified if fqdn-fwd is set to true. - ``fqdn-rev`` - specifies whether the lease should be marked as if reverse DNS update were conducted. Note this only affects the data stored in the lease database, and no DNS update will be performed.. If configured, a DNS update to remove the PTR record will be conducted when the lease is removed due to expiration or being released by a client. If not specified, the default value is false. The hostname parameter must be specified if fqdn-fwd is set to true. - ``hostname`` - specifies the hostname to be associated with this lease. Its value must be non-empty if either fqdn-fwd or fwdn-rev are set to true. If not specified, the default value is an empty string. - ``hw-address`` - optionally specifies a hardware (MAC) address for an IPv6 lease. It is a mandatory parameter for an IPv4 lease. - ``client-id`` - optionally specifies a client identifier for an IPv4 lease. - ``preferred-lft`` - optionally specifies a preferred lifetime for IPv6 leases. If not specified, the value configured for the subnet corresponding to the specified subnet-id is used. This parameter is not used when adding an IPv4 lease. - ``state`` - specify the state of added lease, can be 0 for ``default``, 1 for ``declined`` and 2 for ``expired-reclaimed`` state. Any other value will cause an error. Note that using 1 for a "IA_PD" lease type is illegal and will be rejected. - ``user-context`` - specifies the user context to be associated with this lease. It must be a JSON map. Here is an example of a more complex lease addition: :: { "command": "lease6-add", "arguments": { "subnet-id": 66, "ip-address": "2001:db8::3", "duid": "01:02:03:04:05:06:07:08", "iaid": 1234, "hw-address": "1a:1b:1c:1d:1e:1f", "preferred-lft": 500, "valid-lft": 1000, "expire": 12345678, "fqdn-fwd": true, "fqdn-rev": true, "state": 0, "hostname": "urania.example.org", "user-context": { "version": 1 } } } The command returns a status that indicates either success (result 0) or failure (result 1). A failed command always includes a text parameter that explains the cause of failure. For example: :: { "result": 0, "text": "Lease added." } Example failure: :: { "result": 1, "text": "missing parameter 'ip-address' (:3:19)" } .. _command-lease6-bulk-apply: The lease6-bulk-apply Command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``lease6-bulk-apply`` was implemented to address the performance penalty in the High-Availability mode when a single DHCPv6 transaction resulted in multiple lease updates sent to the partner, if multiple address and/or prefix leases were allocated. Consider the case when a DHCPv6 client requests the assignment of two IPv6 addresses and two IPv6 prefixes: that may result in allocation of four leases. In addition, DHCPv6 may assign a different address than the one requested by the client during the renew or rebind stage, and delete the leases previously used by this client. There are six lease changes sent between the HA partners in this case. Sending these updates in individual commands, e.g. via ``lease6-update``, is highly inefficient and produces unnecessary delays in communication, both between the HA partners and in sending the response to the DHCPv6 client. The ``lease6-bulk-apply`` command deals with this problem by aggregating all lease changes in a single command. Both deleted leases and new/updated leases are conveyed in a single command. The receiving server iterates over the deleted leases and deletes them from its lease database. Next, it iterates over the new/updated leases and adds them to the database or updates them if they already exist. Even though High Availability is the major application for this command, it can be freely used in all cases when it is desired to send multiple lease changes in a single command. In the following example, we ask to delete two leases and to add or update two other leases in the database: :: { "command": "lease6-bulk-apply", "arguments": { "deleted-leases": [ { "ip-address": "2001:db8:abcd::", "type": "IA_PD", ... }, { "ip-address": "2001:db8:abcd::234", "type": "IA_NA", ... } ], "leases": [ { "subnet-id": 66, "ip-address": "2001:db8:cafe::", "type": "IA_PD", ... }, { "subnet-id": 66, "ip-address": "2001:db8:abcd::333", "type": "IA_NA", ... } ] } } If any of the leases are malformed, no lease changes are applied to the lease database. If the leases are well-formed but there is a failure to apply any of the lease changes to the database, the command continues to be processed for other leases. All the leases for which the command was unable to apply the changes in the database are listed in the response. For example: :: { "result": 0, "text": "Bulk apply of 2 IPv6 leases completed". "arguments": { "failed-deleted-leases": [ { "ip-address": "2001:db8:abcd::", "type": "IA_PD", "result": 3, "error-message": "no lease found" } ], "failed-leases": [ { "ip-address": "2001:db8:cafe::", "type": "IA_PD", "result": 1, "error-message": "unable to communicate with the lease database" } ] } } The response above indicates that the hooks library was unable to delete the lease for prefix "2001:db8:abcd::" and add or update the lease for prefix "2001:db8:cafe::". However, there are two other lease changes which have been applied as indicated by the text message. The ``result`` is the status constant that indicates the type of the error experienced for the particular lease. The meaning of the returned codes are the same as the results returned for the commands. In particular, the result of 1 indicates an error while processing the lease, e.g. a communication error with the database. The result of 3 indicates that an attempt to delete the lease was unsuccessful because such a lease doesn't exist (empty result). .. _command-lease4-get: .. _command-lease6-get: The lease4-get, lease6-get Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``lease4-get`` or ``lease6-get`` can be used to query the lease database and retrieve existing leases. There are two types of parameters the ``lease4-get`` command supports: (address) or (subnet-id, identifier-type, identifier). There are also two types for ``lease6-get``: (address, type) or (subnet-id, identifier-type, identifier, IAID, type). The first type of query is used when the address (either IPv4 or IPv6) is known, but the details of the lease are not; one common use case of this type of query is to find out whether a given address is being used. The second query uses identifiers; currently supported identifiers for leases are: "hw-address" (IPv4 only), "client-id" (IPv4 only), and "duid" (IPv6 only). An example ``lease4-get`` command for getting a lease using an IPv4 address is: :: { "command": "lease4-get", "arguments": { "ip-address": "192.0.2.1" } } An example of the ``lease6-get`` query is: :: { "command": "lease6-get", "arguments": { "ip-address": "2001:db8:1234:ab::", "type": "IA_PD" } } An example query by "hw-address" for an IPv4 lease looks as follows: :: { "command": "lease4-get", "arguments": { "identifier-type": "hw-address", "identifier": "08:08:08:08:08:08", "subnet-id": 44 } } An example query by "client-id" for an IPv4 lease looks as follows: :: { "command": "lease4-get", "arguments": { "identifier-type": "client-id", "identifier": "01:01:02:03:04:05:06", "subnet-id": 44 } } An example query by (subnet-id, identifier-type, identifier, iaid, type) for an IPv6 lease is: :: { "command": "lease4-get", "arguments": { "identifier-type": "duid", "identifier": "08:08:08:08:08:08", "iaid": 1234567, "type": "IA_NA", "subnet-id": 44 } } The type is an optional parameter. Supported values are: IA_NA (non-temporary address) and IA_PD (IPv6 prefix). If not specified, IA_NA is assumed. ``leaseX-get`` returns a result that indicates a result of the operation and lease details, if found. It has one of the following values: 0 (success), 1 (error), or 3 (empty). An empty result means that a query has been completed properly, but the object (a lease in this case) has not been found. The lease parameters, if found, are returned as arguments. An example result returned when the host was found: :: { "arguments": { "client-id": "42:42:42:42:42:42:42:42", "cltt": 12345678, "fqdn-fwd": false, "fqdn-rev": true, "hostname": "myhost.example.com.", "hw-address": "08:08:08:08:08:08", "ip-address": "192.0.2.1", "state": 0, "subnet-id": 44, "valid-lft": 3600 }, "result": 0, "text": "IPv4 lease found." } .. _command-lease4-get-all: .. _command-lease6-get-all: The lease4-get-all, lease6-get-all Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``lease4-get-all`` and ``lease6-get-all`` are used to retrieve all IPv4 or IPv6 leases, or all leases for the specified set of subnets. All leases are returned when there are no arguments specified with the command, as in the following example: :: { "command": "lease4-get-all" } If arguments are provided, it is expected that they contain the "subnets" parameter, which is a list of subnet identifiers for which the leases should be returned. For example, in order to retrieve all IPv6 leases belonging to the subnets with identifiers 1, 2, 3, and 4: :: { "command": "lease6-get-all", "arguments": { "subnets": [ 1, 2, 3, 4 ] } } The returned response contains a detailed list of leases in the following format: :: { "arguments": { "leases": [ { "cltt": 12345678, "duid": "42:42:42:42:42:42:42:42", "fqdn-fwd": false, "fqdn-rev": true, "hostname": "myhost.example.com.", "hw-address": "08:08:08:08:08:08", "iaid": 1, "ip-address": "2001:db8:2::1", "preferred-lft": 500, "state": 0, "subnet-id": 44, "type": "IA_NA", "valid-lft": 3600 }, { "cltt": 12345678, "duid": "21:21:21:21:21:21:21:21", "fqdn-fwd": false, "fqdn-rev": true, "hostname": "", "iaid": 1, "ip-address": "2001:db8:0:0:2::", "preferred-lft": 500, "prefix-len": 80, "state": 0, "subnet-id": 44, "type": "IA_PD", "valid-lft": 3600 } ] }, "result": 0, "text": "2 IPv6 lease(s) found." } .. .. warning:: The ``lease4-get-all`` and ``lease6-get-all`` commands may result in very large responses. This may have a negative impact on the DHCP server's responsiveness while the response is generated and transmitted over the control channel, as the server imposes no restriction on the number of leases returned as a result of this command. .. _command-lease4-get-page: .. _command-lease6-get-page: The lease4-get-page, lease6-get-page Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``lease4-get-all`` and ``lease6-get-all`` commands may result in very large responses; generating such a response may consume CPU bandwidth as well as memory. It may even cause the server to become unresponsive. In case of large lease databases it is usually better to retrieve leases in chunks, using the paging mechanism. ``lease4-get-page`` and ``lease6-get-page`` implement a paging mechanism for DHCPv4 and DHCPv6 servers respectively. The following command retrieves the first 1024 IPv4 leases: :: { "command": "lease4-get-page", "arguments": { "from": "start", "limit": 1024 } } The keyword ``start`` denotes that the first page of leases should be retrieved. Alternatively, an IPv4 zero address can be specified to retrieve the first page: :: { "command": "lease4-get-page", "arguments": { "from": "0.0.0.0", "limit": 1024 } } Similarly, the IPv6 zero address can be specified in the ``lease6-get-page`` command: :: { "command": "lease6-get-page", "arguments": { "from": "::", "limit": 6 } } The response has the following structure: :: { "arguments": { "leases": [ { "ip-address": "2001:db8:2::1", ... }, { "ip-address": "2001:db8:2::9", ... }, { "ip-address": "2001:db8:3::1", ... }, { "ip-address": "2001:db8:5::3", ... } { "ip-address": "2001:db8:4::1", ... }, { "ip-address": "2001:db8:2::7", ... } ], "count": 6 }, "result": 0, "text": "6 IPv6 lease(s) found." } Note that the leases' details were excluded from the response above for brevity. Generally, the returned list is not sorted in any particular order. Some lease database backends may sort leases in ascending order of addresses, but the controlling client must not rely on this behavior. In cases of highly distributed databases, such as Cassandra, ordering may be inefficient or even impossible. The ``count`` parameter contains the number of returned leases on the page. To fetch the next page, the client must use the last address of the current page as an input to the next ``lease4-get-page`` or ``lease6-get-page`` command call. In this example it is: :: { "command": "lease6-get-page", "arguments": { "from": "2001:db8:2::7", "count": 6 } } because 2001:db8:2::7 is the last address on the current page. The client may assume that it has reached the last page when the ``count`` value is lower than that specified in the command; this includes the case when the ``count`` is equal to 0, meaning that no leases were found. .. _command-lease4-get-by-hw-address: .. _command-lease4-get-by-client-id: .. _command-lease6-get-by-duid: .. _command-lease4-get-by-hostname: .. _command-lease6-get-by-hostname: The lease4-get-by-\*, lease6-get-by-\* Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``leaseX-get-by-Y`` can be used to query the lease database and retrieve all existing leases with a specified hardware address (IPv4 only), client id IPv4 only), duid (IPv6 only) identifiers or hostname. An example ``lease4-get-by-hw-address`` command for getting IPv4 leases with a given hardware address is: :: { "command": "lease4-get-by-hw-address", "arguments": { "hw-address": "08:08:08:08:08:08" } } An example of the ``lease6-get-by-hostname`` is: :: { "command": "lease6-get-by-hostname", "arguments": { "hostname": "myhost.example.org" } } The by key is the only parameter. The returned response contains a detailed list of leases in the same format as ``leaseX-get-all``. This list can be empty and usually is never large. .. _command-lease4-del: .. _command-lease6-del: The lease4-del, lease6-del Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``leaseX-del`` can be used to delete a lease from the lease database. There are two types of parameters this command supports, similar to the leaseX-get commands: (address) for both v4 and v6, (subnet-id, identifier-type, identifier) for v4, and (subnet-id, identifier-type, identifier, type, IAID) for v6. The first type of query is used when the address (either IPv4 or IPv6) is known, but the details of the lease are not. One common use case is where an administrator wants a specified address to no longer be used. The second form of the command uses identifiers. For maximum flexibility, this interface uses identifiers as a pair of values: the type and the actual identifier. The currently supported identifiers are "hw-address" (IPv4 only), "client-id" (IPv4 only), and "duid" (IPv6 only). An example command for deleting a lease by address is :: { "command": "lease4-del", "arguments": { "ip-address": "192.0.2.202" } } An example IPv4 lease deletion by "hw-address" is: :: { "command": "lease4-del", "arguments": { "identifier": "08:08:08:08:08:08", "identifier-type": "hw-address", "subnet-id": 44 } } As of Kea 1.7.10, a new parameter, ``update-ddns``, is supported (IPv4 and IPv6). When true it instructs the server to queue a request to kea-dhcp-ddns to remove DNS entries after the lease is successfully deleted if: - DDNS updating is enabled. (i.e. "dhcp-ddns":{ "enable-updates": true }) - The lease's hostname is not empty. - At least one of the lease's DNS direction flags (fdqn_fwd or fdqn_rev) is true. This parameter defaults to false. An example of its use is shown below: :: { "command": "lease4-del", "arguments": { "ip-address": "192.0.2.202", "update-ddns": true } } ``leaseX-del`` returns a result that indicates the outcome of the operation. It has one of the following values: 0 (success), 1 (error), or 3 (empty). The empty result means that a query has been completed properly, but the object (a lease in this case) has not been found. .. _command-lease4-update: .. _command-lease6-update: The lease4-update, lease6-update Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``lease4-update`` and ``lease6-update`` commands can be used to update existing leases. Since all lease database backends are indexed by IP addresses, it is not possible to update an address, but all other fields may be altered. If an address needs to be changed, please use ``leaseX-del`` followed by ``leaseX-add``. The subnet-id parameter is optional. If not specified, or if the specified value is 0, Kea will try to determine its value by running a subnet-selection procedure. If specified, however, its value must match the existing subnet. The optional boolean parameter "force-create" specifies whether the lease should be created if it does not exist in the database. It defaults to false, which indicates that the lease is not created if it does not exist. In such a case, an error is returned as a result of trying to update a non-existing lease. If the "force-create" parameter is set to true and the updated lease does not exist, the new lease is created as a result of receiving the ``leaseX-update``. An example of a command to update an IPv4 lease is: :: { "command": "lease4-update", "arguments": { "ip-address": "192.0.2.1", "hostname": "newhostname.example.org", "hw-address": "1a:1b:1c:1d:1e:1f", "subnet-id": 44, "force-create": true } } An example of a command to update an IPv6 lease is: :: { "command": "lease6-update", "arguments": { "ip-address": "2001:db8::1", "duid": "88:88:88:88:88:88:88:88", "iaid": 7654321, "hostname": "newhostname.example.org", "subnet-id": 66, "force-create": false } } .. _command-lease4-wipe: .. _command-lease6-wipe: The lease4-wipe, lease6-wipe Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``lease4-wipe`` and ``lease6-wipe`` are designed to remove all leases associated with a given subnet. This administrative task is expected to be used when an existing subnet is being retired. Note that the leases are not properly expired; no DNS updates are carried out, no log messages are created, and hooks are not called for the leases being removed. An example of ``lease4-wipe`` is: :: { "command": "lease4-wipe", "arguments": { "subnet-id": 44 } } An example of ``lease6-wipe`` is: :: { "command": "lease6-wipe", "arguments": { "subnet-id": 66 } } The commands return a text description of the number of leases removed, plus the status code 0 (success) if any leases were removed or 3 (empty) if there were no leases. Status code 1 (error) may be returned if the parameters are incorrect or some other exception is encountered. Subnet-id 0 has a special meaning; it tells Kea to delete leases from all configured subnets. Also, the subnet-id parameter may be omitted. If not specified, leases from all subnets are wiped. Note: not all backends support this command. .. _command-lease4-resend-ddns: .. _command-lease6-resend-ddns: The lease4-resend-ddns, lease6-resend-ddns Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``lease4-resend-ddns`` or ``lease6-resend-ddns`` can be used to generate a request to kea-dhcp-ddns to update the DNS entries for an existing lease. The desired lease is selected by a single parameter, "ip-address". In order for an update request to be generated, DDNS updating must be enabled and DNS entries must have already been made (or attempted) for the lease. In other words all of the following must be true: - DDNS updating must be enabled. (i.e. "dhcp-ddns":{ "enable-updates": true"}) - The lease's hostname must not be empty. - At least one of the lease's DNS direction flags (fdqn_fwd or fdqn_rev) must be true. An example ``lease4-resend-ddns`` command for getting a lease using an IPv4 address is: :: { "command": "lease4-resend-ddns", "arguments": { "ip-address": "192.0.2.1" } } An example of the ``lease6-resend-ddns`` query is: :: { "command": "lease6-resend-ddns", "arguments": { "ip-address": "2001:db8:1::1" } } ``leaseX-resend-ddns`` returns a result that indicates a result of the operation. It has one of the following values: 0 (success), 1 (error), or 3 (empty). An empty result means that a query has been completed properly, but the object (a lease in this case) has not been found. A successful result does not mean that DNS has been successfully updated. It indicates that a request to update DNS has been successfully created and queued for transmission to kea-dhcp-ddns. An example result returned when the lease was found: :: { "result": 0, "text": "NCR generated for: 2001:db8:1::1, hostname: example.com." } kea-2.0.2/doc/sphinx/arm/dhcp6-srv.rst0000644000175000017500000116350614206773363014426 00000000000000.. _dhcp6: ***************** The DHCPv6 Server ***************** .. _dhcp6-start-stop: Starting and Stopping the DHCPv6 Server ======================================= It is recommended that the Kea DHCPv6 server be started and stopped using ``keactrl`` (described in :ref:`keactrl`); however, it is also possible to run the server directly. It accepts the following command-line switches: - ``-c file`` - specifies the configuration file. This is the only mandatory switch. - ``-d`` - specifies whether the server logging should be switched to debug/verbose mode. In verbose mode, the logging severity and debuglevel specified in the configuration file are ignored; "debug" severity and the maximum debuglevel (99) are assumed. The flag is convenient for temporarily switching the server into maximum verbosity, e.g. when debugging. - ``-p server-port`` - specifies the local UDP port on which the server will listen. This is only useful during testing, as a DHCPv6 server listening on ports other than the standard ones will not be able to handle regular DHCPv6 queries. - ``-P client-port`` - specifies the remote UDP port to which the server will send all responses. This is only useful during testing, as a DHCPv6 server sending responses to ports other than the standard ones will not be able to handle regular DHCPv6 queries. - ``-t file`` - specifies a configuration file to be tested. Kea-dhcp6 will load it, check it, and exit. During the test, log messages are printed to standard output and error messages to standard error. The result of the test is reported through the exit code (0 = configuration looks ok, 1 = error encountered). The check is not comprehensive; certain checks are possible only when running the server. - ``-v`` - displays the Kea version and exits. - ``-V`` - displays the Kea extended version with additional parameters and exits. The listing includes the versions of the libraries dynamically linked to Kea. - ``-W`` - displays the Kea configuration report and exits. The report is a copy of the ``config.report`` file produced by ``./configure``; it is embedded in the executable binary. On startup, the server will detect available network interfaces and will attempt to open UDP sockets on all interfaces mentioned in the configuration file. Since the DHCPv6 server opens privileged ports, it requires root access. This daemon must be run as root. During startup, the server will attempt to create a PID file of the form: [**runstatedir**]/kea/[**conf name**].kea-dhcp6.pid where: - ``runstatedir``: The value as passed into the build configure script; it defaults to "/usr/local/var/run". Note that this value may be overridden at runtime by setting the environment variable ``KEA_PIDFILE_DIR``, although this is intended primarily for testing purposes. - ``conf name``: The configuration file name used to start the server, minus all preceding paths and the file extension. For example, given a pathname of "/usr/local/etc/kea/myconf.txt", the portion used would be "myconf". If the file already exists and contains the PID of a live process, the server will issue a DHCP6_ALREADY_RUNNING log message and exit. It is possible, though unlikely, that the file is a remnant of a system crash and the process to which the PID belongs is unrelated to Kea. In such a case it would be necessary to manually delete the PID file. The server can be stopped using the ``kill`` command. When running in a console, the server can also be shut down by pressing ctrl-c. It detects the key combination and shuts down gracefully. .. _dhcp6-configuration: DHCPv6 Server Configuration =========================== Introduction ------------ This section explains how to configure the DHCPv6 server using a configuration file. Before DHCPv6 is started, its configuration file must be created. The basic configuration is as follows: :: { # DHCPv6 configuration starts on the next line "Dhcp6": { # First we set up global values "valid-lifetime": 4000, "renew-timer": 1000, "rebind-timer": 2000, "preferred-lifetime": 3000, # Next we set up the interfaces to be used by the server. "interfaces-config": { "interfaces": [ "eth0" ] }, # And we specify the type of lease database "lease-database": { "type": "memfile", "persist": true, "name": "/var/lib/kea/dhcp6.leases" }, # Finally, we list the subnets from which we will be leasing addresses. "subnet6": [ { "subnet": "2001:db8:1::/64", "pools": [ { "pool": "2001:db8:1::1-2001:db8:1::ffff" } ] } ] # DHCPv6 configuration ends with the next line } } The following paragraphs provide a brief overview of the parameters in the above example, along with their format. Subsequent sections of this chapter go into much greater detail for these and other parameters. The lines starting with a hash (#) are comments and are ignored by the server; they do not impact its operation in any way. The configuration starts in the first line with the initial opening curly bracket (or brace). Each configuration must contain an object specifying the configuration of the Kea module using it. In the example above this object is called ``Dhcp6``. .. note:: In the current Kea release it is possible to specify configurations of multiple modules within a single configuration file, but this is not recommended and support for it was removed in 1.7.10 release, including the ``Logging`` object: its previous content, the list of loggers, must now be inside the ``Dhcp6`` object. The Dhcp6 configuration starts with the ``"Dhcp6": {`` line and ends with the corresponding closing brace (in the above example, the brace after the last comment). Everything defined between those lines is considered to be the Dhcp6 configuration. In general, the order in which those parameters appear does not matter, but there are two caveats. The first one is to remember that the configuration file must be well-formed JSON. That means that the parameters for any given scope must be separated by a comma, and there must not be a comma after the last parameter. When reordering a configuration file, keep in mind that moving a parameter to or from the last position in a given scope may also require moving the comma. The second caveat is that it is uncommon — although legal JSON — to repeat the same parameter multiple times. If that happens, the last occurrence of a given parameter in a given scope is used, while all previous instances are ignored. This is unlikely to cause any confusion as there are no real-life reasons to keep multiple copies of the same parameter in the configuration file. The first few DHCPv6 configuration elements define some global parameters. ``valid-lifetime`` defines how long the addresses (leases) given out by the server are valid. If nothing changes, a client that got an address is allowed to use it for 4000 seconds. (Note that integer numbers are specified as is, without any quotes around them.) The address will become deprecated in 3000 seconds, i.e. clients are allowed to keep old connections, but can't use this address for creating new connections. ``renew-timer`` and ``rebind-timer`` are values (also in seconds) that define T1 and T2 timers that govern when the client will begin the renewal and rebind procedures. The ``interfaces-config`` map specifies the server configuration concerning the network interfaces on which the server should listen to the DHCP messages. The ``interfaces`` parameter specifies a list of network interfaces on which the server should listen. Lists are opened and closed with square brackets, with elements separated by commas. To listen on two interfaces, the ``interfaces-config`` should look like this: :: "interfaces-config": { "interfaces": [ "eth0", "eth1" ] }, The next couple of lines define the lease database, the place where the server stores its lease information. This particular example tells the server to use ``memfile``, which is the simplest (and fastest) database backend. It uses an in-memory database and stores leases on disk in a CSV (comma-separated values) file. This is a very simple configuration; usually the lease database configuration is more extensive and contains additional parameters. Note that ``lease-database`` is an object and opens up a new scope, using an opening brace. Its parameters (just one in this example: ``type``) follow. If there were more than one, they would be separated by commas. This scope is closed with a closing brace. As more parameters for the Dhcp6 definition follow, a trailing comma is present. Finally, we need to define a list of IPv6 subnets. This is the most important DHCPv6 configuration structure, as the server uses that information to process clients' requests. It defines all subnets from which the server is expected to receive DHCP requests. The subnets are specified with the ``subnet6`` parameter. It is a list, so it starts and ends with square brackets. Each subnet definition in the list has several attributes associated with it, so it is a structure and is opened and closed with braces. At a minimum, a subnet definition has to have at least two parameters: ``subnet`` (which defines the whole subnet) and ``pools`` (which is a list of dynamically allocated pools that are governed by the DHCP server). The example contains a single subnet. If more than one were defined, additional elements in the ``subnet6`` parameter would be specified and separated by commas. For example, to define two subnets, the following syntax would be used: :: "subnet6": [ { "pools": [ { "pool": "2001:db8:1::/112" } ], "subnet": "2001:db8:1::/64" }, { "pools": [ { "pool": "2001:db8:2::1-2001:db8:2::ffff" } ], "subnet": "2001:db8:2::/64" } ] Note that indentation is optional and is used for aesthetic purposes only. In some cases it may be preferable to use more compact notation. After all the parameters are specified, we have two contexts open: global and Dhcp6; thus, we need two closing curly brackets to close them. Lease Storage ------------- All leases issued by the server are stored in the lease database. Currently there are four database backends available: memfile (which is the default backend), MySQL, PostgreSQL, and Cassandra. Memfile - Basic Storage for Leases ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The server is able to store lease data in different repositories. Larger deployments may elect to store leases in a database. :ref:`database-configuration6` describes this option. In typical smaller deployments, though, the server will store lease information in a CSV file rather than a database. As well as requiring less administration, an advantage of using a file for storage is that it eliminates a dependency on third-party database software. The configuration of the file backend (memfile) is controlled through the Dhcp6/lease-database parameters. The ``type`` parameter is mandatory and it specifies which storage for leases the server should use. The value of ``"memfile"`` indicates that the file should be used as the storage. The following list gives additional optional parameters that can be used to configure the memfile backend. - ``persist``: controls whether the new leases and updates to existing leases are written to the file. It is strongly recommended that the value of this parameter be set to ``true`` at all times during the server's normal operation. Not writing leases to disk means that if a server is restarted (e.g. after a power failure), it will not know which addresses have been assigned. As a result, it may assign new clients addresses that are already in use. The value of ``false`` is mostly useful for performance-testing purposes. The default value of the ``persist`` parameter is ``true``, which enables writing lease updates to the lease file. - ``name``: specifies an absolute location of the lease file in which new leases and lease updates will be recorded. The default value for this parameter is ``"[kea-install-dir]/var/lib/kea/kea-leases6.csv"``. - ``lfc-interval``: specifies the interval, in seconds, at which the server will perform a lease file cleanup (LFC). This removes redundant (historical) information from the lease file and effectively reduces the lease file size. The cleanup process is described in more detail later in this section. The default value of the ``lfc-interval`` is ``3600``. A value of 0 disables the LFC. - ``max-row-errors``: when the server loads a lease file, it is processed row by row, each row containing a single lease. If a row is flawed and cannot be processed correctly the server will log it, discard the row, and go on to the next row. This parameter can be used to set a limit on the number of such discards that may occur after which the server will abandon the effort and exit. The default value of 0 disables the limit and allows the server to process the entire file, regardless of how many rows are discarded. An example configuration of the memfile backend is presented below: :: "Dhcp6": { "lease-database": { "type": "memfile", "persist": true, "name": "/tmp/kea-leases6.csv", "lfc-interval": 1800, "max-row-errors": 100 } } This configuration selects the ``/tmp/kea-leases6.csv`` as the storage for lease information and enables persistence (writing lease updates to this file). It also configures the backend to perform a periodic cleanup of the lease file every 30 minutes and sets the maximum number of row errors to 100. It is important to know how the lease file contents are organized to understand why the periodic lease file cleanup is needed. Every time the server updates a lease or creates a new lease for the client, the new lease information must be recorded in the lease file. For performance reasons, the server does not update the existing client's lease in the file, as this would potentially require rewriting the entire file. Instead, it simply appends the new lease information to the end of the file; the previous lease entries for the client are not removed. When the server loads leases from the lease file, e.g. at the server startup, it assumes that the latest lease entry for the client is the valid one. The previous entries are discarded, meaning that the server can re-construct the accurate information about the leases even though there may be many lease entries for each client. However, storing many entries for each client results in a bloated lease file and impairs the performance of the server's startup and reconfiguration, as it needs to process a larger number of lease entries. Lease file cleanup (LFC) removes all previous entries for each client and leaves only the latest ones. The interval at which the cleanup is performed is configurable, and it should be selected according to the frequency of lease renewals initiated by the clients. The more frequent the renewals, the smaller the value of ``lfc-interval`` should be. Note, however, that the LFC takes time and thus it is possible (although unlikely) that, if the ``lfc-interval`` is too short, a new cleanup may be started while the previous one is still running. The server would recover from this by skipping the new cleanup when it detected that the previous cleanup was still in progress. But it implies that the actual cleanups will be triggered more rarely than configured. Moreover, triggering a new cleanup adds overhead to the server, which will not be able to respond to new requests for a short period of time when the new cleanup process is spawned. Therefore, it is recommended that the ``lfc-interval`` value be selected in a way that allows the LFC to complete the cleanup before a new cleanup is triggered. Lease file cleanup is performed by a separate process (in the background) to avoid a performance impact on the server process. To avoid conflicts between two processes both using the same lease files, the LFC process starts with Kea opening a new lease file; the actual LFC process operates on the lease file that is no longer used by the server. There are also other files created as a side effect of the lease file cleanup. The detailed description of the LFC process is located later in this Kea Administrator's Reference Manual: :ref:`kea-lfc`. .. _database-configuration6: Lease Database Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. note:: Lease database access information must be configured for the DHCPv6 server, even if it has already been configured for the DHCPv4 server. The servers store their information independently, so each server can use a separate database or both servers can use the same database. .. note:: Kea requires the database timezone to match the system timezone. For more details, see :ref:`mysql-database-create` and :ref:`pgsql-database-create`. Lease database configuration is controlled through the Dhcp6/lease-database parameters. The database type must be set to "memfile", "mysql", "postgresql", or "cql", e.g.: :: "Dhcp6": { "lease-database": { "type": "mysql", ... }, ... } Next, the name of the database to hold the leases must be set; this is the name used when the database was created (see :ref:`mysql-database-create`, :ref:`pgsql-database-create`, or :ref:`cql-database-create`). :: "Dhcp6": { "lease-database": { "name": "database-name" , ... }, ... } For Cassandra: :: "Dhcp6": { "lease-database": { "keyspace": "database-name" , ... }, ... } If the database is located on a different system from the DHCPv6 server, the database host name must also be specified: :: "Dhcp6": { "lease-database": { "host": "remote-host-name", ... }, ... } (It should be noted that this configuration may have a severe impact on server performance.) For Cassandra, multiple contact points can be provided: :: "Dhcp6": { "lease-database": { "contact-points": "remote-host-name[, ...]" , ... }, ... } Normally, the database will be on the same machine as the DHCPv6 server. In this case, set the value to the empty string: :: "Dhcp6": { "lease-database": { "host" : "", ... }, ... } For Cassandra: :: "Dhcp6": { "lease-database": { "contact-points": "", ... }, ... } Should the database use a port other than the default, it may be specified as well: :: "Dhcp6": { "lease-database": { "port" : 12345, ... }, ... } Should the database be located on a different system, the administrator may need to specify a longer interval for the connection timeout: :: "Dhcp6": { "lease-database": { "connect-timeout" : timeout-in-seconds, ... }, ... } The default value of five seconds should be more than adequate for local connections. If a timeout is given, though, it should be an integer greater than zero. The maximum number of times the server will automatically attempt to reconnect to the lease database after connectivity has been lost may be specified: :: "Dhcp6": { "lease-database": { "max-reconnect-tries" : number-of-tries, ... }, ... } If the server is unable to reconnect to the database after making the maximum number of attempts, the server will exit. A value of zero (the default) disables automatic recovery and the server will exit immediately upon detecting a loss of connectivity (MySQL and PostgreSQL only). The number of milliseconds the server will wait between attempts to reconnect to the lease database after connectivity has been lost may also be specified: :: "Dhcp6": { "lease-database": { "reconnect-wait-time" : number-of-milliseconds, ... }, ... } The default value for MySQL and PostgreSQL is 0, which disables automatic recovery and causes the server to exit immediately upon detecting the loss of connectivity. The default value for Cassandra is 2000 ms. :: "Dhcp6": { "lease-database": { "on-fail" : "stop-retry-exit", ... }, ... } The possible values are: - ``stop-retry-exit`` disables the DHCP service while trying to automatically recover lost connections. Shuts down the server on failure after exhausting ``max-reconnect-tries``. This is the default value for MySQL and PostgreSQL. - ``serve-retry-exit`` DHCP service continues while trying to automatically recover lost connections. Shuts down the server on failure after exhausting ``max-reconnect-tries``. - ``serve-retry-continue`` DHCP service continues and does not shut down the server even if the recovery fails. .. note:: Automatic reconnection to database backends is configured individually per backend. This allows users to tailor the recovery parameters to each backend they use. We do suggest that users enable it either for all backends or none, so behavior is consistent. Losing connectivity to a backend for which reconnect is disabled will result (if configured) in the server shutting itself down. This includes cases when the lease database backend and the hosts database backend are connected to the same database instance. It is highly recommended to not change the ``stop-retry-exit`` default setting for the lease manager as it is critical for the connection to be active while processing DHCP traffic. Change this only if the server is used exclusively as a configuration tool. .. .. note:: Note that the host parameter is used by the MySQL and PostgreSQL backends. Cassandra has a concept of contact points that can be used to contact the cluster, instead of a single IP or hostname. It takes a list of comma-separated IP addresses, which may be specified as: :: "Dhcp6": { "lease-database": { "contact-points" : "192.0.2.1,192.0.2.2", ... }, ... } Finally, the credentials of the account under which the server will access the database should be set: :: "Dhcp6": { "lease-database": { "user": "user-name", "password": "password", ... }, ... } If there is no password to the account, set the password to the empty string "". (This is also the default.) .. _cassandra-database-configuration6: Cassandra-Specific Parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The parameters are the same for both DHCPv4 and DHCPv6. See :ref:`cassandra-database-configuration4` for details. .. _hosts6-storage: Hosts Storage ------------- Kea is also able to store information about host reservations in the database. The hosts database configuration uses the same syntax as the lease database. In fact, a Kea server opens independent connections for each purpose, be it lease or hosts information. This arrangement gives the most flexibility. Kea can keep leases and host reservations separately, but can also point to the same database. Currently the supported hosts database types are MySQL, PostgreSQL, and Cassandra. For example, the following configuration can be used to configure a connection to MySQL: :: "Dhcp6": { "hosts-database": { "type": "mysql", "name": "kea", "user": "kea", "password": "secret123", "host": "localhost", "port": 3306 } } Note that depending on the database configuration, many of the parameters may be optional. Please note that usage of hosts storage is optional. A user can define all host reservations in the configuration file, and that is the recommended way if the number of reservations is small. However, when the number of reservations grows, it is more convenient to use host storage. Please note that both storage methods (configuration file and one of the supported databases) can be used together. If hosts are defined in both places, the definitions from the configuration file are checked first and external storage is checked later, if necessary. In fact, host information can be placed in multiple stores. Operations are performed on the stores in the order they are defined in the configuration file, although this leads to a restriction in ordering in the case of a host reservation addition; read-only stores must be configured after a (required) read-write store, or the addition will fail. .. note:: Kea requires the database timezone to match the system timezone. For more details, see :ref:`mysql-database-create` and :ref:`pgsql-database-create`. .. _hosts-databases-configuration6: DHCPv6 Hosts Database Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Hosts database configuration is controlled through the Dhcp6/hosts-database parameters. If enabled, the type of database must be set to "mysql" or "postgresql". :: "Dhcp6": { "hosts-database": { "type": "mysql", ... }, ... } Next, the name of the database to hold the reservations must be set; this is the name used when the lease database was created (see :ref:`supported-databases` for instructions on how to set up the desired database type): :: "Dhcp6": { "hosts-database": { "name": "database-name" , ... }, ... } If the database is located on a different system than the DHCPv6 server, the database host name must also be specified: :: "Dhcp6": { "hosts-database": { "host": remote-host-name, ... }, ... } (Again, it should be noted that this configuration may have a severe impact on server performance.) Normally, the database will be on the same machine as the DHCPv6 server. In this case, set the value to the empty string: :: "Dhcp6": { "hosts-database": { "host" : "", ... }, ... } :: "Dhcp6": { "hosts-database": { "port" : 12345, ... }, ... } The maximum number of times the server will automatically attempt to reconnect to the host database after connectivity has been lost may be specified: :: "Dhcp6": { "hosts-database": { "max-reconnect-tries" : number-of-tries, ... }, ... } If the server is unable to reconnect to the database after making the maximum number of attempts, the server will exit. A value of zero (the default) disables automatic recovery and the server will exit immediately upon detecting a loss of connectivity (MySQL and PostgreSQL only). For Cassandra, Kea uses a Cassandra interface that connects to all nodes in a cluster at the same time. Any connectivity issues should be handled by internal Cassandra mechanisms. The number of milliseconds the server will wait between attempts to reconnect to the host database after connectivity has been lost may also be specified: :: "Dhcp6": { "hosts-database": { "reconnect-wait-time" : number-of-milliseconds, ... }, ... } The default value for MySQL and PostgreSQL is 0, which disables automatic recovery and causes the server to exit immediately upon detecting the loss of connectivity. The default value for Cassandra is 2000 ms. :: "Dhcp6": { "hosts-database": { "on-fail" : "stop-retry-exit", ... }, ... } The possible values are: - ``stop-retry-exit`` disables the DHCP service while trying to automatically recover lost connections. Shuts down the server on failure after exhausting ``max-reconnect-tries``. This is the default value for MySQL and PostgreSQL. - ``serve-retry-exit`` DHCP service continues while trying to automatically recover lost connections. Shuts down the server on failure after exhausting ``max-reconnect-tries``. - ``serve-retry-continue`` DHCP service continues and does not shut down the server even if the recovery fails. .. note:: Automatic reconnection to database backends is configured individually per backend. This allows users to tailor the recovery parameters to each backend they use. We do suggest that users enable it either for all backends or none, so behavior is consistent. Losing connectivity to a backend for which reconnect is disabled will result (if configured) in the server shutting itself down. This includes cases when the lease database backend and the hosts database backend are connected to the same database instance. Finally, the credentials of the account under which the server will access the database should be set: :: "Dhcp6": { "hosts-database": { "user": "user-name", "password": "password", ... }, ... } If there is no password to the account, set the password to the empty string "". (This is also the default.) The multiple storage extension uses a similar syntax; a configuration is placed into a "hosts-databases" list instead of into a "hosts-database" entry, as in: :: "Dhcp6": { "hosts-databases": [ { "type": "mysql", ... }, ... ], ... } For additional Cassandra-specific parameters, see :ref:`cassandra-database-configuration4`. If the same host is configured both in-file and in-database, Kea does not issue a warning, as it would if both were specified in the same data source. Instead, the host configured in-file has priority over the one configured in-database. .. _read-only-database-configuration6: Using Read-Only Databases for Host Reservations with DHCPv6 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In some deployments the database user whose name is specified in the database backend configuration may not have write privileges to the database. This is often required by the policy within a given network to secure the data from being unintentionally modified. In many cases administrators have deployed inventory databases, which contain substantially more information about the hosts than just the static reservations assigned to them. The inventory database can be used to create a view of a Kea hosts database and such a view is often read-only. Kea host database backends operate with an implicit configuration to both read from and write to the database. If the database user does not have write access to the host database, the backend will fail to start and the server will refuse to start (or reconfigure). However, if access to a read-only host database is required for retrieving reservations for clients and/or assigning specific addresses and options, it is possible to explicitly configure Kea to start in "read-only" mode. This is controlled by the ``readonly`` boolean parameter as follows: :: "Dhcp6": { "hosts-database": { "readonly": true, ... }, ... } Setting this parameter to ``false`` configures the database backend to operate in "read-write" mode, which is also the default configuration if the parameter is not specified. .. note:: The ``readonly`` parameter is currently only supported for MySQL and PostgreSQL databases. .. _dhcp6-interface-configuration: Interface Configuration ----------------------- The DHCPv6 server must be configured to listen on specific network interfaces. The simplest network interface configuration tells the server to listen on all available interfaces: :: "Dhcp6": { "interfaces-config": { "interfaces": [ "*" ] } ... } The asterisk plays the role of a wildcard and means "listen on all interfaces." However, it is usually a good idea to explicitly specify interface names: :: "Dhcp6": { "interfaces-config": { "interfaces": [ "eth1", "eth3" ] }, ... } It is possible to use a wildcard interface name (asterisk) concurrently with explicit interface names: :: "Dhcp6": { "interfaces-config": { "interfaces": [ "eth1", "eth3", "*" ] }, ... } It is anticipated that this form of usage will only be used when it is desired to temporarily override a list of interface names and listen on all interfaces. As with the DHCPv4 server, binding to specific addresses and disabling re-detection of interfaces are supported. But ``dhcp-socket-type`` is not supported, because DHCPv6 uses UDP/IPv6 sockets only. The following example shows how to disable the interface detection: :: "Dhcp6": { "interfaces-config": { "interfaces": [ "eth1", "eth3" ], "re-detect": false }, ... } The loopback interfaces (i.e. the "lo" or "lo0" interface) are not configured by default, unless explicitly mentioned in the configuration. Note that Kea requires a link-local address (which does not exist on all systems) or a specified unicast address, as in: :: "Dhcp6": { "interfaces-config": { "interfaces": [ "enp0s2/2001:db8::1234:abcd" ] }, ... } .. _ipv6-subnet-id: IPv6 Subnet Identifier ---------------------- The subnet identifier is a unique number associated with a particular subnet. In principle, it is used to associate clients' leases with their respective subnets. When a subnet identifier is not specified for a subnet being configured, it will be automatically assigned by the configuration mechanism. The identifiers are assigned from 1 and are monotonically increased for each subsequent subnet: 1, 2, 3 .... If there are multiple subnets configured with auto-generated identifiers and one of them is removed, the subnet identifiers may be renumbered. For example: if there are four subnets and the third is removed, the last subnet will be assigned the identifier that the third subnet had before removal. As a result, the leases stored in the lease database for subnet 3 are now associated with subnet 4, something that may have unexpected consequences. The only remedy for this issue at present is to manually specify a unique identifier for each subnet. .. note:: Subnet IDs must be greater than zero and less than 4294967295. The following configuration will assign the specified subnet identifier to a newly configured subnet: :: "Dhcp6": { "subnet6": [ { "subnet": "2001:db8:1::/64", "id": 1024, ... } ] } This identifier will not change for this subnet unless the "id" parameter is removed or set to 0. The value of 0 forces auto-generation of the subnet identifier. .. _ipv6-subnet-prefix: IPv6 Subnet Prefix ------------------ The subnet prefix is the second way to identify a subnet. It does not need to have the address part to match the prefix length, for instance this configuration is accepted: :: "Dhcp6": { "subnet6": [ { "subnet": "2001:db8:1::1/64", ... } ] } Even there is another subnet with the "2001:db8:1::/64" prefix: only the textual form of subnets are compared to avoid duplicates. .. note:: Abuse of this feature can lead to incorrect subnet selection (see :ref:`dhcp6-config-subnets`). .. _dhcp6-unicast: Unicast Traffic Support ----------------------- When the DHCPv6 server starts, by default it listens to the DHCP traffic sent to multicast address ff02::1:2 on each interface that it is configured to listen on (see :ref:`dhcp6-interface-configuration`). In some cases it is useful to configure a server to handle incoming traffic sent to global unicast addresses as well; the most common reason for this is to have relays send their traffic to the server directly. To configure the server to listen on a specific unicast address, add a slash after the interface name, followed by the global unicast address on which the server should listen. The server will listen to this address in addition to normal link-local binding and listening on the ff02::1:2 address. The sample configuration below shows how to listen on 2001:db8::1 (a global address) configured on the eth1 interface. :: "Dhcp6": { "interfaces-config": { "interfaces": [ "eth1/2001:db8::1" ] }, ... "option-data": [ { "name": "unicast", "data": "2001:db8::1" } ], ... } This configuration will cause the server to listen on eth1 on the link-local address, the multicast group (ff02::1:2), and 2001:db8::1. Usually unicast support is associated with a server unicast option which allows clients to send unicast messages to the server. The example above includes a server unicast option specification which will cause the client to send messages to the specified unicast address. It is possible to mix interface names, wildcards, and interface names/addresses in the list of interfaces. It is not possible, however, to specify more than one unicast address on a given interface. Care should be taken to specify proper unicast addresses. The server will attempt to bind to the addresses specified without any additional checks. This approach was selected on purpose, to allow the software to communicate over uncommon addresses if so desired. .. _dhcp6-address-config: Configuration of IPv6 Address Pools ----------------------------------- The main role of a DHCPv6 server is address assignment. For this, the server must be configured with at least one subnet and one pool of dynamic addresses to be managed. For example, assume that the server is connected to a network segment that uses the 2001:db8:1::/64 prefix. The administrator of that network decides that addresses from range 2001:db8:1::1 to 2001:db8:1::ffff are going to be managed by the Dhcp6 server. Such a configuration can be achieved in the following way: :: "Dhcp6": { "subnet6": [ { "subnet": "2001:db8:1::/64", "pools": [ { "pool": "2001:db8:1::1-2001:db8:1::ffff" } ], ... } ] } Note that ``subnet`` is defined as a simple string, but the ``pools`` parameter is actually a list of pools; for this reason, the pool definition is enclosed in square brackets, even though only one range of addresses is specified. Each ``pool`` is a structure that contains the parameters that describe a single pool. Currently there is only one parameter, ``pool``, which gives the range of addresses in the pool. It is possible to define more than one pool in a subnet; continuing the previous example, further assume that 2001:db8:1:0:5::/80 should also be managed by the server. It could be written as 2001:db8:1:0:5:: to 2001:db8:1::5:ffff:ffff:ffff, but typing so many 'f's is cumbersome. It can be expressed more simply as 2001:db8:1:0:5::/80. Both formats are supported by Dhcp6 and can be mixed in the pool list. For example, one could define the following pools: :: "Dhcp6": { "subnet6": [ { "subnet": "2001:db8:1::/64", "pools": [ { "pool": "2001:db8:1::1-2001:db8:1::ffff" }, { "pool": "2001:db8:1:05::/80" } ], ... } ] } White space in pool definitions is ignored, so spaces before and after the hyphen are optional. They can be used to improve readability. The number of pools is not limited, but for performance reasons it is recommended to use as few as possible. The server may be configured to serve more than one subnet. To add a second subnet, use a command similar to the following: :: "Dhcp6": { "subnet6": [ { "subnet": "2001:db8:1::/64", "pools": [ { "pool": "2001:db8:1::1-2001:db8:1::ffff" } ] }, { "subnet": "2001:db8:2::/64", "pools": [ { "pool": "2001:db8:2::/64" } ] }, ... ] } In this example, we allow the server to dynamically assign all addresses available in the whole subnet. Although rather wasteful, it is certainly a valid configuration to dedicate the whole /64 subnet for that purpose. Note that the Kea server does not preallocate the leases, so there is no danger in using gigantic address pools. When configuring a DHCPv6 server using prefix/length notation, please pay attention to the boundary values. When specifying that the server can use a given pool, it will also be able to allocate the first (typically a network address) address from that pool. For example, for pool 2001:db8:2::/64, the 2001:db8:2:: address may be assigned as well. To avoid this, use the "min-max" notation. .. _dhcp6-prefix-config: Subnet and Prefix Delegation Pools ---------------------------------- Subnets may also be configured to delegate prefixes, as defined in `RFC 8415 `__, section 6.3. A subnet may have one or more prefix delegation pools. Each pool has a prefixed address, which is specified as a prefix (``prefix``) and a prefix length (``prefix-len``), as well as a delegated prefix length (``delegated-len``). The delegated length must not be shorter than (that is, it must be numerically greater than or equal to) the prefix length. If both the delegated and prefix lengths are equal, the server will be able to delegate only one prefix. The delegated prefix does not have to match the subnet prefix. Below is a sample subnet configuration which enables prefix delegation for the subnet: :: "Dhcp6": { "subnet6": [ { "subnet": "2001:d8b:1::/64", "pd-pools": [ { "prefix": "3000:1::", "prefix-len": 64, "delegated-len": 96 } ] } ], ... } .. _pd-exclude-option: Prefix Exclude Option --------------------- For each delegated prefix, the delegating router may choose to exclude a single prefix out of the delegated prefix as specified in `RFC 6603 `__. The requesting router must not assign the excluded prefix to any of its downstream interfaces, and it is intended to be used on a link through which the delegating router exchanges DHCPv6 messages with the requesting router. The configuration example below demonstrates how to specify an excluded prefix within a prefix pool definition. The excluded prefix "2001:db8:1:8000:cafe:80::/72" will be sent to a requesting router which includes the Prefix Exclude option in the Option Request option (ORO), and which is delegated a prefix from this pool. :: "Dhcp6": { "subnet6": [ { "subnet": "2001:db8:1::/48", "pd-pools": [ { "prefix": "2001:db8:1:8000::", "prefix-len": 48, "delegated-len": 64, "excluded-prefix": "2001:db8:1:8000:cafe:80::", "excluded-prefix-len": 72 } ] } ] } .. _dhcp6-std-options: Standard DHCPv6 Options ----------------------- One of the major features of the DHCPv6 server is the ability to provide configuration options to clients. Although there are several options that require special behavior, most options are sent by the server only if the client explicitly requests them. The following example shows how to configure the addresses of DNS servers, one of the most frequently used options. Options specified in this way are considered global and apply to all configured subnets. :: "Dhcp6": { "option-data": [ { "name": "dns-servers", "code": 23, "space": "dhcp6", "csv-format": true, "data": "2001:db8::cafe, 2001:db8::babe" }, ... ] } The ``option-data`` line creates a new entry in the option-data table. This table contains information on all global options that the server is supposed to configure in all subnets. The ``name`` line specifies the option name. (For a complete list of currently supported names, see :ref:`dhcp6-std-options-list`.) The next line specifies the option code, which must match one of the values from that list. The line beginning with ``space`` specifies the option space, which must always be set to "dhcp6" as these are standard DHCPv6 options. For other name spaces, including custom option spaces, see :ref:`dhcp6-option-spaces`. The following line specifies the format in which the data will be entered; use of CSV (comma-separated values) is recommended. Finally, the ``data`` line gives the actual value to be sent to clients. The data parameter is specified as normal text, with values separated by commas if more than one value is allowed. Options can also be configured as hexadecimal values. If "csv-format" is set to false, the option data must be specified as a hexadecimal string. The following commands configure the DNS-SERVERS option for all subnets with the following addresses: 2001:db8:1::cafe and 2001:db8:1::babe. :: "Dhcp6": { "option-data": [ { "name": "dns-servers", "code": 23, "space": "dhcp6", "csv-format": false, "data": "20 01 0D B8 00 01 00 00 00 00 00 00 00 00 CA FE 20 01 0D B8 00 01 00 00 00 00 00 00 00 00 BA BE" }, ... ] } .. .. note:: The value for the setting of the "data" element is split across two lines in this example for clarity; when entering the command, the whole string should be entered on the same line. Kea supports the following formats when specifying hexadecimal data: - ``Delimited octets`` - one or more octets separated by either colons or spaces (':' or ' '). While each octet may contain one or two digits, we strongly recommend always using two digits. Valid examples are "ab:cd:ef" and "ab cd ef". - ``String of digits`` - a continuous string of hexadecimal digits with or without a "0x" prefix. Valid examples are "0xabcdef" and "abcdef". Care should be taken to use proper encoding when using hexadecimal format; Kea's ability to validate data correctness in hexadecimal is limited. As of Kea 1.6.0, it is also possible to specify data for binary options as a single-quoted text string within double quotes as shown (note that ``csv-format`` must be set to false): :: "Dhcp6": { "option-data": [ { "name": "subscriber-id", "code": 38, "space": "dhcp6", "csv-format": false, "data": "'convert this text to binary'" }, ... ], ... } Most of the parameters in the "option-data" structure are optional and can be omitted in some circumstances, as discussed in :ref:`dhcp6-option-data-defaults`. Only one of name or code is required; it is not necessary to specify both. Space has a default value of "dhcp6", so this can be skipped as well if a regular (not encapsulated) DHCPv6 option is defined. Finally, csv-format defaults to "true", so it too can be skipped, unless the option value is specified as hexstring. Therefore, the above example can be simplified to: :: "Dhcp6": { "option-data": [ { "name": "dns-servers", "data": "2001:db8::cafe, 2001:db8::babe" }, ... ] } Defined options are added to the response when the client requests them, as well as any options required by a protocol. An administrator can also specify that an option is always sent, even if a client did not specifically request it. To enforce the addition of a particular option, set the "always-send" flag to true as in: :: "Dhcp6": { "option-data": [ { "name": "dns-servers", "data": "2001:db8::cafe, 2001:db8::babe", "always-send": true }, ... ] } The effect is the same as if the client added the option code in the Option Request option (or its equivalent for vendor options), as in: :: "Dhcp6": { "option-data": [ { "name": "dns-servers", "data": "2001:db8::cafe, 2001:db8::babe", "always-send": true }, ... ], "subnet6": [ { "subnet": "2001:db8:1::/64", "option-data": [ { "name": "dns-servers", "data": "2001:db8:1::cafe, 2001:db8:1::babe" }, ... ], ... }, ... ], ... } The DNS servers option is always added to responses (the always-send is "sticky"), but the value is the subnet one when the client is localized in the subnet. It is possible to override options on a per-subnet basis. If clients connected to most subnets are expected to get the same values of a given option, administrators should use global options; it is possible to override specific values for a small number of subnets. On the other hand, if different values are used in each subnet, it does not make sense to specify global option values; rather, only subnet-specific ones should be set. The following commands override the global DNS servers option for a particular subnet, setting a single DNS server with address 2001:db8:1::3. :: "Dhcp6": { "subnet6": [ { "option-data": [ { "name": "dns-servers", "code": 23, "space": "dhcp6", "csv-format": true, "data": "2001:db8:1::3" }, ... ], ... }, ... ], ... } In some cases it is useful to associate some options with an address or prefix pool from which a client is assigned a lease. Pool-specific option values override subnet-specific and global option values. If the client is assigned multiple leases from different pools, the server will assign options from all pools from which the leases have been obtained. However, if the particular option is specified in multiple pools from which the client obtains the leases, only one instance of this option will be handed out to the client. The server's administrator must not try to prioritize assignment of pool-specific options by trying to order pools declarations in the server configuration. The following configuration snippet demonstrates how to specify the DNS servers option, which will be assigned to a client only if the client obtains an address from the given pool: :: "Dhcp6": { "subnet6": [ { "pools": [ { "pool": "2001:db8:1::100-2001:db8:1::300", "option-data": [ { "name": "dns-servers", "data": "2001:db8:1::10" } ] } ] }, ... ], ... } Options can also be specified in class or host reservation scope. The current Kea options precedence order is (from most important): host reservation, pool, subnet, shared network, class, global. The currently supported standard DHCPv6 options are listed in :ref:`dhcp6-std-options-list`. "Name" and "Code" are the values that should be used as a name/code in the option-data structures. "Type" designates the format of the data; the meanings of the various types are given in :ref:`dhcp-types`. When a data field is a string and that string contains the comma (,; U+002C) character, the comma must be escaped with two backslashes (\; U+005C). This double escape is required because both the routine splitting CSV data into fields and JSON use the same escape character; a single escape (\,) would make the JSON invalid. For example, the string "EST5EDT4,M3.2.0/02:00,M11.1.0/02:00" must be represented as: :: "Dhcp6": { "subnet6": [ { "pools": [ { "option-data": [ { "name": "new-posix-timezone", "data": "EST5EDT4\\,M3.2.0/02:00\\,M11.1.0/02:00" } ] }, ... ], ... }, ... ], ... } Some options are designated as arrays, which means that more than one value is allowed in such an option. For example, the option dns-servers allows the specification of more than one IPv6 address, enabling clients to obtain the addresses of multiple DNS servers. :ref:`dhcp6-custom-options` describes the configuration syntax to create custom option definitions (formats). Creation of custom definitions for standard options is generally not permitted, even if the definition being created matches the actual option format defined in the RFCs. There is an exception to this rule for standard options for which Kea currently does not provide a definition. In order to use such options, a server administrator must create a definition as described in :ref:`dhcp6-custom-options` in the 'dhcp6' option space. This definition should match the option format described in the relevant RFC, but the configuration mechanism will allow any option format as it currently has no means to validate it. .. _dhcp6-std-options-list: .. table:: List of Standard DHCPv6 Options configurable by an administrator +--------------------------+-----------------+-----------------+-----------------+ | Name | Code | Type | Array? | +==========================+=================+=================+=================+ | preference | 7 | uint8 | false | +--------------------------+-----------------+-----------------+-----------------+ | unicast | 12 | ipv6-address | false | +--------------------------+-----------------+-----------------+-----------------+ | sip-server-dns | 21 | fqdn | true | +--------------------------+-----------------+-----------------+-----------------+ | sip-server-addr | 22 | ipv6-address | true | +--------------------------+-----------------+-----------------+-----------------+ | dns-servers | 23 | ipv6-address | true | +--------------------------+-----------------+-----------------+-----------------+ | domain-search | 24 | fqdn | true | +--------------------------+-----------------+-----------------+-----------------+ | nis-servers | 27 | ipv6-address | true | +--------------------------+-----------------+-----------------+-----------------+ | nisp-servers | 28 | ipv6-address | true | +--------------------------+-----------------+-----------------+-----------------+ | nis-domain-name | 29 | fqdn | true | +--------------------------+-----------------+-----------------+-----------------+ | nisp-domain-name | 30 | fqdn | true | +--------------------------+-----------------+-----------------+-----------------+ | sntp-servers | 31 | ipv6-address | true | +--------------------------+-----------------+-----------------+-----------------+ | information-refresh-time | 32 | uint32 | false | +--------------------------+-----------------+-----------------+-----------------+ | bcmcs-server-dns | 33 | fqdn | true | +--------------------------+-----------------+-----------------+-----------------+ | bcmcs-server-addr | 34 | ipv6-address | true | +--------------------------+-----------------+-----------------+-----------------+ | geoconf-civic | 36 | record (uint8, | false | | | | uint16, binary) | | +--------------------------+-----------------+-----------------+-----------------+ | remote-id | 37 | record (uint32, | false | | | | binary) | | +--------------------------+-----------------+-----------------+-----------------+ | subscriber-id | 38 | binary | false | +--------------------------+-----------------+-----------------+-----------------+ | client-fqdn | 39 | record (uint8, | false | | | | fqdn) | | +--------------------------+-----------------+-----------------+-----------------+ | pana-agent | 40 | ipv6-address | true | +--------------------------+-----------------+-----------------+-----------------+ | new-posix-timezone | 41 | string | false | +--------------------------+-----------------+-----------------+-----------------+ | new-tzdb-timezone | 42 | string | false | +--------------------------+-----------------+-----------------+-----------------+ | ero | 43 | uint16 | true | +--------------------------+-----------------+-----------------+-----------------+ | lq-query (1) | 44 | record (uint8, | false | | | | ipv6-address) | | +--------------------------+-----------------+-----------------+-----------------+ | client-data (1) | 45 | empty | false | +--------------------------+-----------------+-----------------+-----------------+ | clt-time (1) | 46 | uint32 | false | +--------------------------+-----------------+-----------------+-----------------+ | lq-relay-data (1) | 47 | record | false | | | | (ipv6-address, | | | | | binary) | | +--------------------------+-----------------+-----------------+-----------------+ | lq-client-link (1) | 48 | ipv6-address | true | +--------------------------+-----------------+-----------------+-----------------+ | v6-lost | 51 | fqdn | false | +--------------------------+-----------------+-----------------+-----------------+ | capwap-ac-v6 | 52 | ipv6-address | true | +--------------------------+-----------------+-----------------+-----------------+ | relay-id | 53 | binary | false | +--------------------------+-----------------+-----------------+-----------------+ | v6-access-domain | 57 | fqdn | false | +--------------------------+-----------------+-----------------+-----------------+ | sip-ua-cs-list | 58 | fqdn | true | +--------------------------+-----------------+-----------------+-----------------+ | bootfile-url | 59 | string | false | +--------------------------+-----------------+-----------------+-----------------+ | bootfile-param | 60 | tuple | true | +--------------------------+-----------------+-----------------+-----------------+ | client-arch-type | 61 | uint16 | true | +--------------------------+-----------------+-----------------+-----------------+ | nii | 62 | record (uint8, | false | | | | uint8, uint8) | | +--------------------------+-----------------+-----------------+-----------------+ | aftr-name | 64 | fqdn | false | +--------------------------+-----------------+-----------------+-----------------+ | erp-local-domain-name | 65 | fqdn | false | +--------------------------+-----------------+-----------------+-----------------+ | rsoo | 66 | empty | false | +--------------------------+-----------------+-----------------+-----------------+ | pd-exclude | 67 | binary | false | +--------------------------+-----------------+-----------------+-----------------+ | rdnss-selection | 74 | record | true | | | | (ipv6-address, | | | | | uint8, fqdn) | | +--------------------------+-----------------+-----------------+-----------------+ | client-linklayer-addr | 79 | binary | false | +--------------------------+-----------------+-----------------+-----------------+ | link-address | 80 | ipv6-address | false | +--------------------------+-----------------+-----------------+-----------------+ | solmax-rt | 82 | uint32 | false | +--------------------------+-----------------+-----------------+-----------------+ | inf-max-rt | 83 | uint32 | false | +--------------------------+-----------------+-----------------+-----------------+ | dhcp4o6-server-addr | 88 | ipv6-address | true | +--------------------------+-----------------+-----------------+-----------------+ | s46-rule | 89 | record (uint8, | false | | | | uint8, uint8, | | | | | ipv4-address, | | | | | ipv6-prefix) | | +--------------------------+-----------------+-----------------+-----------------+ | s46-br | 90 | ipv6-address | false | +--------------------------+-----------------+-----------------+-----------------+ | s46-dmr | 91 | ipv6-prefix | false | +--------------------------+-----------------+-----------------+-----------------+ | s46-v4v6bind | 92 | record | false | | | | (ipv4-address, | | | | | ipv6-prefix) | | +--------------------------+-----------------+-----------------+-----------------+ | s46-portparams | 93 | record(uint8, | false | | | | psid) | | +--------------------------+-----------------+-----------------+-----------------+ | s46-cont-mape | 94 | empty | false | +--------------------------+-----------------+-----------------+-----------------+ | s46-cont-mapt | 95 | empty | false | +--------------------------+-----------------+-----------------+-----------------+ | s46-cont-lw | 96 | empty | false | +--------------------------+-----------------+-----------------+-----------------+ | v6-captive-portal | 103 | string | false | +--------------------------+-----------------+-----------------+-----------------+ | ipv6-address-andsf | 143 | ipv6-address | true | +--------------------------+-----------------+-----------------+-----------------+ Options marked with (1) have option definitions, but the logic behind them is not implemented. That means that, technically, Kea knows how to parse them in incoming messages or how to send them if configured to do so, but not what to do with them. Since the related RFCs require certain processing, the support for those options is non-functional. However, it may be useful in some limited lab testing; hence the definition formats are listed here. Kea supports more options than the listed above. The following list is mostly useful for readers who want to understand whether Kea is able to support certain options. The following options are returned by the Kea engine itself and in general should not be configured manually. .. table:: List of standard DHCPv6 options managed by Kea on its own and not directly configurable by an administrator +--------------+------+------------------------------------------------------------------------+ | Name | Code | Description | +==============+======+========================================================================+ | client-id | 1 | sent by the client and Kea uses it to distinguish between clients. | +--------------+------+------------------------------------------------------------------------+ | server-id | 2 | sent by clients to request action from a specific server and by the | | | | server to identify itself. See :ref:`dhcp6-serverid` for details. | +--------------+------+------------------------------------------------------------------------+ | ia-na | 3 | a container option that conveys IPv6 addresses (``iaddr`` options). Kea| | | | receives and sends those options using its allocation engine. | +--------------+------+------------------------------------------------------------------------+ | ia-ta | 4 | conveys temporary addresses. Deprecated feature, not supported. | +--------------+------+------------------------------------------------------------------------+ | iaaddr | 5 | conveys addresses with lifetimes in ``ia-na`` and ``ia-ta`` options. | +--------------+------+------------------------------------------------------------------------+ | oro | 6 | ORO (or Option Request Option) is used by the clients to request a list| | | | of options they are interested in. Kea supports it and will send the | | | | requested options back if configured with required options. | +--------------+------+------------------------------------------------------------------------+ | elapsed-time | 8 | sent by the clients to identify how long they're trying to obtain a | | | | configuration. Kea uses high values sent by clients as an indicator | | | | that something is wrong and this is one of the aspects used in HA to | | | | determine if the partner is healthy or not. | +--------------+------+------------------------------------------------------------------------+ | relay-msg | 9 | used by relays to encapsulate the original client message. Kea uses it | | | | when sending back relayed responses to the relay agent. | +--------------+------+------------------------------------------------------------------------+ | auth | 10 | used to pass authentication information between clients and server. The| | | | support for this option is very limited. | +--------------+------+------------------------------------------------------------------------+ | status-code | 13 | an option that the server can attach in case of various failures, such | | | | as running out of addresses or not being configured to assign prefixes.| +--------------+------+------------------------------------------------------------------------+ | rapid-commit | 14 | used to signal client's willingness to support ``rapid-commit`` and | | | | server's acceptance for this configuration. See | | | | :ref:`dhcp6-rapid-commit` for details. | +--------------+------+------------------------------------------------------------------------+ | user-class | 15 | sent by the client to self-identify what kind of device type it is. Kea| | | | can use this for client classification. | +--------------+------+------------------------------------------------------------------------+ | vendor-class | 16 | similar to ``user-class``, but it is vendor specific. | +--------------+------+------------------------------------------------------------------------+ | vendor-opts | 17 | a vendor specific container that is used by both the client and the | | | | server to exchange vendor specific options. The logic behind those | | | | options vary between vendors. The vendor options are explained in | | | | :ref:`dhcp6-vendor-opts`. | +--------------+------+------------------------------------------------------------------------+ | interface-id | 18 | may be inserted by the relay agent to identify the interface that the | | | | original client message was received on. Kea may be told to use this | | | | information to select specific subnets. Also, if specified, Kea will | | | | echo this option back, so the relay will know which interface to use to| | | | reach the client. | +--------------+------+------------------------------------------------------------------------+ | ia-pd | 25 | a container for conveying PDs (Prefix Delegation) that are being | | | | delegated to clients. See :ref:`dhcp6-prefix-config` for details. | +--------------+------+------------------------------------------------------------------------+ | iaprefix | 26 | conveys IPv6 prefix in ``ia-pd`` option. See :ref:`dhcp6-prefix-config`| | | | for details. | +--------------+------+------------------------------------------------------------------------+ .. _s46-options: Common Softwire46 Options ------------------------- Softwire46 options are involved in IPv4 over IPv6 provisioning by means of tunneling or translation as specified in `RFC 7598 `__. The following sections provide configuration examples of these options. .. _s46-containers: Softwire46 Container Options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Softwire46 (S46) container options group rules and optional port parameters for a specified domain. There are three container options specified in the "dhcp6" (top-level) option space: the MAP-E Container option, the MAP-T Container option, and the S46 Lightweight 4over6 Container option. These options only contain the encapsulated options specified below; they do not include any data fields. To configure the server to send a specific container option along with all encapsulated options, the container option must be included in the server configuration as shown below: :: "Dhcp6": { ... "option-data": [ { "name": "s46-cont-mape" } ], ... } This configuration will cause the server to include the MAP-E Container option to the client. Use "s46-cont-mapt" or "s46-cont-lw" for the MAP-T Container and S46 Lightweight 4over6 Container options, respectively. All remaining Softwire options described below are included in one of the container options. Thus, they must be included in appropriate option spaces by selecting a "space" name, which specifies in which option they are supposed to be included. S46 Rule Option ~~~~~~~~~~~~~~~ The S46 Rule option is used for conveying the Basic Mapping Rule (BMR) and Forwarding Mapping Rule (FMR). :: { "space": "s46-cont-mape-options", "name": "s46-rule", "data": "128, 0, 24, 192.0.2.0, 2001:db8:1::/64" } Another possible "space" value is "s46-cont-mapt-options". The S46 Rule option conveys a number of parameters: - ``flags`` - an unsigned 8-bit integer, with currently only the most-significant bit specified. It denotes whether the rule can be used for forwarding (128) or not (0). - ``ea-len`` - an 8-bit-long Embedded Address length. Allowed values range from 0 to 48. - ``IPv4 prefix length`` - 8 bits long; expresses the prefix length of the Rule IPv4 prefix specified in the ipv4-prefix field. Allowed values range from 0 to 32. - ``IPv4 prefix`` - a fixed-length 32-bit field that specifies the IPv4 prefix for the S46 rule. The bits in the prefix after a specific number of bits (defined in prefix4-len) are reserved, and MUST be initialized to zero by the sender and ignored by the receiver. - ``IPv6 prefix`` - in prefix/length notation that specifies the IPv6 domain prefix for the S46 rule. The field is padded on the right with zero bits up to the nearest octet boundary, when prefix6-len is not evenly divisible by 8. S46 BR Option ~~~~~~~~~~~~~ The S46 BR option is used to convey the IPv6 address of the Border Relay. This option is mandatory in the MAP-E Container option and is not permitted in the MAP-T and S46 Lightweight 4over6 Container options. :: { "space": "s46-cont-mape-options", "name": "s46-br", "data": "2001:db8:cafe::1", } Another possible "space" value is "s46-cont-lw-options". S46 DMR Option ~~~~~~~~~~~~~~ The S46 DMR option is used to convey values for the Default Mapping Rule (DMR). This option is mandatory in the MAP-T container option and is not permitted in the MAP-E and S46 Lightweight 4over6 Container options. :: { "space": "s46-cont-mapt-options", "name": "s46-dmr", "data": "2001:db8:cafe::/64", } This option must not be included in other containers. S46 IPv4/IPv6 Address Binding Option ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The S46 IPv4/IPv6 Address Binding option may be used to specify the full or shared IPv4 address of the Customer Edge (CE). The IPv6 prefix field is used by the CE to identify the correct prefix to use for the tunnel source. :: { "space": "s46-cont-lw", "name": "s46-v4v6bind", "data": "192.0.2.3, 2001:db8:1:cafe::/64" } This option must not be included in other containers. S46 Port Parameters ~~~~~~~~~~~~~~~~~~~ The S46 Port Parameters option specifies optional port-set information that MAY be provided to CEs. :: { "space": "s46-rule-options", "name": "s46-portparams", "data": "2, 3/4", } Another possible "space" value is "s46-v4v6bind", to include this option in the S46 IPv4/IPv6 Address Binding option. Note that the second value in the example above specifies the PSID and PSID-length fields in the format of PSID/PSID length. This is equivalent to the values of PSID-len=4 and PSID=12288 conveyed in the S46 Port Parameters option. .. _dhcp6-custom-options: Custom DHCPv6 Options --------------------- Kea supports custom (non-standard) DHCPv6 options. Assume that we want to define a new DHCPv6 option called "foo" which will have code 100 and which will convey a single, unsigned, 32-bit integer value. We can define such an option by putting the following entry in the configuration file: :: "Dhcp6": { "option-def": [ { "name": "foo", "code": 100, "type": "uint32", "array": false, "record-types": "", "space": "dhcp6", "encapsulate": "" }, ... ], ... } The ``false`` value of the ``array`` parameter determines that the option does NOT comprise an array of "uint32" values but is, instead, a single value. Two other parameters have been left blank: ``record-types`` and ``encapsulate``. The former specifies the comma-separated list of option data fields, if the option comprises a record of data fields. The ``record-types`` value should be non-empty if ``type`` is set to "record"; otherwise it must be left blank. The latter parameter specifies the name of the option space being encapsulated by the particular option. If the particular option does not encapsulate any option space, the parameter should be left blank. Note that the ``option-def`` configuration statement only defines the format of the new option and does not set its value(s). The ``name``, ``code``, and ``type`` parameters are required; all others are optional. The ``array`` default value is ``false``. The ``record-types`` and ``encapsulate`` default values are blank (i.e. ""). The default ``space`` is "dhcp6". Once the new option format is defined, its value is set in the same way as for a standard option. For example, the following commands set a global value that applies to all subnets. :: "Dhcp6": { "option-data": [ { "name": "foo", "code": 100, "space": "dhcp6", "csv-format": true, "data": "12345" }, ... ], ... } New options can take more complex forms than simple use of primitives (uint8, string, ipv6-address, etc.); it is possible to define an option comprising a number of existing primitives. For example, assume we want to define a new option that will consist of an IPv6 address, followed by an unsigned 16-bit integer, followed by a boolean value, followed by a text string. Such an option could be defined in the following way: :: "Dhcp6": { "option-def": [ { "name": "bar", "code": 101, "space": "dhcp6", "type": "record", "array": false, "record-types": "ipv6-address, uint16, boolean, string", "encapsulate": "" }, ... ], ... } The ``type`` is set to "record" to indicate that the option contains multiple values of different types. These types are given as a comma-separated list in the ``record-types`` field and should be ones from those listed in :ref:`dhcp-types`. The values of the options are set in an ``option-data`` statement as follows: :: "Dhcp6": { "option-data": [ { "name": "bar", "space": "dhcp6", "code": 101, "csv-format": true, "data": "2001:db8:1::10, 123, false, Hello World" } ], ... } ``csv-format`` is set to ``true`` to indicate that the ``data`` field comprises a comma-separated list of values. The values in ``data`` must correspond to the types set in the ``record-types`` field of the option definition. When ``array`` is set to ``true`` and ``type`` is set to "record", the last field is an array, i.e. it can contain more than one value, as in: :: "Dhcp6": { "option-def": [ { "name": "bar", "code": 101, "space": "dhcp6", "type": "record", "array": true, "record-types": "ipv6-address, uint16", "encapsulate": "" }, ... ], ... } The new option content is one IPv6 address followed by one or more 16-bit unsigned integers. .. note:: In general, boolean values are specified as ``true`` or ``false``, without quotes. Some specific boolean parameters may accept also ``"true"``, ``"false"``, ``0``, ``1``, ``"0"``, and ``"1"``. .. _dhcp6-vendor-opts: DHCPv6 Vendor-Specific Options ------------------------------ Vendor options in DHCPv6 are carried in the Vendor-Specific Information option (code 17). The following examples show how to define an option "foo" with code 1 that consists of an IPv6 address, an unsigned 16-bit integer, and a string. The "foo" option is conveyed in a Vendor-Specific Information option, which comprises a single uint32 value that is set to "12345". The sub-option "foo" follows the data field holding this value. The first step is to define the format of the option: :: "Dhcp6": { "option-def": [ { "name": "foo", "code": 1, "space": "vendor-12345", "type": "record", "array": false, "record-types": "ipv6-address, uint16, string", "encapsulate": "" } ], ... } (Note that the option space is set to ``vendor-12345``.) Once the option format is defined, the next step is to define actual values for that option: :: "Dhcp6": { "option-data": [ { "name": "foo", "space": "vendor-12345", "data": "2001:db8:1::10, 123, Hello World" }, ... ], ... } We should also define a value (enterprise-number) for the Vendor-Specific Information option, that conveys our option "foo". :: "Dhcp6": { "option-data": [ ..., { "name": "vendor-opts", "data": "12345" } ], ... } Alternatively, the option can be specified using its code. :: "Dhcp6": { "option-data": [ ..., { "code": 17, "data": "12345" } ], ... } A common configuration is to set the always-send flag to true so the vendor option is sent even when the client did not specify it in the query. .. note:: Currently only a single instance of the vendor-class (code 16) and a single instance of the vendor-opts (code 17) options can be specified. Specifying multiple options with different enterprise numbers is currently not supported by Kea. .. _dhcp6-option-spaces: Nested DHCPv6 Options (Custom Option Spaces) -------------------------------------------- It is sometimes useful to define completely new option spaces, such as when a user creates a new option to convey sub-options that use a separate numbering scheme, for example sub-options with codes 1 and 2. Those option codes conflict with standard DHCPv6 options, so a separate option space must be defined. Note that the creation of a new option space is not required when defining sub-options for a standard option, because one is created by default if the standard option is meant to convey any sub-options (see :ref:`dhcp6-vendor-opts`). Assume that we want to have a DHCPv6 option called "container" with code 102 that conveys two sub-options with codes 1 and 2. First we need to define the new sub-options: :: "Dhcp6": { "option-def": [ { "name": "subopt1", "code": 1, "space": "isc", "type": "ipv6-address", "record-types": "", "array": false, "encapsulate": "" }, { "name": "subopt2", "code": 2, "space": "isc", "type": "string", "record-types": "", "array": false "encapsulate": "" } ], ... } Note that we have defined the options to belong to a new option space (in this case, "isc"). The next step is to define a regular DHCPv6 option with the desired code and specify that it should include options from the new option space: :: "Dhcp6": { "option-def": [ ..., { "name": "container", "code": 102, "space": "dhcp6", "type": "empty", "array": false, "record-types": "", "encapsulate": "isc" } ], ... } The name of the option space in which the sub-options are defined is set in the ``encapsulate`` field. The ``type`` field is set to ``empty``, which limits this option to only carrying data in sub-options. Finally, we can set values for the new options: :: "Dhcp6": { "option-data": [ { "name": "subopt1", "code": 1, "space": "isc", "data": "2001:db8::abcd" }, } "name": "subopt2", "code": 2, "space": "isc", "data": "Hello world" }, { "name": "container", "code": 102, "space": "dhcp6" } ], ... } Note that it is possible to create an option which carries some data in addition to the sub-options defined in the encapsulated option space. For example, if the "container" option from the previous example were required to carry a uint16 value as well as the sub-options, the ``type`` value would have to be set to "uint16" in the option definition. (Such an option would then have the following data structure: DHCP header, uint16 value, sub-options.) The value specified with the ``data`` parameter — which should be a valid integer enclosed in quotes, e.g. "123" — would then be assigned to the uint16 field in the "container" option. .. _dhcp6-option-data-defaults: Unspecified Parameters for DHCPv6 Option Configuration ------------------------------------------------------ In many cases it is not required to specify all parameters for an option configuration, and the default values can be used. However, it is important to understand the implications of not specifying some of them, as it may result in configuration errors. The list below explains the behavior of the server when a particular parameter is not explicitly specified: - ``name`` - the server requires either an option name or an option code to identify an option. If this parameter is unspecified, the option code must be specified. - ``code`` - the server requires either an option name or an option code to identify an option. This parameter may be left unspecified if the ``name`` parameter is specified. However, this also requires that the particular option has a definition (either as a standard option or an administrator-created definition for the option using an 'option-def' structure), as the option definition associates an option with a particular name. It is possible to configure an option for which there is no definition (unspecified option format). Configuration of such options requires the use of the option code. - ``space`` - if the option space is unspecified it will default to 'dhcp6', which is an option space holding standard DHCPv6 options. - ``data`` - if the option data is unspecified it defaults to an empty value. The empty value is mostly used for the options which have no payload (boolean options), but it is legal to specify empty values for some options which carry variable-length data and for which the specification allows a length of 0. For such options, the data parameter may be omitted in the configuration. - ``csv-format`` - if this value is not specified, the server will assume that the option data is specified as a list of comma-separated values to be assigned to individual fields of the DHCP option. .. _dhcp6-t1-t2-times: Controlling the Values Sent for T1 and T2 Times ----------------------------------------------- According to RFC 8415, section 21.4, the recommended T1 and T2 values are 50% and 80% of the preferred lease time, respectively. Kea can be configured to send values that are specified explicitly or that are calculated as percentages of the preferred lease time. The server's behavior is governed by a combination of configuration parameters, two of which have already been mentioned. Beginning with Kea 1.6.0 lease preferred and valid lifetime are extended from single values to triplets with minimum, default and maximum values using: - ``min-preferred-lifetime`` - specifies the minimum preferred lifetime (optional). - ``preferred-lifetime`` - specifies the default preferred lifetime. - ``max-preferred-lifetime`` - specifies the maximum preferred lifetime (optional). - ``min-valid-lifetime`` - specifies the minimum valid lifetime (optional). - ``valid-lifetime`` - specifies the default valid lifetime. - ``max-valid-lifetime`` - specifies the maximum valid lifetime (optional). As of Kea 1.9.11, these values may be specified within client classes. When the client does not specify lifetimes the default is used. When it specifies a lifetime using IAADDR or IAPREFIX sub option with non-zero values, these values are used when they are between configured minimum (lower values are round up) and maximum (larger values are rounded down) bounds. To send specific, fixed values use the following two parameters: - ``renew-timer`` - specifies the value of T1 in seconds. - ``rebind-timer`` - specifies the value of T2 in seconds. Any value greater than or equal to zero may be specified for T2. When specifying T1 it must be less than T2. This flexibility is allowed to support a use case where administrators want to suppress client renewals and rebinds by deferring them beyond the lifespan of the lease. This should cause the lease to expire, rather than get renewed by clients. If T1 is specified as larger than T2, T1 will be set to zero in the outbound IA. In the great majority of cases the values should follow this rule: T1 < T2 < preferred lifetime < valid lifetime. Alternatively, both T1 and T2 values can be configured to 0, which is a signal to DHCPv6 clients that they may renew at their own discretion. However, there are known broken client implementations in use that will start renewing immediately. Administrators who plan to use T1=T2=0 values should test first and make sure their clients behave rationally. In some rare cases there may be a need to disable a client's ability to renew addresses. This is undesired from a protocol perspective and should be avoided if possible. However, if necessary, administrators can configure the T1 and T2 values to be equal or greater to the valid lifetime. Be advised that this will cause clients to occasionally lose their addresses, which is generally perceived as poor service. However, there may be some rare business cases when this is desired (e.g. when it is desirable to intentionally break long-lasting connections). Calculation of the values is controlled by the following three parameters: - ``calculate-tee-times`` - when true, T1 and T2 will be calculated as percentages of the valid lease time. It defaults to true. - ``t1-percent`` - the percentage of the valid lease time to use for T1. It is expressed as a real number between 0.0 and 1.0 and must be less than t2-percent. The default value is 0.5 per RFC 8415. - ``t2-percent`` - the percentage of the valid lease time to use for T2. It is expressed as a real number between 0.0 and 1.0 and must be greater than t1-percent. The default value is 0.8 per RFC 8415. .. .. note:: In the event that both explicit values are specified and calculate-tee-times is true, the server will use the explicit values. Administrators with a setup where some subnets or share-networks will use explicit values and some will use calculated values must not define the explicit values at any level higher than where they will be used. Inheriting them from too high a scope, such as global, will cause them to have values at every level underneath (shared-networks and subnets), effectively disabling calculated values. .. _dhcp6-config-subnets: IPv6 Subnet Selection --------------------- The DHCPv6 server may receive requests from local (connected to the same subnet as the server) and remote (connected via relays) clients. As the server may have many subnet configurations defined, it must select an appropriate subnet for a given request. In IPv4, the server can determine which of the configured subnets are local, as there is a reasonable expectation that the server will have a (global) IPv4 address configured on the interface. That assumption is not true in IPv6; the DHCPv6 server must be able to operate while only using link-local addresses. Therefore, an optional ``interface`` parameter is available within a subnet definition to designate that a given subnet is local, i.e. reachable directly over the specified interface. For example, a server that is intended to serve a local subnet over eth0 may be configured as follows: :: "Dhcp6": { "subnet6": [ { "subnet": "2001:db8:beef::/48", "pools": [ { "pool": "2001:db8:beef::/48" } ], "interface": "eth0" } ], ... } .. _dhcp6-rapid-commit: Rapid Commit ------------ The Rapid Commit option, described in `RFC 8415 `__, is supported by the Kea DHCPv6 server. However, support is disabled by default. It can be enabled on a per-subnet basis using the ``rapid-commit`` parameter as shown below: :: "Dhcp6": { "subnet6": [ { "subnet": "2001:db8:beef::/48", "rapid-commit": true, "pools": [ { "pool": "2001:db8:beef::1-2001:db8:beef::10" } ], } ], ... } This setting only affects the subnet for which ``rapid-commit`` is set to ``true``. For clients connected to other subnets, the server will ignore the Rapid Commit option sent by the client and will follow the 4-way exchange procedure, i.e. respond with an Advertise for a Solicit containing a Rapid Commit option. .. _dhcp6-relays: DHCPv6 Relays ------------- A DHCPv6 server with multiple subnets defined must select the appropriate subnet when it receives a request from a client. For clients connected via relays, two mechanisms are used: The first uses the linkaddr field in the RELAY_FORW message. The name of this field is somewhat misleading in that it does not contain a link-layer address; instead, it holds an address (typically a global address) that is used to identify a link. The DHCPv6 server checks to see whether the address belongs to a defined subnet and, if it does, that subnet is selected for the client's request. The second mechanism is based on interface-id options. While forwarding a client's message, relays may insert an interface-id option into the message that identifies the interface on the relay that received the message. (Some relays allow configuration of that parameter, but it is sometimes hard-coded and may range from the very simple (e.g. "vlan100") to the very cryptic; one example seen on real hardware was "ISAM144|299|ipv6|nt:vp:1:110"). The server can use this information to select the appropriate subnet. The information is also returned to the relay, which then knows the interface to use to transmit the response to the client. For this to work successfully, the relay interface IDs must be unique within the network and the server configuration must match those values. When configuring the DHCPv6 server, it should be noted that two similarly named parameters can be configured for a subnet: - ``interface`` defines which local network interface can be used to access a given subnet. - ``interface-id`` specifies the content of the interface-id option used by relays to identify the interface on the relay to which the response packet is sent. The two are mutually exclusive; a subnet cannot be reachable both locally (direct traffic) and via relays (remote traffic). Specifying both is a configuration error and the DHCPv6 server will refuse such a configuration. The following example configuration shows how to specify an interface-id with a value of "vlan123": :: "Dhcp6": { "subnet6": [ { "subnet": "2001:db8:beef::/48", "pools": [ { "pool": "2001:db8:beef::/48" } ], "interface-id": "vlan123" } ], ... } .. _dhcp6-rsoo: Relay-Supplied Options ---------------------- `RFC 6422 `__ defines a mechanism called Relay-Supplied DHCP Options. In certain cases relay agents are the only entities that may have specific information, and they can insert options when relaying messages from the client to the server. The server will then do certain checks and copy those options to the response sent to the client. There are certain conditions that must be met for the option to be included. First, the server must not provide the option itself; in other words, if both relay and server provide an option, the server always takes precedence. Second, the option must be RSOO-enabled. (RSOO is the "Relay Supplied Options option.") IANA maintains a list of RSOO-enabled options `here `__. However, there may be cases when system administrators want to echo other options. Kea can be instructed to treat other options as RSOO-enabled. For example, to mark options 110, 120, and 130 as RSOO-enabled, the following syntax should be used: :: "Dhcp6": { "relay-supplied-options": [ "110", "120", "130" ], ... } As of February 2019, only option 65 is RSOO-enabled by IANA. This option will always be treated as such, so there is no need to explicitly mark it. Also, when enabling standard options, it is possible to use their names rather than their option code, e.g. use ``dns-servers`` instead of ``23``. See ref:`dhcp6-std-options-list` for the names. In certain cases this may also work for custom options, but due to the nature of the parser code this may be unreliable and should be avoided. .. _dhcp6-client-classifier: Client Classification in DHCPv6 ------------------------------- The DHCPv6 server includes support for client classification. For a deeper discussion of the classification process see :ref:`classify`. In certain cases it is useful to configure the server to differentiate between DHCP client types and treat them accordingly. Client classification can be used to modify the behavior of almost any part of the DHCP message processing. Kea currently offers three mechanisms that take advantage of client classification in DHCPv6: subnet selection, address pool selection, and DHCP options assignment. Kea can be instructed to limit access to given subnets based on class information. This is particularly useful for cases where two types of devices share the same link and are expected to be served from two different subnets. The primary use case for such a scenario is cable networks, where there are two classes of devices: the cable modem itself, which should be handed a lease from subnet A; and all other devices behind the modem, which should get a lease from subnet B. That segregation is essential to prevent overly curious users from playing with their cable modems. For details on how to set up class restrictions on subnets, see :ref:`classification-subnets`. When subnets belong to a shared network, the classification applies to subnet selection but not to pools; that is, a pool in a subnet limited to a particular class can still be used by clients which do not belong to the class, if the pool they are expected to use is exhausted. So the limit on access based on class information is also available at the address/prefix pool level; see :ref:`classification-pools`, within a subnet. This is useful when segregating clients belonging to the same subnet into different address ranges. In a similar way, a pool can be constrained to serve only known clients, i.e. clients which have a reservation, using the built-in "KNOWN" or "UNKNOWN" classes. Addresses can be assigned to registered clients without giving a different address per reservation, for instance when there are not enough available addresses. The determination whether there is a reservation for a given client is made after a subnet is selected, so it is not possible to use "KNOWN"/"UNKNOWN" classes to select a shared network or a subnet. The process of classification is conducted in five steps. The first step is to assess an incoming packet and assign it to zero or more classes. The second step is to choose a subnet, possibly based on the class information. When the incoming packet is in the special class, "DROP, it is dropped and a debug message logged. The next step is to evaluate class expressions depending on the built-in "KNOWN"/"UNKNOWN" classes after host reservation lookup, using them for pool/pd-pool selection and assigning classes from host reservations. The list of required classes is then built and each class of the list has its expression evaluated; when it returns "true" the packet is added as a member of the class. The last step is to assign options, again possibly based on the class information. More complete and detailed information is available in :ref:`classify`. There are two main methods of classification. The first is automatic and relies on examining the values in the vendor class options or the existence of a host reservation. Information from these options is extracted, and a class name is constructed from it and added to the class list for the packet. The second specifies an expression that is evaluated for each packet. If the result is "true", the packet is a member of the class. .. note:: Care should be taken with client classification, as it is easy for clients that do not meet class criteria to be denied all service. Defining and Using Custom Classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following example shows how to configure a class using an expression and a subnet using that class. This configuration defines the class named "Client_enterprise". It is comprised of all clients whose client identifiers start with the given hex string (which would indicate a DUID based on an enterprise id of 0xAABBCCDD). Members of this class will be given an address from 2001:db8:1::0 to 2001:db8:1::FFFF and the addresses of their DNS servers set to 2001:db8:0::1 and 2001:db8:2::1. :: "Dhcp6": { "client-classes": [ { "name": "Client_enterprise", "test": "substring(option[1].hex,0,6) == 0x0002AABBCCDD", "option-data": [ { "name": "dns-servers", "code": 23, "space": "dhcp6", "csv-format": true, "data": "2001:db8:0::1, 2001:db8:2::1" } ] }, ... ], "subnet6": [ { "subnet": "2001:db8:1::/64", "pools": [ { "pool": "2001:db8:1::-2001:db8:1::ffff" } ], "client-class": "Client_enterprise" } ], ... } This example shows a configuration using an automatically generated "VENDOR_CLASS\_" class. The administrator of the network has decided that addresses in the range 2001:db8:1::1 to 2001:db8:1::ffff are to be managed by the DHCP6 server and that only clients belonging to the eRouter1.0 client class are allowed to use that pool. :: "Dhcp6": { "subnet6": [ { "subnet": "2001:db8:1::/64", "pools": [ { "pool": "2001:db8:1::-2001:db8:1::ffff" } ], "client-class": "VENDOR_CLASS_eRouter1.0" } ], ... } .. _dhcp6-required-class: Required Classification ~~~~~~~~~~~~~~~~~~~~~~~ In some cases it is useful to limit the scope of a class to a shared network, subnet, or pool. There are two parameters which are used to limit the scope of the class by instructing the server to evaluate test expressions when required. The first one is the per-class ``only-if-required`` flag, which is false by default. When it is set to ``true``, the test expression of the class is not evaluated at the reception of the incoming packet but later, and only if the class evaluation is required. The second is ``require-client-classes``, which takes a list of class names and is valid in shared-network, subnet, and pool scope. Classes in these lists are marked as required and evaluated after selection of this specific shared-network/subnet/pool and before output option processing. In this example, a class is assigned to the incoming packet when the specified subnet is used: :: "Dhcp6": { "client-classes": [ { "name": "Client_foo", "test": "member('ALL')", "only-if-required": true }, ... ], "subnet6": [ { "subnet": "2001:db8:1::/64" "pools": [ { "pool": "2001:db8:1::-2001:db8:1::ffff" } ], "require-client-classes": [ "Client_foo" ], ... }, ... ], ... } Required evaluation can be used to express complex dependencies like subnet membership. It can also be used to reverse the precedence; if an option-data is set in a subnet, it takes precedence over an option-data in a class. If the option-data is moved to a required class and required in the subnet, a class evaluated earlier may take precedence. Required evaluation is also available at shared-network and pool/pd-pool levels. The order in which required classes are considered is: shared-network, subnet, and (pd-)pool, i.e. in the opposite order in which option-data is processed. .. _dhcp6-ddns-config: DDNS for DHCPv6 --------------- As mentioned earlier, kea-dhcp6 can be configured to generate requests to the DHCP-DDNS server (referred to here as "D2") to update DNS entries. These requests are known as Name Change Requests or NCRs. Each NCR contains the following information: 1. Whether it is a request to add (update) or remove DNS entries 2. Whether the change requests forward DNS updates (AAAA records), reverse DNS updates (PTR records), or both 3. The Fully Qualified Domain Name (FQDN), lease address, and DHCID (information identifying the client associated with the FQDN) Prior to Kea 1.7.1, all parameters for controlling DDNS were within the global ``dhcp-ddns`` section of the kea-dhcp6. Beginning with Kea 1.7.1 DDNS related parameters were split into two groups: 1. Connectivity Parameters These are parameters which specify where and how kea-dhcp6 connects to and communicates with D2. These parameters can only be specified within the top-level ``dhcp-ddns`` section in the kea-dhcp6 configuration. The connectivity parameters are listed below: - ``enable-updates`` - ``server-ip`` - ``server-port`` - ``sender-ip`` - ``sender-port`` - ``max-queue-size`` - ``ncr-protocol`` - ``ncr-format"`` 2. Behavioral Parameters These parameters influence behavior such as how client host names and FQDN options are handled. They have been moved out of the ``dhcp-ddns`` section so that they may be specified at the global, shared-network, and/or subnet levels. Furthermore, they are inherited downward from global to shared-network to subnet. In other words, if a parameter is not specified at a given level, the value for that level comes from the level above it. The behavioral parameter as follows: - ``ddns-send-updates`` - ``ddns-override-no-update`` - ``ddns-override-client-update`` - ``ddns-replace-client-name"`` - ``ddns-generated-prefix`` - ``ddns-qualifying-suffix`` - ``ddns-update-on-renew`` - ``ddns-use-conflict-resolution`` - ``hostname-char-set`` - ``hostname-char-replacement`` .. note:: For backward compatibility, configuration parsing will still recognize the original behavioral parameters specified in ``dhcp-ddns``. It will do so by translating the parameter into its global equivalent. If a parameter is specified both globally and in ``dhcp-ddns``, the latter value will be ignored. In either case, a log will be emitted explaining what has occurred. Specifying these values within ``dhcp-ddns`` is deprecated and support for it will be removed at some future date. The default configuration and values would appear as follows: :: "Dhcp6": { "dhcp-ddns": { // Connectivity parameters "enable-updates": false, "server-ip": "127.0.0.1", "server-port":53001, "sender-ip":"", "sender-port":0, "max-queue-size":1024, "ncr-protocol":"UDP", "ncr-format":"JSON" }, // Behavioral parameters (global) "ddns-send-updates": true, "ddns-override-no-update": false, "ddns-override-client-update": false, "ddns-replace-client-name": "never", "ddns-generated-prefix": "myhost", "ddns-qualifying-suffix": "", "ddns-update-on-renew": false, "ddns-use-conflict-resolution": true, "hostname-char-set": "", "hostname-char-replacement": "" ... } As of Kea 1.7.1, there are two parameters which determine if kea-dhcp6 can generate DDNS requests to D2: the existing ``dhcp-ddns:enable-updates`` parameter, which now only controls whether kea-dhcp6 connects to D2; and the new behavioral parameter, ``ddns-send-updates``, which determines whether DDNS updates are enabled at a given level (i.e. global, shared-network, or subnet). The following table shows how the two parameters function together: .. table:: Enabling and Disabling DDNS Updates +-----------------+--------------------+-------------------------------+ | dhcp-ddns: | Global | Outcome | | enable-updates | ddns-send-updates | | +=================+====================+===============================+ | false (default) | false | no updates at any scope | +-----------------+--------------------+-------------------------------+ | false | true (default) | no updates at any scope | +-----------------+--------------------+-------------------------------+ | true | false | updates only at scopes with | | | | a local value of true for | | | | ddns-enable-updates | +-----------------+--------------------+-------------------------------+ | true | true | updates at all scopes except | | | | those with a local value of | | | | false for ddns-enable-updates | +-----------------+--------------------+-------------------------------+ Kea 1.9.1 adds two new parameters. The first new parameter is ``ddns-update-on-renew``. Normally, when leases are renewed the server only updates DNS if the DNS information for the lease (e.g. FQDN, DNS update direction flags) has changed. Setting ``ddns-update-on-renew`` to true instructs the server to always update the DNS information when a lease is renewed even if its DNS information has not changed. This allows Kea to "self-heal" if it was previously unable to add DNS entries or they were somehow lost by the DNS server. .. note:: Setting ``ddns-update-on-renew`` to true may impact performance, especially for servers with numerous clients who renew often. The second parameter added in Kea 1.9.1 is ``ddns-use-conflict-resolution``. The value of this parameter is passed by kea-dhcp6 to D2 with each DNS update request. When true, (the default value), D2 will employ conflict resolution, as described in `RFC 4703 `__, when attempting to fulfill the update request. When false, D2 will simply attempt to update the DNS entries per the request, regardless of whether or not they conflict with existing entries owned by other DHCP6 clients. .. note:: Setting ``ddns-use-conflict-resolution`` to false disables the overwrite safeguards that the rules of conflict resolution ( `RFC 4703 `__) are intended to prevent. This means that existing entries for a FQDN or an IP address made for Client-A can be deleted or replaced by entries for Client-B. Furthermore, there are two scenarios by which entries for multiple clients for the same key (e.g. FQDN or IP) can be created. 1. Client-B uses the same FQDN as Client-A but a different IP address. In this case the forward DNS entries (AAAA, and DHCID RRs) for Client-A will be deleted as they match the FQDN and new entries for Client-B will be added. The reverse DNS entries (PTR and DHCID RRs) for Client-A, however, will not be deleted as they belong to a different IP address while new entries for Client-B will still be added. 2. Client-B uses the same IP address as Client-A but a different FQDN. In this case the reverse DNS entries (PTR and DHCID RRs) for Client-A will be deleted as they match the IP address and new entries for Client-B will be added. The forward DNS entries (AAAA and DHCID RRs) for Client-A, however, will not be deleted as they belong to a different FQDN while new entries for Client-B will still be added. Disabling conflict resolution should be done only after careful review of specific use cases. The best way to avoid unwanted DNS entries is to always ensure lease changes are processed through Kea, whether they are released, expire, or are deleted via the lease-del6 command, prior to reassigning either FQDNs or IP addresses. Doing so causes kea-dhcp6 to generate DNS removal requests to D2. .. note:: The DNS entries Kea creates contain a value for TTL (time to live). As of Kea 1.9.3, kea-dhcp6 calculates that value based on `RFC 4702, Section 5 `__ which suggests that the TTL value be 1/3 of the lease's lifetime with a minimum value of 10 minutes. Prior to this the server set the TTL value equal to the lease's valid lifetime. Future releases may add one or more parameters to customize this value. .. _dhcpv6-d2-io-config: DHCP-DDNS Server Connectivity ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For NCRs to reach the D2 server, kea-dhcp6 must be able to communicate with it. kea-dhcp6 uses the following configuration parameters to control this communication: - ``enable-updates`` - As of Kea 1.7.1, this parameter only enables connectivity to kea-dhcp-ddns such that DDNS updates can be constructed and sent. It must be true for NCRs to be generated and sent to D2. It defaults to false. - ``server-ip`` - IP address on which D2 listens for requests. The default is the local loopback interface at address 127.0.0.1. Either an IPv4 or IPv6 address may be specified. - ``server-port`` - port on which D2 listens for requests. The default value is 53001. - ``sender-ip`` - the IP address which kea-dhcp6 uses to send requests to D2. The default value is blank, which instructs kea-dhcp6 to select a suitable address. - ``sender-port`` - the port which kea-dhcp6 uses to send requests to D2. The default value of 0 instructs kea-dhcp6 to select a suitable port. - ``max-queue-size`` - the maximum number of requests allowed to queue waiting to be sent to D2. This value guards against requests accumulating uncontrollably if they are being generated faster than they can be delivered. If the number of requests queued for transmission reaches this value, DDNS updating will be turned off until the queue backlog has been sufficiently reduced. The intent is to allow the kea-dhcp6 server to continue lease operations without running the risk that its memory usage grows without limit. The default value is 1024. - ``ncr-protocol`` - the socket protocol to use when sending requests to D2. Currently only UDP is supported. - ``ncr-format`` - the packet format to use when sending requests to D2. Currently only JSON format is supported. By default, kea-dhcp-ddns is assumed to be running on the same machine as kea-dhcp6, and all of the default values mentioned above should be sufficient. If, however, D2 has been configured to listen on a different address or port, these values must be altered accordingly. For example, if D2 has been configured to listen on 2001:db8::5 port 900, the following configuration is required: :: "Dhcp6": { "dhcp-ddns": { "server-ip": "2001:db8::5", "server-port": 900, ... }, ... } .. _dhcpv6-d2-rules-config: When Does the kea-dhcp6 Server Generate a DDNS Request? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ kea-dhcp6 follows the behavior prescribed for DHCP servers in `RFC 4704 `__. It is important to keep in mind that kea-dhcp6 makes the initial decision of when and what to update and forwards that information to D2 in the form of NCRs. Carrying out the actual DNS updates and dealing with such things as conflict resolution are within the purview of D2 itself (see :ref:`dhcp-ddns-server`). This section describes when kea-dhcp6 will generate NCRs and the configuration parameters that can be used to influence this decision. It assumes that the ``enable-updates`` parameter is true. .. note:: Currently the interface between kea-dhcp6 and D2 only supports requests which update DNS entries for a single IP address. If a lease grants more than one address, kea-dhcp6 will create the DDNS update request for only the first of these addresses. In general, kea-dhcp6 will generate DDNS update requests when: 1. A new lease is granted in response to a DHCPREQUEST; 2. An existing lease is renewed but the FQDN associated with it has changed; or 3. An existing lease is released in response to a DHCPRELEASE. In the second case, lease renewal, two DDNS requests will be issued: one request to remove entries for the previous FQDN, and a second request to add entries for the new FQDN. In the last case, a lease release, a single DDNS request to remove its entries will be made. As for the first case, the decisions involved when granting a new lease are more complex. When a new lease is granted, kea-dhcp6 will generate a DDNS update request only if the DHCPREQUEST contains the FQDN option (code 39). By default, kea-dhcp6 will respect the FQDN N and S flags specified by the client as shown in the following table: .. table:: Default FQDN Flag Behavior +-----------------+-----------------+-----------------+-----------------+ | Client | Client Intent | Server Response | Server | | Flags:N-S | | | Flags:N-S-O | +=================+=================+=================+=================+ | 0-0 | Client wants to | Server | 1-0-0 | | | do forward | generates | | | | updates, server | reverse-only | | | | should do | request | | | | reverse updates | | | +-----------------+-----------------+-----------------+-----------------+ | 0-1 | Server should | Server | 0-1-0 | | | do both forward | generates | | | | and reverse | request to | | | | updates | update both | | | | | directions | | +-----------------+-----------------+-----------------+-----------------+ | 1-0 | Client wants no | Server does not | 1-0-0 | | | updates done | generate a | | | | | request | | +-----------------+-----------------+-----------------+-----------------+ The first row in the table above represents "client delegation." Here the DHCP client states that it intends to do the forward DNS updates and the server should do the reverse updates. By default, kea-dhcp6 will honor the client's wishes and generate a DDNS request to D2 to update only reverse DNS data. The parameter ``ddns-override-client-update`` can be used to instruct the server to override client delegation requests. When this parameter is "true", kea-dhcp6 will disregard requests for client delegation and generate a DDNS request to update both forward and reverse DNS data. In this case, the N-S-O flags in the server's response to the client will be 0-1-1 respectively. (Note that the flag combination N=1, S=1 is prohibited according to `RFC 4702 `__. If such a combination is received from the client, the packet will be dropped by kea-dhcp6.) To override client delegation, set the following values in the configuration file: :: "Dhcp6": { ... "ddns-override-client-update": true, ... } The third row in the table above describes the case in which the client requests that no DNS updates be done. The parameter, ``ddns-override-no-update``, can be used to instruct the server to disregard the client's wishes. When this parameter is true, kea-dhcp6 will generate DDNS update requests to kea-dhcp-ddns even if the client requests that no updates be done. The N-S-O flags in the server's response to the client will be 0-1-1. To override client delegation, issue the following commands: :: "Dhcp6": { ... "ddns-override-no-update": true, ... } .. _dhcpv6-fqdn-name-generation: kea-dhcp6 Name Generation for DDNS Update Requests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Each Name Change Request must of course include the fully qualified domain name whose DNS entries are to be affected. kea-dhcp6 can be configured to supply a portion or all of that name, based upon what it receives from the client in the DHCPREQUEST. The default rules for constructing the FQDN that will be used for DNS entries are: 1. If the DHCPREQUEST contains the client FQDN option, take the candidate name from there. 2. If the candidate name is a partial (i.e. unqualified) name, then add a configurable suffix to the name and use the result as the FQDN. 3. If the candidate name provided is empty, generate an FQDN using a configurable prefix and suffix. 4. If the client provides neither option, then take no DNS action. These rules can be amended by setting the ``ddns-replace-client-name`` parameter, which provides the following modes of behavior: - ``never`` - use the name the client sent. If the client sent no name, do not generate one. This is the default mode. - ``always`` - replace the name the client sent. If the client sent no name, generate one for the client. - ``when-present`` - replace the name the client sent. If the client sent no name, do not generate one. - ``when-not-present`` - use the name the client sent. If the client sent no name, generate one for the client. .. .. note:: Note that in early versions of Kea, this parameter was a boolean and permitted only values of ``true`` and ``false``. Boolean values have been deprecated and are no longer accepted. Administrators currently using booleans must replace them with the desired mode name. A value of ``true`` maps to ``"when-present"``, while ``false`` maps to ``"never"``. For example, to instruct kea-dhcp6 to always generate the FQDN for a client, set the parameter ``ddns-replace-client-name`` to ``always`` as follows: :: "Dhcp6": { ... "ddns-replace-client-name": "always", ... } The prefix used in the generation of an FQDN is specified by the ``ddns-generated-prefix`` parameter. The default value is "myhost". To alter its value, simply set it to the desired string: :: "Dhcp6": { ... "ddns-generated-prefix": "another.host", ... } The suffix used when generating an FQDN, or when qualifying a partial name, is specified by the ``ddns-qualifying-suffix`` parameter. This parameter has no default value; thus, it is mandatory when DDNS updates are enabled. To set its value simply set it to the desired string: :: "Dhcp6": { ... "ddns-qualifying-suffix": "foo.example.org", ... } When qualifying a partial name, kea-dhcp6 constructs the name in the format: [**candidate-name**].[**ddns-qualifying-suffix**]. where **candidate-name** is the partial name supplied in the DHCPREQUEST. For example, if the FQDN domain name value is "some-computer" and the ``ddns-qualifying-suffix`` "example.com", the generated FQDN is: **some-computer.example.com.** When generating the entire name, kea-dhcp6 will construct the name in the format: [**ddns-generated-prefix**]-[**address-text**].[**ddns-qualifying-suffix**]. where **address-text** is simply the lease IP address converted to a hyphenated string. For example, if the lease address is 3001:1::70E, the qualifying suffix "example.com", and the default value is used for ``ddns-generated-prefix``, the generated FQDN is: **myhost-3001-1--70E.example.com.** .. _dhcp6-host-name-sanitization: Sanitizing Client FQDN Names ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Some DHCP clients may provide values in the name component of the FQDN option (option code 39) that contain undesirable characters. It is possible to configure kea-dhcp6 to sanitize these values. The most typical use case is ensuring that only characters that are permitted by RFC 1035 be included: A-Z, a-z, 0-9, and '-'. This may be accomplished with the following two parameters: - ``hostname-char-set`` - a regular expression describing the invalid character set. This can be any valid, regular expression using POSIX extended expression syntax. Embedded nulls (0x00) are always considered an invalid character to be replaced (or omitted). - ``hostname-char-replacement`` - a string of zero or more characters with which to replace each invalid character in the host name. An empty string causes invalid characters to be OMITTED rather than replaced. .. note:: Starting with Kea 1.7.5, the default values are as follows: - "hostname-char-set": "[^A-Za-z0-9.-]", - "hostname-char-replacement": "" This enables sanitizing and omits any character that is not a letter, digit, hyphen, dot, or null. The following configuration replaces anything other than a letter, digit, hyphen, or dot with the letter 'x': :: "Dhcp6": { ... "hostname-char-set": "[^A-Za-z0-9.-]", "hostname-char-replacement": "x", ... } Thus, a client-supplied value of "myhost-$[123.org" would become "myhost-xx123.org". Sanitizing is performed only on the portion of the name supplied by the client, and it is performed before applying a qualifying suffix (if one is defined and needed). .. note:: The following are some considerations to keep in mind: Name sanitizing is meant to catch the more common cases of invalid characters through a relatively simple character-replacement scheme. It is difficult to devise a scheme that works well in all cases. Administrators who find they have clients with odd corner cases of character combinations that cannot be readily handled with this mechanism should consider writing a hook that can carry out sufficiently complex logic to address their needs. Do not include dots in the hostname-char-set expression. When scrubbing FQDNs, dots are treated as delimiters and used to separate the option value into individual domain labels that are scrubbed and then re-assembled. If clients are sending values that differ only by characters considered as invalid by the hostname-char-set, be aware that scrubbing them will yield identical values. In such cases, DDNS conflict rules will permit only one of them to register the name. Finally, given the latitude clients have in the values they send, it is virtually impossible to guarantee that a combination of these two parameters will always yield a name that is valid for use in DNS. For example, using an empty value for hostname-char-replacement could yield an empty domain label within a name, if that label consists only of invalid characters. .. note:: Since the 1.6.0 Kea release, it is possible to specify hostname-char-set and/or hostname-char-replacement at the global scope. This allows sanitizing of host names without requiring a dhcp-ddns entry. When a hostname-char parameter is defined at the global scope and in a dhcp-ddns entry, the second (local) value is used. .. _dhcp6-dhcp4o6-config: DHCPv4-over-DHCPv6: DHCPv6 Side ------------------------------- The support of DHCPv4-over-DHCPv6 transport is described in `RFC 7341 `__ and is implemented using cooperating DHCPv4 and DHCPv6 servers. This section is about the configuration of the DHCPv6 side (the DHCPv4 side is described in :ref:`dhcp4-dhcp4o6-config`). .. note:: DHCPv4-over-DHCPv6 support is experimental and the details of the inter-process communication may change; both the DHCPv4 and DHCPv6 sides should be running the same version of Kea. For instance, the support of port relay (RFC 8357) introduced an incompatible change. There is only one specific parameter for the DHCPv6 side: ``dhcp4o6-port``, which specifies the first of the two consecutive ports of the UDP sockets used for the communication between the DHCPv6 and DHCPv4 servers. The DHCPv6 server is bound to ::1 on ``port`` and connected to ::1 on ``port`` + 1. Two other configuration entries are generally required: unicast traffic support (see :ref:`dhcp6-unicast`) and DHCP 4o6 server address option (name "dhcp4o6-server-addr", code 88). The following configuration was used during some tests: :: { # DHCPv6 conf "Dhcp6": { "interfaces-config": { "interfaces": [ "eno33554984/2001:db8:1:1::1" ] }, "lease-database": { "type": "memfile", "name": "leases6" }, "preferred-lifetime": 3000, "valid-lifetime": 4000, "renew-timer": 1000, "rebind-timer": 2000, "subnet6": [ { "subnet": "2001:db8:1:1::/64", "interface": "eno33554984", "pools": [ { "pool": "2001:db8:1:1::1:0/112" } ] } ], "dhcp4o6-port": 6767, "option-data": [ { "name": "dhcp4o6-server-addr", "code": 88, "space": "dhcp6", "csv-format": true, "data": "2001:db8:1:1::1" } ], "loggers": [ { "name": "kea-dhcp6", "output_options": [ { "output": "/tmp/kea-dhcp6.log" } ], "severity": "DEBUG", "debuglevel": 0 } ] } } .. .. note:: Relayed DHCPv4-QUERY DHCPv6 messages are not supported. .. _sanity-checks6: Sanity Checks in DHCPv6 ----------------------- An important aspect of a well-running DHCP system is an assurance that the data remain consistent. However, in some cases it may be convenient to tolerate certain inconsistent data. For example, a network administrator that temporarily removed a subnet from a configuration would not want all the leases associated with it to disappear from the lease database. Kea has a mechanism to control sanity checks for situations such as this. Kea supports a configuration scope called ``sanity-checks``. It currently allows only a single parameter, called ``lease-checks``, which governs the verification carried out when a new lease is loaded from a lease file. This mechanism permits Kea to attempt to correct inconsistent data. Every subnet has a subnet-id value; this is how Kea internally identifies subnets. Each lease has a subnet-id parameter as well, which identifies which subnet it belongs to. However, if the configuration has changed, it is possible that a lease could exist with a subnet-id, but without any subnet that matches it. Also, it may be possible that the subnet's configuration has changed and the subnet-id now belongs to a subnet that does not match the lease. Kea's corrective algorithm first checks to see if there is a subnet with the subnet-id specified by the lease. If there is, it verifies whether the lease belongs to that subnet. If not, depending on the lease-checks setting, the lease is discarded, a warning is displayed, or a new subnet is selected for the lease that matches it topologically. Since delegated prefixes do not have to belong to a subnet in which they are offered, there is no way to implement such a mechanism for IPv6 prefixes. As such, the mechanism works for IPv6 addresses only. There are five levels which are supported: - ``none`` - do no special checks; accept the lease as is. - ``warn`` - if problems are detected display a warning, but accept the lease data anyway. This is the default value. - ``fix`` - if a data inconsistency is discovered, try to correct it. If the correction is not successful, the incorrect data will be inserted anyway. - ``fix-del`` - if a data inconsistency is discovered, try to correct it. If the correction is not successful, reject the lease. This setting ensures the data's correctness, but some incorrect data may be lost. Use with care. - ``del`` - this is the strictest mode. If any inconsistency is detected, reject the lease. Use with care. This feature is currently implemented for the memfile backend. Note the sanity check applies to the lease database in memory, not to the lease file, i.e. inconsistent leases will stay in the lease file. An example configuration that sets this parameter looks as follows: :: "Dhcp6": { "sanity-checks": { "lease-checks": "fix-del" }, ... } .. _store-extended-info-v6: Storing Extended Lease Information ---------------------------------- In order to support such features as DHCPv6 Reconfigure (`RFC 3315 `__) and LeaseQuery (`RFC 5007 `__) it is necessary to store additional information with each lease. Because the amount of information stored for each lease has ramifications in terms of performance and system resource consumption, storing this additional information is configurable through the "store-extended-info" parameter. It defaults to false and may be set at the global, shared-network, and subnet levels. :: "Dhcp6": { "store-extended-info": true, ... } When enabled, information relevant to the DHCPv6 query (e.g. REQUEST, RENEW, or REBIND) asking for the lease is added into the lease's user-context as a map element labeled "ISC". Currently the information contained in the map will be a list of relays, one for each relay message layer that encloses the client query. Other values may be added at a future date. The lease's user-context for a two-hop query might look something like this (shown pretty-printed for clarity): :: { "ISC": { "relays": [ { "hop": 2, "link": "2001:db8::1", "peer": "2001:db8::2" }, { "hop": 1, "link": "2001:db8::3", "options": "0x00C800080102030405060708", "peer": "2001:db8::4" }] } } .. note:: This feature is intended to be used in conjunction with an upcoming LeaseQuery hook library and at this time there is other use for this information within Kea. .. note:: It is possible that other hook libraries are already using user-context. Enabling store-extended-info should not interfere with any other user-context content, as long as it does not also use an element labeled "ISC". In other words, user-context is intended to be a flexible container serving multiple purposes. As long as no other purpose also writes an "ISC" element to user-context there should not be a conflict. .. _dhcp6-multi-threading-settings: Multi-Threading Settings ------------------------ The Kea server can be configured to process packets in parallel using multiple threads. These settings can be found under ``multi-threading`` structure and are represented by: - ``enable-multi-threading`` - use multiple threads to process packets in parallel (default false). - ``thread-pool-size`` - specify the number of threads to process packets in parallel. Supported values are: 0 (auto detect), any positive number sets thread count explicitly (default 0). - ``packet-queue-size`` - specify the size of the queue used by the thread pool to process packets. Supported values are: 0 (unlimited), any positive number sets queue size explicitly (default 64). An example configuration that sets these parameter looks as follows: :: "Dhcp6": { "multi-threading": { "enable-multi-threading": true, "thread-pool-size": 4, "packet-queue-size": 16 } ... } Multi-Threading Settings in Different Backends ---------------------------------------------- Both kea-dhcp4 and kea-dhcp6 are tested internally to determine which settings give the best performance. Although this section describes our results, they are merely recommendations and are very dependent on the particular hardware that was used for testing. We strongly advise that administrators run their own performance tests. A full report of performance results for the latest stable Kea can be found `here `_. This includes hardware and test scenario descriptions, as well as current results. After enabling multi-threading, the number of threads is set by ``thread-pool-size`` parameter, and results from our tests show that best configurations for kea-dhcp6 are: - ``thread-pool-size``: 4 when using ``memfile`` for storing leases. - ``thread-pool-size``: 12 or more when using ``mysql`` for storing leases. - ``thread-pool-size``: 6 when using ``postgresql``. Another very important parameter is ``packet-queue-size`` and in our tests we used it as multiplier of ``thread-pool-size``. So actual setting strongly depends on ``thread-pool-size``. Our tests reported best results when: - ``packet-queue-size``: 150 * ``thread-pool-size`` when using ``memfile`` for storing leases. In our case it's 150 * 4 = 600. This means that at any given time, up to 600 packets could be queued. - ``packet-queue-size``: 200 * ``thread-pool-size`` when using ``mysql`` for storing leases. In our case it's 200 * 12 = 2400. This means that up to 2400 packets could be queued. - ``packet-queue-size``: 11 * ``thread-pool-size`` when using ``postgresql`` for storing leases. In our case it's 11 * 6 = 66. Lease Caching ------------- Clients that attempt renewal frequently can cause the server to update and write to the database frequently resulting in a performance impact on the server. The cache parameters instruct the DHCP server to avoid updating leases too frequently thus avoiding this behavior. Instead the server assigns the same lease (i.e. reuses it) with no modifications except for CLTT (Client Last Transmission Time) which does not require disk operations. The two parameters are the ``cache-threshold`` double and the ``cache-max-age`` integer and have no default, i.e. the lease caching feature must be explicitly enabled. These parameters can be configured at the global, shared network and subnet levels. The subnet level has the precedence on the shared network level, the global level is used as last resort. For example: :: "subnet6": [ { "subnet": "2001:db8:1:1::/64", "pools": [ { "pool": "2001:db8:1:1::1:0/112" } ], "cache-threshold": .25, "cache-max-age": 600, "valid-lifetime": 2000, ... } ], When an already assigned lease can fulfill a client query: - any important change e.g. for DDNS parameter, hostname, or preferred or valid lifetime reduction makes the lease not reusable - lease age i.e. the difference between the creation or last modification time and the current time is computed (elapsed duration) - if ``cache-max-age`` is explicitly configured, it is compared with the age and leases that are too old are not reusable (this means that the value 0 for ``cache-max-age`` disables the lease cache feature) - if ``cache-threshold`` is explicitly configured and is between 0.0 and 1.0, it expresses the percentage of the lease valid lifetime which is allowed for the lease age. Values below and including 0.0 and values greater than 1.0 disable the lease cache feature. In the example a lease with a valid lifetime of 2000 seconds can be reused if it was committed less than 500 seconds ago. With a lifetime of 3000 seconds the maximum age of 600 seconds applies. In outbound client responses (e.g. DHCPV6_REPLY messages) used preferred and valid lifetimes are the reusable values i.e. the expiration dates do not change. .. _host-reservation-v6: Host Reservation in DHCPv6 ========================== There are many cases where it is useful to provide a configuration on a per-host basis. The most obvious one is to reserve a specific, static IPv6 address or/and prefix for exclusive use by a given client (host); the returning client will receive the same address or/and prefix every time, and other clients will never get that address. Another situation when host reservations are applicable is when a host has specific requirements, e.g. a printer that needs additional DHCP options or a cable modem that needs specific parameters. Yet another possible use case is to define unique names for hosts. Note that there may be cases when a new reservation has been made for a client for an address or prefix currently in use by another client. We call this situation a "conflict." These conflicts get resolved automatically over time as described in subsequent sections. Once the conflict is resolved, the correct client will receive the reserved configuration when it renews. Host reservations are defined as parameters for each subnet. Each host must be identified by either DUID or its hardware/MAC address; see :ref:`mac-in-dhcpv6` for details. There is an optional ``reservations`` array in the ``subnet6`` structure; each element in that array is a structure that holds information about a single host. In particular, the structure has an identifier that uniquely identifies a host. In the DHCPv6 context, the identifier is usually a DUID, but it can also be a hardware or MAC address. One or more addresses or prefixes may also be specified, and it is possible to specify a hostname and DHCPv6 options for a given host. .. note:: Kea requires that reserved addresses must be within the subnet. Kea 1.7.10 is the last release that does not enforce this. This does not apply to reserved prefixes. The following example shows how to reserve addresses and prefixes for specific hosts: :: "subnet6": [ { "subnet": "2001:db8:1::/48", "pools": [ { "pool": "2001:db8:1::/80" } ], "pd-pools": [ { "prefix": "2001:db8:1:8000::", "prefix-len": 48, "delegated-len": 64 } ], "reservations": [ { "duid": "01:02:03:04:05:0A:0B:0C:0D:0E", "ip-addresses": [ "2001:db8:1::100" ] }, { "hw-address": "00:01:02:03:04:05", "ip-addresses": [ "2001:db8:1::101", "2001:db8:1::102" ] }, { "duid": "01:02:03:04:05:06:07:08:09:0A", "ip-addresses": [ "2001:db8:1::103" ], "prefixes": [ "2001:db8:2:abcd::/64" ], "hostname": "foo.example.com" } ] } ] This example includes reservations for three different clients. The first reservation is for the address 2001:db8:1::100 for a client using DUID 01:02:03:04:05:0A:0B:0C:0D:0E. The second reservation is for two addresses, 2001:db8:1::101 and 2001:db8:1::102, for a client using MAC address 00:01:02:03:04:05. Lastly, address 2001:db8:1::103 and prefix 2001:db8:2:abcd::/64 are reserved for a client using DUID 01:02:03:04:05:06:07:08:09:0A. The last reservation also assigns a hostname to this client. Note that DHCPv6 allows a single client to lease multiple addresses and multiple prefixes at the same time. Therefore ``ip-addresses`` and ``prefixes`` are plural and are actually arrays. When the client sends multiple IA options (IA_NA or IA_PD), each reserved address or prefix is assigned to an individual IA of the appropriate type. If the number of IAs of a specific type is lower than the number of reservations of that type, the number of reserved addresses or prefixes assigned to the client is equal to the number of IA_NAs or IA_PDs sent by the client; that is, some reserved addresses or prefixes are not assigned. However, they still remain reserved for this client and the server will not assign them to any other client. If the number of IAs of a specific type sent by the client is greater than the number of reserved addresses or prefixes, the server will try to assign all reserved addresses or prefixes to the individual IAs and dynamically allocate addresses or prefixes to the remaining IAs. If the server cannot assign a reserved address or prefix because it is in use, the server will select the next reserved address or prefix and try to assign it to the client. If the server subsequently finds that there are no more reservations that can be assigned to the client at that moment, the server will try to assign leases dynamically. Making a reservation for a mobile host that may visit multiple subnets requires a separate host definition in each subnet that host is expected to visit. It is not possible to define multiple host definitions with the same hardware address in a single subnet. Multiple host definitions with the same hardware address are valid if each is in a different subnet. The reservation for a given host should include only one identifier, either DUID or hardware address; defining both for the same host is considered a configuration error. Adding host reservations incurs a performance penalty. In principle, when a server that does not support host reservation responds to a query, it needs to check whether there is a lease for a given address being considered for allocation or renewal. The server that does support host reservation has to perform additional checks: not only whether the address is currently used (i.e., if there is a lease for it), but also whether the address could be used by someone else (i.e., if there is a reservation for it). That additional check incurs extra overhead. .. _reservation6-types: Address/Prefix Reservation Types -------------------------------- In a typical scenario there is an IPv6 subnet defined, with a certain part of it dedicated for dynamic address allocation by the DHCPv6 server. There may be an additional address space defined for prefix delegation. Those dynamic parts are referred to as dynamic pools, address and prefix pools, or simply pools. In principle, a host reservation can reserve any address or prefix that belongs to the subnet. The reservations that specify addresses that belong to configured pools are called "in-pool reservations." In contrast, those that do not belong to dynamic pools are called "out-of-pool reservations." There is no formal difference in the reservation syntax and both reservation types are handled uniformly. Kea supports global host reservations. These are reservations that are specified at the global level within the configuration and that do not belong to any specific subnet. Kea will still match inbound client packets to a subnet as before, but when the subnet's reservation mode is set to ``"global"``, Kea will look for host reservations only among the global reservations defined. Typically, such reservations would be used to reserve hostnames for clients which may move from one subnet to another. .. note:: Global reservations, while useful in certain circumstances, have aspects that must be given due consideration when using them. Please see :ref:`reservation6-conflict` for more details. .. note:: Beginning with Kea 1.9.1 reservation mode was replaced by three boolean flags ``"reservations-global"``, ``"reservations-in-subnet"`` and ``"reservations-out-of-pool"`` which allows the configuration of host reservations both globally and in a subnet. In such cases a subnet host reservation has preference over a global reservation when both exist for the same client. .. _reservation6-conflict: Conflicts in DHCPv6 Reservations -------------------------------- As reservations and lease information are stored separately, conflicts may arise. Consider the following series of events: the server has configured the dynamic pool of addresses from the range of 2001:db8::10 to 2001:db8::20. Host A requests an address and gets 2001:db8::10. Now the system administrator decides to reserve address 2001:db8::10 for Host B. In general, reserving an address that is currently assigned to someone else is not recommended, but there are valid use cases where such an operation is warranted. The server now has a conflict to resolve. If Host B boots up and requests an address, the server is not able to assign the reserved address 2001:db8::10. A naive approach would to be immediately remove the lease for Host A and create a new one for Host B. That would not solve the problem, though, because as soon as Host B gets the address, it will detect that the address is already in use (by Host A) and will send a DHCPDECLINE message. Therefore, in this situation, the server has to temporarily assign a different address from the dynamic pool (not matching what has been reserved) to Host B. When Host A renews its address, the server will discover that the address being renewed is now reserved for someone else - Host B. The server will remove the lease for 2001:db8::10, select a new address, and create a new lease for it. It will send two addresses in its response: the old address, with lifetime set to 0 to explicitly indicate that it is no longer valid; and the new address, with a non-zero lifetime. When Host B tries to renew its temporarily assigned address, the server will detect that the existing lease does not match the reservation, so it will release the current address Host B has and will create a new lease matching the reservation. As before, the server will send two addresses: the temporarily assigned one with zeroed lifetimes, and the new one that matches the reservation with proper lifetimes set. This recovery will succeed, even if other hosts attempt to get the reserved address. If Host C requests the address 2001:db8::10 after the reservation is made, the server will propose a different address. This recovery mechanism allows the server to fully recover from a case where reservations conflict with existing leases; however, this procedure will take roughly as long as the value set for renew-timer. The best way to avoid such recovery is not to define new reservations that conflict with existing leases. Another recommendation is to use out-of-pool reservations. If the reserved address does not belong to a pool, there is no way that other clients can get it. .. note:: The conflict-resolution mechanism does not work for global reservations. Although the global address reservations feature may be useful in certain settings, it is generally recommended not to use global reservations for addresses. Administrators who do choose to use global reservations must manually ensure that the reserved addresses are not in dynamic pools. .. _reservation6-hostname: Reserving a Hostname -------------------- When the reservation for a client includes the ``hostname``, the server will assign this hostname to the client and send it back in the Client FQDN, if the client sent the FQDN option to the server. The reserved hostname always takes precedence over the hostname supplied by the client (via the FQDN option) or the autogenerated (from the IPv6 address) hostname. The server qualifies the reserved hostname with the value of the ``ddns-qualifying-suffix`` parameter. For example, the following subnet configuration: :: "subnet6": [ { "subnet": "2001:db8:1::/48", "pools": [ { "pool": "2001:db8:1::/80" } ], "ddns-qualifying-suffix": "example.isc.org.", "reservations": [ { "duid": "01:02:03:04:05:0A:0B:0C:0D:0E", "ip-addresses": [ "2001:db8:1::100" ] "hostname": "alice-laptop" } ] } ], "dhcp-ddns": { "enable-updates": true } will result in assigning the "alice-laptop.example.isc.org." hostname to the client using the DUID "01:02:03:04:05:0A:0B:0C:0D:0E". If the ``ddns-qualifying-suffix`` is not specified, the default (empty) value will be used, and in this case the value specified as a ``hostname`` will be treated as a fully qualified name. Thus, by leaving the ``ddns-qualifying-suffix`` empty it is possible to qualify hostnames for different clients with different domain names: :: "subnet6": [ { "subnet": "2001:db8:1::/48", "pools": [ { "pool": "2001:db8:1::/80" } ], "reservations": [ { "duid": "01:02:03:04:05:0A:0B:0C:0D:0E", "ip-addresses": [ "2001:db8:1::100" ] "hostname": "mark-desktop.example.org." } ] } ], "dhcp-ddns": { "enable-updates": true, } The above example results in the assignment of the "mark-desktop.example.org." hostname to the client using the DUID "01:02:03:04:05:0A:0B:0C:0D:0E". .. _reservation6-options: Including Specific DHCPv6 Options in Reservations ------------------------------------------------- Kea offers the ability to specify options on a per-host basis. These options follow the same rules as any other options. These can be standard options (see :ref:`dhcp6-std-options`), custom options (see :ref:`dhcp6-custom-options`), or vendor-specific options (see :ref:`dhcp6-vendor-opts`). The following example demonstrates how standard options can be defined. :: "reservations": [ { "duid": "01:02:03:05:06:07:08", "ip-addresses": [ "2001:db8:1::2" ], "option-data": [ { "option-data": [ { "name": "dns-servers", "data": "3000:1::234" }, { "name": "nis-servers", "data": "3000:1::234" } } ] } ] Vendor-specific options can be reserved in a similar manner: :: "reservations": [ { "duid": "aa:bb:cc:dd:ee:ff", "ip-addresses": [ "2001:db8::1" ], "option-data": [ { "name": "vendor-opts", "data": 4491 }, { "name": "tftp-servers", "space": "vendor-4491", "data": "3000:1::234" } ] } ] Options defined at host level have the highest priority. In other words, if there are options defined with the same type on global, subnet, class, and host levels, the host-specific values will be used. .. _reservation6-client-classes: Reserving Client Classes in DHCPv6 ---------------------------------- :ref:`classification-using-expressions` explains how to configure the server to assign classes to a client, based on the content of the options that this client sends to the server. Host reservations mechanisms also allow for the static assignment of classes to clients. The definitions of these classes are placed in the Kea configuration or a database. The following configuration snippet shows how to specify that a client belongs to classes ``reserved-class1`` and ``reserved-class2``. Those classes are associated with specific options sent to the clients which belong to them. :: { "client-classes": [ { "name": "reserved-class1", "option-data": [ { "name": "dns-servers", "data": "2001:db8:1::50" } ] }, { "name": "reserved-class2", "option-data": [ { "name": "nis-servers", "data": "2001:db8:1::100" } ] } ], "subnet6": [ { "pools": [ { "pool": "2001:db8:1::/64" } ], "subnet": "2001:db8:1::/48", "reservations": [ { "duid": "01:02:03:04:05:06:07:08", "client-classes": [ "reserved-class1", "reserved-class2" ] } ] } ] } In some cases the host reservations can be used in conjunction with client classes specified within the Kea configuration. In particular, when a host reservation exists for a client within a given subnet, the "KNOWN" built-in class is assigned to the client. Conversely, when there is no static assignment for the client, the "UNKNOWN" class is assigned to the client. Class expressions within the Kea configuration file can refer to "KNOWN" or "UNKNOWN" classes using the "member" operator. For example: :: { "client-classes": [ { "name": "dependent-class", "test": "member('KNOWN')", "only-if-required": true } ] } Note that the ``only-if-required`` parameter is needed here to force evaluation of the class after the lease has been allocated and thus the reserved class has been also assigned. .. note:: Be aware that the classes specified in non-global host reservations are assigned to the processed packet after all classes with the ``only-if-required`` parameter set to ``false`` have been evaluated. This has an implication that these classes must not depend on the statically assigned classes from the host reservations. If there is a need to create such dependency, the ``only-if-required`` must be set to ``true`` for the dependent classes. Such classes are evaluated after the static classes have been assigned to the packet. This, however, imposes additional configuration overhead, because all classes marked as ``only-if-required`` must be listed in the ``require-client-classes`` list for every subnet where they are used. .. note:: Client classes specified within the Kea configuration file may depend on the classes specified within the global host reservations. In such a case the ``only-if-required`` parameter is not needed. Refer to the :ref:`pool-selection-with-class-reservations6` and :ref:`subnet-selection-with-class-reservations6` for the specific use cases. .. _reservations6-mysql-pgsql-cql: Storing Host Reservations in MySQL, PostgreSQL, or Cassandra ------------------------------------------------------------ It is possible to store host reservations in MySQL, PostgreSQL, or Cassandra. See :ref:`hosts6-storage` for information on how to configure Kea to use reservations stored in MySQL, PostgreSQL, or Cassandra. Kea provides a dedicated hook for managing reservations in a database; section :ref:`host-cmds` provides detailed information. The `Kea wiki `__ provides some examples of how to conduct common host reservations operations. .. note:: In Kea, the maximum length of an option specified per-host is arbitrarily set to 4096 bytes. .. _reservations6-tuning: Fine-Tuning DHCPv6 Host Reservation ----------------------------------- The host reservation capability introduces additional restrictions for the allocation engine (the component of Kea that selects an address for a client) during lease selection and renewal. In particular, three major checks are necessary. First, when selecting a new lease, it is not sufficient for a candidate lease to simply not be in use by another DHCP client; it also must not be reserved for another client. Second, when renewing a lease, an additional check must be performed to see whether the address being renewed is reserved for another client. Finally, when a host renews an address or a prefix, the server must check whether there is a reservation for this host, so the existing (dynamically allocated) address should be revoked and the reserved one be used instead. Some of those checks may be unnecessary in certain deployments and not performing them may improve performance. The Kea server provides the ``reservation-mode`` configuration parameter to select the types of reservations allowed for a particular subnet. Each reservation type has different constraints for the checks to be performed by the server when allocating or renewing a lease for the client. Allowed values are: - ``all`` - enables both in-pool and out-of-pool host reservation types. This setting is the default value, and is the safest and most flexible. However, as all checks are conducted, it is also the slowest. It does not check against global reservations. - ``out-of-pool`` - allows only out-of-pool host reservations. With this setting in place, the server may assume that all host reservations are for addresses that do not belong to the dynamic pool. Therefore, it can skip the reservation checks when dealing with in-pool addresses, thus improving performance. Do not use this mode if any reservations use in-pool addresses. Caution is advised when using this setting; Kea does not sanity-check the reservations against ``reservation-mode`` and misconfiguration may cause problems. - ``global`` - allows only global host reservations. With this setting in place, the server searches for reservations for a client only among the defined global reservations. If an address is specified, the server skips the reservation checks carried out when dealing in other modes, thus improving performance. Caution is advised when using this setting; Kea does not sanity-check the reservations when ``global`` and misconfiguration may cause problems. - ``disabled`` - host reservation support is disabled. As there are no reservations, the server will skip all checks. Any reservations defined will be completely ignored. As the checks are skipped, the server may operate faster in this mode. Since Kea 1.9.1, the ``reservation-mode`` is replaced by the ``reservations-global``, ``reservations-in-subnet`` and ``reservations-out-of-pool`` flags. The flags can be activated independently and can produce various combinations, some of them being unsupported by the deprecated ``reservation-mode``. The ``reservation-mode`` parameter can be specified at: - global level: ``.Dhcp6["reservation-mode"]`` (lowest priority: gets overridden by all others) - subnet level: ``.Dhcp6.subnet6[]["reservation-mode"]`` (low priority) - shared-network level: ``.Dhcp6["shared-networks"][]["reservation-mode"]`` (high priority) - shared-network subnet-level: ``.Dhcp6["shared-networks"][].subnet6[]["reservation-mode"]`` (highest priority: overrides all others) To decide which ``"reservation-mode"`` to choose, the following decision diagram may be useful: :: O | v +-----------------------------+------------------------------+ | Is per-host configuration needed, such as | | reserving specific addresses, | | assigning specific options or | | assigning packets to specific classes on per-device basis? | +-+-----------------+----------------------------------------+ | | no| yes| | | +--------------------------------------+ | | | For all given hosts, | +--> "disabled" +-->+ can the reserved resources | | be used in all configured subnets? | +--------+---------------------------+-+ | | +----------------------------+ |no |yes | Is | | | | at least one reservation +<--+ "global" <--+ | used to reserve addresses | | or prefixes? | +-+------------------------+-+ | | no| yes| +---------------------------+ | | | Is high leases-per-second | +--> "out-of-pool" +-->+ performance or efficient | ^ | resource usage | | | (CPU ticks, RAM usage, | | | database roundtrips) | | | important to your setup? | | +-+----------------+--------+ | | | | yes| no| | | | | +-------------+ | | | | | | +----------------------+ | | | | Can it be guaranteed | | | +-->+ that the reserved | | | | addresses/prefixes | | | | aren't part of the | | | | pools configured | | | | in the respective | | | | subnet? | | | +-+------------------+-+ | | | | | | yes| no| | | | | V +----------------+ +--> "all" An example configuration that disables reservations looks as follows: .. code-block:: json { "Dhcp6": { "subnet6": [ { "pools": [ { "pool": "2001:db8:1::-2001:db8:1::100" } ], "reservation-mode": "disabled", "subnet": "2001:db8:1::/64" } ] } } An example configuration using global reservations is shown below: .. code-block:: json { "Dhcp6": { "reservation-mode": "global", "reservations": [ { "duid": "00:03:00:01:11:22:33:44:55:66", "hostname": "host-one" }, { "duid": "00:03:00:01:99:88:77:66:55:44", "hostname": "host-two" } ], "subnet6": [ { "pools": [ { "pool": "2001:db8:1::-2001:db8:1::100" } ], "subnet": "2001:db8:1::/64" } ] } } The meaning of the reservation flags are: - ``reservations-global``: fetch global reservations. - ``reservations-in-subnet``: fetch subnet reservations. For a shared network this includes all subnet members of the shared network. - ``reservations-out-of-pool``: this makes sense only when the ``reservations-in-subnet`` flag is true. When ``reservations-out-of-pool`` is true the server may assume that all host reservations are for addresses that do not belong to the dynamic pool. Therefore, it can skip the reservation checks when dealing with in-pool addresses, thus improving performance. Also the server will not assign reserved addresses that are inside the dynamic pools to the respective clients. This also means that the addresses matching the respective reservations from inside the dynamic pools (if any) can be dynamically assigned to any client. The ``reservation-mode`` will be deprecated in a future Kea version. The correspondence of old values are: ``disabled``: .. code-block:: json { "Dhcp6": { "reservations-global": false, "reservations-in-subnet": false } } ``global``: .. code-block:: json { "Dhcp6": { "reservations-global": true, "reservations-in-subnet": false } } ``out-of-pool``: .. code-block:: json { "Dhcp6": { "reservations-global": false, "reservations-in-subnet": true, "reservations-out-of-pool": true } } ``all``: .. code-block:: json { "Dhcp6": { "reservations-global": false, "reservations-in-subnet": true, "reservations-out-of-pool": false } } To activate both ``global`` and ``all``, the following combination can be used: .. code-block:: json { "Dhcp6": { "reservations-global": true, "reservations-in-subnet": true, "reservations-out-of-pool": false } } To activate both ``global`` and ``out-of-pool``, the following combination can be used: .. code-block:: json { "Dhcp6": { "reservations-global": true, "reservations-in-subnet": true, "reservations-out-of-pool": true } } Note that enabling ``out-of-pool`` and disabling ``in-subnet`` at the same time is not recommended because ``out-of-pool`` is about host reservations in a subnet which are fetched only when the ``in-subnet`` flag is true. The parameter can be specified at global, subnet, and shared-network levels. An example configuration that disables reservations looks as follows: .. code-block:: json { "Dhcp6": { "subnet6": [ { "reservations-global": false, "reservations-in-subnet": false, "subnet": "2001:db8:1::/64" } ] } } An example configuration using global reservations is shown below: .. code-block:: json { "Dhcp6": { "reservations": [ { "duid": "00:03:00:01:11:22:33:44:55:66", "hostname": "host-one" }, { "duid": "00:03:00:01:99:88:77:66:55:44", "hostname": "host-two" } ], "reservations-global": true, "reservations-in-subnet": false, "subnet6": [ { "pools": [ { "pool": "2001:db8:1::-2001:db8:1::100" } ], "subnet": "2001:db8:1::/64" } ] } } For more details regarding global reservations, see :ref:`global-reservations6`. Another aspect of host reservations is the different types of identifiers. Kea currently supports two types of identifiers in DHCPv6: hardware address and DUID. This is beneficial from a usability perspective; however, there is one drawback. For each incoming packet Kea has to extract each identifier type and then query the database to see if there is a reservation by this particular identifier. If nothing is found, the next identifier is extracted and the next query is issued. This process continues until either a reservation is found or all identifier types have been checked. Over time, with an increasing number of supported identifier types, Kea would become slower and slower. To address this problem, a parameter called ``host-reservation-identifiers`` is available. It takes a list of identifier types as a parameter. Kea will check only those identifier types enumerated in host-reservation-identifiers. From a performance perspective, the number of identifier types should be kept to a minimum, ideally one. If the deployment uses several reservation types, please enumerate them from most- to least-frequently used, as this increases the chances of Kea finding the reservation using the fewest queries. An example of host reservation identifiers looks as follows: :: "host-reservation-identifiers": [ "duid", "hw-address" ], "subnet6": [ { "subnet": "2001:db8:1::/64", ... } ] If not specified, the default value is: :: "host-reservation-identifiers": [ "hw-address", "duid" ] .. _global-reservations6: Global Reservations in DHCPv6 ----------------------------- In some deployments, such as mobile, clients can roam within the network and certain parameters must be specified regardless of the client's current location. To facilitate such a need, a global reservation mechanism has been implemented. The idea behind it is that regular host reservations are tied to specific subnets, by using a specific subnet-id. Kea can specify a global reservation that can be used in every subnet that has global reservations enabled. This feature can be used to assign certain parameters, such as hostname or other dedicated, host-specific options. It can also be used to assign addresses or prefixes. However, global reservations that assign either of these bypass the whole topology determination provided by DHCP logic implemented in Kea. It is very easy to misuse this feature and get a configuration that is inconsistent. To give a specific example, imagine a global reservation for an address 2001:db8:1111::1 and two subnets 2001:db8:1111::/48 and 2001:db8:ffff::/48. If global reservations are used in both subnets and a device matching global host reservations visits part of the network that is covered by 2001:db8:ffff::/48, it will get an IP address 2001:db8:ffff::1, which will be outside of the prefix announced by its local router using Router Advertisements. Such a configuration is unusable or, at the very least, riddled with issues, such as downlink traffic not reaching the device. To use global host reservations, a configuration similar to the following can be used: :: "Dhcp6:" { # This specifies global reservations. # They will apply to all subnets that # have global reservations enabled. "reservations": [ { "hw-address": "aa:bb:cc:dd:ee:ff", "hostname": "hw-host-dynamic" }, { "hw-address": "01:02:03:04:05:06", "hostname": "hw-host-fixed", # Use of IP addresses in global reservations is risky. # If used outside of matching subnet, such as 3001::/64, # it will result in a broken configuration being handed # to the client. "ip-address": "2001:db8:ff::77" }, { "duid": "01:02:03:04:05", "hostname": "duid-host" } ], "valid-lifetime": 600, "subnet4": [ { "subnet": "2001:db8:1::/64", # It is replaced by the "reservations-global" # "reservations-in-subnet" and "reservations-out-of-pool" # parameters. # "reservation-mode": "global", # Specify if the server should lookup global reservations. "reservations-global": true, # Specify if the server should lookup in-subnet reservations. "reservations-in-subnet": false, # Specify if the server can assume that all reserved addresses # are out-of-pool. It can be ignored because "reservations-in-subnet" # is false. # "reservations-out-of-pool": false, "pools": [ { "pool": "2001:db8:1::-2001:db8:1::100" } ] } ] } When using database backends, the global host reservations are distinguished from regular reservations by using subnet-id value of zero. .. _pool-selection-with-class-reservations6: Pool Selection with Client Class Reservations --------------------------------------------- Client classes can be specified both in the Kea configuration file and/or host reservations. The classes specified in the Kea configuration file are evaluated immediately after receiving the DHCP packet and therefore can be used to influence subnet selection using the ``client-class`` parameter specified in the subnet scope. The classes specified within the host reservations are fetched and assigned to the packet after the server has already selected a subnet for the client. This means that the client class specified within a host reservation cannot be used to influence subnet assignment for this client, unless the subnet belongs to a shared network. If the subnet belongs to a shared network, the server may dynamically change the subnet assignment while trying to allocate a lease. If the subnet does not belong to a shared network, once selected, the subnet is not changed. If the subnet does not belong to a shared network, it is possible to use host reservation based client classification to select an address pool within the subnet as follows: :: "Dhcp6": { "client-classes": [ { "name": "reserved_class" }, { "name": "unreserved_class", "test": "not member('reserved_class')" } ], "subnet6": [ { "subnet": "2001:db8:1::/64", "reservations": [{" "hw-address": "aa:bb:cc:dd:ee:fe", "client-classes": [ "reserved_class" ] }], "pools": [ { "pool": "2001:db8:1::10-2001:db8:1::20", "client-class": "reserved_class" }, { "pool": "2001:db8:1::30-2001:db8:1::40", "client-class": "unreserved_class" } ] } ] } The ``reserved_class`` is declared without the ``test`` parameter because it may be only assigned to the client via host reservation mechanism. The second class, ``unreserved_class``, is assigned to the clients which do not belong to the ``reserved_class``. The first pool within the subnet is only used for the clients having a reservation for the ``reserved_class``. The second pool is used for the clients not having such reservation. The configuration snippet includes one host reservation which causes the client having the MAC address of aa:bb:cc:dd:ee:fe to be assigned to the ``reserved_class``. Thus, this client will be given an IP address from the first address pool. .. _subnet-selection-with-class-reservations6: Subnet Selection with Client Class Reservations ----------------------------------------------- There is one specific use case when subnet selection may be influenced by client classes specified within host reservations. This is the case when the client belongs to a shared network. In such a case it is possible to use classification to select a subnet within this shared network. Consider the following example: :: "Dhcp6": { "client-classes": [ { "name": "reserved_class" }, { "name: "unreserved_class", "test": "not member('reserved_class')" } ], "reservations": [{" "hw-address": "aa:bb:cc:dd:ee:fe", "client-classes": [ "reserved_class" ] }], # It is replaced by the "reservations-global" # "reservations-in-subnet" and "reservations-out-of-pool" parameters. # Specify if the server should lookup global reservations. "reservations-global": true, # Specify if the server should lookup in-subnet reservations. "reservations-in-subnet": false, # Specify if the server can assume that all reserved addresses # are out-of-pool. It can be ignored because "reservations-in-subnet" # is false, but if specified, it is inherited by "shared-networks" # and "subnet6" levels. # "reservations-out-of-pool": false, "shared-networks": [{ "subnet6": [ { "subnet": "2001:db8:1::/64", "pools": [ { "pool": "2001:db8:1::10-2001:db8:1::20", "client-class": "reserved_class" } ] }, { "subnet": "2001:db8:2::/64", "pools": [ { "pool": "2001:db8:2::10-2001:db8:2::20", "client-class": "unreserved_class" } ] } ] }] } This is similar to the example described in the :ref:`pool-selection-with-class-reservations6`. This time, however, there are two subnets, each of them having a pool associated with a different class. The clients which don't have a reservation for the ``reserved_class`` will be assigned an address from the subnet 2001:db8:2::/64. Clients having a reservation for the ``reserved_class`` will be assigned an address from the subnet 2001:db8:1::/64. The subnets must belong to the same shared network. In addition, the reservation for the client class must be specified at the global scope (global reservation) and the ``reservations-global`` must be set to true. In the example above the ``client-class`` could also be specified at the subnet level rather than pool level yielding the same effect. .. _multiple-reservations-same-ip6: Multiple Reservations for the Same IP ------------------------------------- Host Reservations were designed to preclude creation of multiple reservations for the same IP address or delegated prefix within a particular subnet to avoid the situation when two different clients compete for the same lease. When using the default settings, the server returns a configuration error when it finds two or more reservations for the same lease within a subnet in the Kea configuration file. The :ref:`host-cmds` hooks library returns an error in response to the ``reservation-add`` command when it detects that the reservation exists in the database for the lease for which the new reservation is being added. Similar to DHCPv4 (see :ref:`multiple-reservations-same-ip4`), the DHCPv6 server can also be configured to allow creating multiple reservations for the same IPv6 address and/or delegated prefix in a given subnet. This is supported beginning with Kea release 1.9.1 as an optional mode of operation enabled with the ``ip-reservations-unique`` global parameter. The ``ip-reservations-unique`` is a boolean parameter, which defaults to ``true``, which forbids the specification of more than one reservation for the same lease in a given subnet. Setting this parameter to ``false`` allows for creating such reservations both in the Kea configuration file and in the host database backends via ``host-cmds`` hooks library. This setting is currently supported by the most popular host database backends, i.e. MySQL and PostgreSQL. It is not supported for Cassandra, Host Cache (see :ref:`hooks-host-cache`) or Radius backend (see :ref:`hooks-radius`). An attempt to set ``ip-reservations-unique`` to ``false`` when any of these three backends is in use yields a configuration error. .. note:: When ``ip-reservations-unique`` is set to ``true`` (the default value) the server ensures that IP reservations are unique for a subnet within a single host backend and/or Kea configuration file. It does not guarantee that the reservations are unique across multiple backends. The following is the example configuration with two reservations for the same IPv6 address and for different MAC addresses: :: "Dhcp6": { "ip-reservations-unique": false, "subnet6": [ { "subnet": "2001:db8:1::/64", "reservations": [ { "hw-address": "1a:1b:1c:1d:1e:1f", "ip-address": "2001:db8:1::11" }, { "hw-address": "2a:2b:2c:2d:2e:2f", "ip-address": "2001:db8:1::11" } ] } ] } It is possible to control the ``ip-reservations-unique`` via the :ref:`dhcp6-cb`. If the new setting of this parameter conflicts with the currently used backends (backends do not support the new setting), the new setting is ignored and the warning log message is output. The backends continue to use the default setting, i.e. expecting that IP reservations are unique within each subnet. To allow the creation of non-unique IP reservations, the administrator must remove the backends which lack support for them from the configuration file. Administrators must be careful when they have been using multiple reservations for the same IP address and/or delegated prefix and later decide to return to the default mode in which this is no longer allowed. The administrators must make sure that at most one reservation for the given IP address or delegated prefix exists within a subnet prior to switching back to the default mode. If such duplicates are left in the configuration file, the server reports a configuration error. Leaving such reservations in the host databases does not cause configuration errors but may lead to lease allocation errors during the server operation, when it unexpectedly finds multiple reservations for the same IP address or delegated prefix. .. note:: Currently the server does not verify whether multiple reservations for the same IP address and/or delegated prefix exist in the host databases (MySQL and/or PostgreSQL) when ``ip-reservations-unique`` is updated from ``true`` to ``false``. This may cause issues with lease allocations. The administrator must ensure that there is at most one reservation for each IP address and/or delegated prefix within each subnet prior to this configuration update. .. _shared-network6: Shared Networks in DHCPv6 ========================= DHCP servers use subnet information in two ways. First, it is used to determine the point of attachment, or where the client is connected to the network. Second, the subnet information is used to group information pertaining to a specific location in the network. This approach works well in general, but there are scenarios where the boundaries are blurred. Sometimes it is useful to have more than one logical IP subnet being deployed on the same physical link. Understanding that two or more subnets are used on the same link requires additional logic in the DHCP server. This capability is called "shared networks" in the Kea and ISC DHCP projects. (It is sometimes also called "shared subnets"; in Microsoft's nomenclature it is called "multinet.") There are many use cases where the feature is useful; the most common example in IPv4 is when the server is running out of available addresses in a subnet. This is less common in IPv6, but shared networks are still useful in IPv6. One of the use cases is an exhaustion of IPv6- delegated prefixes within a subnet; another is an experiment with an addressing scheme. With the advent of IPv6 deployment and a vast address space, many organizations split the address space into subnets, deploy it, and then after a while discover that they want to split it differently. In the transition period, they want both old and new addressing to be available; thus the need for more than one subnet on the same physical link. Finally, the case of cable networks is directly applicable in IPv6. There are two types of devices in cable networks: cable modems and the end-user devices behind them. It is a common practice to use different subnets for cable modems to prevent users from tinkering with them. In this case, the distinction is based on the type of device, rather than on address-space exhaustion. A client connected to a shared network may be assigned a lease (address or prefix) from any of the pools defined within the subnets belonging to the shared network. Internally, the server selects one of the subnets belonging to a shared network and tries to allocate a lease from this subnet. If the server is unable to allocate a lease from the selected subnet (e.g., due to pool exhaustion), it will use another subnet from the same shared network and will try to allocate a lease from this subnet, etc. Therefore, the server will typically allocate all leases available in a given subnet before it starts allocating leases from other subnets belonging to the same shared network. However, in certain situations the client can be allocated a lease from the other subnets before the pools in the first subnet get exhausted; this sometimes occurs when the client provides a hint that belongs to another subnet, or the client has reservations in a subnet other than the default. .. note:: Deployments should not assume that Kea waits until it has allocated all the addresses from the first subnet in a shared network before allocating addresses from other subnets. In order to define a shared network an additional configuration scope is introduced: :: "Dhcp6": { "shared-networks": [{ # Name of the shared network. It may be an arbitrary string # and it must be unique among all shared networks. "name": "ipv6-lab-1", # The subnet selector can be specified on the shared network # level. Subnets from this shared network will be selected # for clients communicating via relay agent having # the specified IP address. "relay": { "ip-addresses": [ "2001:db8:2:34::1" ] }, # This starts a list of subnets in this shared network. # There are two subnets in this example. "subnet6": [{ "subnet": "2001:db8::/48", "pools": [{ "pool": "2001:db8::1 - 2001:db8::ffff" }] }, { "subnet": "3ffe:ffe::/64", "pools": [{ "pool": "3ffe:ffe::/64" }] }] }], # end of shared-networks # It is likely that in the network there will be a mix of regular, # "plain" subnets and shared networks. It is perfectly valid # to mix them in the same configuration file. # # This is a regular subnet. It is not part of any shared-network. "subnet6": [{ "subnet": "2001:db9::/48", "pools": [{ "pool": "2001:db9::/64" }], "relay": { "ip-addresses": [ "2001:db8:1:2::1" ] } }] } # end of Dhcp6 As demonstrated in the example, it is possible to mix shared and regular ("plain") subnets. Each shared network must have a unique name. This is similar to the ID for subnets, but gives administrators more flexibility. It is used for logging, but also internally for identifying shared networks. In principle it makes sense to define only shared networks that consist of two or more subnets. However, for testing purposes, an empty subnet or a network with just a single subnet is allowed. This is not a recommended practice in production networks, as the shared network logic requires additional processing and thus lowers the server's performance. To avoid unnecessary performance degradation, the shared subnets should only be defined when required by the deployment. Shared networks provide an ability to specify many parameters in the shared network scope that apply to all subnets within it. If necessary, it is possible to specify a parameter in the shared network scope and then override its value in the subnet scope. For example: :: "shared-networks": [ { "name": "lab-network3", "relay": { "ip-addresses": [ "2001:db8:2:34::1" ] }, # This applies to all subnets in this shared network, unless # values are overridden on subnet scope. "valid-lifetime": 600, # This option is made available to all subnets in this shared # network. "option-data": [ { "name": "dns-servers", "data": "2001:db8::8888" } ], "subnet6": [ { "subnet": "2001:db8:1::/48", "pools": [ { "pool": "2001:db8:1::1 - 2001:db8:1::ffff" } ], # This particular subnet uses different values. "valid-lifetime": 1200, "option-data": [ { "name": "dns-servers", "data": "2001:db8::1:2" }, { "name": "unicast", "data": "2001:abcd::1" } ] }, { "subnet": "2001:db8:2::/48", "pools": [ { "pool": "2001:db8:2::1 - 2001:db8:2::ffff" } ], # This subnet does not specify its own valid-lifetime value, # so it is inherited from shared network scope. "option-data": [ { "name": "dns-servers", "data": "2001:db8:cafe::1" } ] } ], } ] In this example, there is a dns-servers option defined that is available to clients in both subnets in this shared network. Also, the valid lifetime is set to 10 minutes (600s). However, the first subnet overrides some of the values (valid lifetime is 20 minutes, different IP address for dns-servers), but also adds its own option (unicast address). Assuming a client asking for a server unicast and dns-servers options is assigned a lease from this subnet, it will get a lease for 20 minutes and dns-servers, and be allowed to use server unicast at address 2001:abcd::1. If the same client is assigned to the second subnet, it will get a 10-minute lease, a dns-servers value of 2001:db8:cafe::1, and no server unicast. Some parameters must be the same in all subnets in the same shared network. This restriction applies to the ``interface`` and ``rapid-commit`` settings. The most convenient way is to define them on the shared network scope, but they can be specified for each subnet. However, care should be taken for each subnet to have the same value. .. note:: There is an inherent ambiguity when using clients that send multiple IA options in a single request and shared-networks whose subnets have different values for options and configuration parameters. The server sequentially processes IA options in the order that they occur in the client's query. If the leases requested in the IA options end up being fulfilled from different subnets then which parameters and options should apply? Currently, the code will use the values from the last subnet of the last IA option fulfilled. We view this largely as a site configuration issue. A shared-network generally means the same physical link, so services configured by options from subnet A should be as easily reachable from subnet B and vice versa. There are a number of ways to avoid this situation: - Use the same values for options and parameters for subnets within the shared-network. - Use subnet selectors or client class guards that ensure that for a single client's query, the same subnet will be used for all IA options in that query. - Avoid using shared-networks with clients that send multiple IA options per query Local and Relayed Traffic in Shared Networks -------------------------------------------- It is possible to specify an interface name at the shared network level to tell the server that this specific shared network is reachable directly (not via relays) using the local network interface. As all subnets in a shared network are expected to be used on the same physical link, it is a configuration error to attempt to define a shared network using subnets that are reachable over different interfaces. In other words, all subnets within the shared network must have the same value of the "interface" parameter. The following configuration is wrong. :: "shared-networks": [ { "name": "office-floor-2", "subnet6": [ { "subnet": "2001:db8::/64", "pools": [ { "pool": "2001:db8::1 - 2001:db8::ffff" } ], "interface": "eth0" }, { "subnet": "3ffe:abcd::/64", "pools": [ { "pool": "3ffe:abcd::1 - 3ffe:abcd::ffff" } ], # Specifying the different interface name is a configuration # error. This value should rather be "eth0" or the interface # name in the other subnet should be "eth1". # "interface": "eth1" } ], } ] To minimize the chance of the configuration errors, it is often more convenient to simply specify the interface name once, at the shared network level, like shown in the example below. :: "shared-networks": [ { "name": "office-floor-2", # This tells Kea that the whole shared network is reachable over a # local interface. This applies to all subnets in this network. "interface": "eth0", "subnet6": [ { "subnet": "2001:db8::/64", "pools": [ { "pool": "2001:db8::1 - 2001:db8::ffff" } ], }, { "subnet": "3ffe:abcd::/64", "pools": [ { "pool": "3ffe:abcd::1 - 3ffe:abcd::ffff" } ] } ], } ] In case of the relayed traffic, the subnets are typically selected using the relay agents' addresses. If the subnets are used independently (not grouped within a shared network) it is allowed to specify different relay addresses for each of these subnets. When multiple subnets belong to a shared network they must be selected via the same relay address and, similarly to the case of the local traffic described above, it is a configuration error to specify different relay addresses for the respective subnets in the shared network. The following configuration is wrong. :: "shared-networks": [ { "name": "kakapo", "subnet6": [ { "subnet": "2001:db8::/64", "relay": { "ip-addresses": [ "2001:db8::1234" ] }, "pools": [ { "pool": "2001:db8::1 - 2001:db8::ffff" } ] }, { "subnet": "3ffe:abcd::/64", "pools": [ { "pool": "3ffe:abcd::1 - 3ffe:abcd::ffff" } ], "relay": { # Specifying a different relay address for this # subnet is a configuration error. In this case # it should be 2001:db8::1234 or the relay address # in the previous subnet should be 3ffe:abcd::cafe. "ip-addresses": [ "3ffe:abcd::cafe" ] } } ] } ] Again, it is better to specify the relay address at the shared network level and this value will be inherited by all subnets belonging to the shared network. :: "shared-networks": [ { "name": "kakapo", "relay": { # This relay address is inherited by both subnets. "ip-addresses": [ "2001:db8::1234" ] }, "subnet6": [ { "subnet": "2001:db8::/64", "pools": [ { "pool": "2001:db8::1 - 2001:db8::ffff" } ] }, { "subnet": "3ffe:abcd::/64", "pools": [ { "pool": "3ffe:abcd::1 - 3ffe:abcd::ffff" } ] } ] } ] Even though it is technically possible to configure two (or more) subnets within the shared network to use different relay addresses, this will almost always lead to a different behavior than what the user would expect. In this case, the Kea server will initially select one of the subnets by matching the relay address in the client's packet with the subnet's configuration. However, it MAY end up using the other subnet (even though it does not match the relay address) if the client already has a lease in this subnet, has a host reservation in this subnet or simply the initially selected subnet has no more addresses available. Therefore, it is strongly recommended to always specify subnet selectors (interface or a relay address) at shared network level if the subnets belong to a shared network, as it is rarely useful to specify them at the subnet level and it may lead to the configuration errors described above. Client Classification in Shared Networks ---------------------------------------- Sometimes it is desirable to segregate clients into specific subnets based on certain properties. This mechanism is called client classification and is described in :ref:`classify`. Client classification can be applied to subnets belonging to shared networks in the same way as it is used for subnets specified outside of shared networks. It is important to understand how the server selects subnets for clients when client classification is in use, to ensure that the desired subnet is selected for a given client type. If a subnet is associated with a class, only the clients belonging to this class can use this subnet. If there are no classes specified for a subnet, any client connected to a given shared network can use this subnet. A common mistake is to assume that the subnet including a client class is preferred over subnets without client classes. Consider the following example: :: { "client-classes": [ { "name": "b-devices", "test": "option[1234].hex == 0x0002" } ], "shared-networks": [ { "name": "galah", "relay": { "ip-address": [ "2001:db8:2:34::1" ] }, "subnet6": [ { "subnet": "2001:db8:1::/64", "pools": [ { "pool": "2001:db8:1::20 - 2001:db8:1::ff" } ], }, { "subnet": "2001:db8:3::/64", "pools": [ { "pool": "2001:db8:3::20 - 2001:db8:3::ff" } ], "client-class": "b-devices" } ] } ] } If the client belongs to the "b-devices" class (because it includes option 1234 with a value of 0x0002), that does not guarantee that the subnet 2001:db8:3::/64 will be used (or preferred) for this client. The server can use either of the two subnets, because the subnet 2001:db8:1::/64 is also allowed for this client. The client classification used in this case should be perceived as a way to restrict access to certain subnets, rather than a way to express subnet preference. For example, if the client does not belong to the "b-devices" class it may only use the subnet 2001:db8:1::/64 and will never use the subnet 2001:db8:3::/64. A typical use case for client classification is in a cable network, where cable modems should use one subnet and other devices should use another subnet within the same shared network. In this case it is necessary to apply classification on all subnets. The following example defines two classes of devices, and the subnet selection is made based on option 1234 values. :: { "client-classes": [ { "name": "a-devices", "test": "option[1234].hex == 0x0001" }, { "name": "b-devices", "test": "option[1234].hex == 0x0002" } ], "shared-networks": [ { "name": "galah", "relay": { "ip-addresses": [ "2001:db8:2:34::1" ] }, "subnet6": [ { "subnet": "2001:db8:1::/64", "pools": [ { "pool": "2001:db8:1::20 - 2001:db8:1::ff" } ], "client-class": "a-devices" }, { "subnet": "2001:db8:3::/64", "pools": [ { "pool": "2001:db8:3::20 - 2001:db8:3::ff" } ], "client-class": "b-devices" } ] } ] } In this example each class has its own restriction. Only clients that belong to class "a-devices" will be able to use subnet 2001:db8:1::/64 and only clients belonging to "b-devices" will be able to use subnet 2001:db8:3::/64. Care should be taken not to define too-restrictive classification rules, as clients that are unable to use any subnets will be refused service. However, this may be a desired outcome if one wishes to provide service only to clients with known properties (e.g. only VoIP phones allowed on a given link). Note that it is possible to achieve an effect similar to the one presented in this section without the use of shared networks. If the subnets are placed in the global subnets scope, rather than in the shared network, the server will still use classification rules to pick the right subnet for a given class of devices. The major benefit of placing subnets within the shared network is that common parameters for the logically grouped subnets can be specified once, in the shared network scope, e.g. the "interface" or "relay" parameter. All subnets belonging to this shared network will inherit those parameters. Host Reservations in Shared Networks ------------------------------------ Subnets that are part of a shared network allow host reservations, similar to regular subnets: :: { "shared-networks": [ { "name": "frog", "relay": { "ip-addresses": [ "2001:db8:2:34::1" ] }, "subnet6": [ { "subnet": "2001:db8:1::/64", "id": 100, "pools": [ { "2001:db8:1::1 - 2001:db8:1::64" } ], "reservations": [ { "duid": "00:03:00:01:11:22:33:44:55:66", "ip-addresses": [ "2001:db8:1::28" ] } ] }, { "subnet": "2001:db8:3::/64", "id": 101, "pools": [ { "pool": "2001:db8:3::1 - 2001:db8:3::64" } ], "reservations": [ { "duid": "00:03:00:01:aa:bb:cc:dd:ee:ff", "ip-addresses": [ "2001:db8:2::28" ] } ] } ] } ] } It is worth noting that Kea conducts additional checks when processing a packet if shared networks are defined. First, instead of simply checking whether there's a reservation for a given client in its initially selected subnet, Kea looks through all subnets in a shared network for a reservation. This is one of the reasons why defining a shared network may impact performance. If there is a reservation for a client in any subnet, that particular subnet will be picked for the client. Although it is technically not an error, it is considered a bad practice to define reservations for the same host in multiple subnets belonging to the same shared network. While not strictly mandatory, it is strongly recommended to use explicit "id" values for subnets if database storage will be used for host reservations. If an ID is not specified, the values for it are auto generated, i.e. it assigns increasing integer values starting from 1. Thus, the auto generated IDs are not stable across configuration changes. .. _dhcp6-serverid: Server Identifier in DHCPv6 =========================== The DHCPv6 protocol uses a "server identifier" (also known as a DUID) to allow clients to discriminate between several servers present on the same link. `RFC 8415 `__ currently defines four DUID types: DUID-LLT, DUID-EN, DUID-LL, and DUID-UUID. The Kea DHCPv6 server generates a server identifier once, upon the first startup, and stores it in a file. This identifier is not modified across restarts of the server and so is a stable identifier. Kea follows the recommendation from `RFC 8415 `__ to use DUID-LLT as the default server identifier. However, ISC has received reports that some deployments require different DUID types, and there is a need to administratively select both the DUID type and/or its contents. The server identifier can be configured using parameters within the ``server-id`` map element in the global scope of the Kea configuration file. The following example demonstrates how to select DUID-EN as a server identifier: :: "Dhcp6": { "server-id": { "type": "EN" }, ... } Currently supported values for the ``type`` parameter are: "LLT", "EN", and "LL", for DUID-LLT, DUID-EN, and DUID-LL respectively. When a new DUID type is selected, the server generates its value and replaces any existing DUID in the file. The server then uses the new server identifier in all future interactions with the clients. .. note:: If the new server identifier is created after some clients have obtained their leases, the clients using the old identifier are not able to renew the leases; the server will ignore messages containing the old server identifier. Clients will continue sending Renew until they transition to the rebinding state. In this state, they will start sending Rebind messages to the multicast address without a server identifier. The server will respond to the Rebind messages with a new server identifier, and the clients will associate the new server identifier with their leases. Although the clients will be able to keep their leases and will eventually learn the new server identifier, this will be at the cost of an increased number of renewals and multicast traffic due to a need to rebind. Therefore, it is recommended that modification of the server identifier type and value be avoided if the server has already assigned leases and these leases are still valid. There are cases when an administrator needs to explicitly specify a DUID value rather than allow the server to generate it. The following example demonstrates how to explicitly set all components of a DUID-LLT. :: "Dhcp6": { "server-id": { "type": "LLT", "htype": 8, "identifier": "A65DC7410F05", "time": 2518920166 }, ... } where: - ``htype`` is a 16-bit unsigned value specifying hardware type, - ``identifier`` is a link-layer address, specified as a string of hexadecimal digits, and - ``time`` is a 32-bit unsigned time value. The hexadecimal representation of the DUID generated as a result of the configuration specified above is: :: 00:01:00:08:96:23:AB:E6:A6:5D:C7:41:0F:05 |type |htype| time | identifier | A special value of 0 for "htype" and "time" is allowed, which indicates that the server should use ANY value for these components. If the server already uses a DUID-LLT, it will use the values from this DUID; if the server uses a DUID of a different type or doesn't yet use any DUID, it will generate these values. Similarly, if the "identifier" is assigned an empty string, the value of the identifier will be generated. Omitting any of these parameters is equivalent to setting them to those special values. For example, the following configuration: :: "Dhcp6": { "server-id": { "type": "LLT", "htype": 0, "identifier": "", "time": 2518920166 }, ... } indicates that the server should use ANY link-layer address and hardware type. If the server is already using DUID-LLT, it will use the link-layer address and hardware type from the existing DUID. If the server is not yet using any DUID, it will use the link-layer address and hardware type from one of the available network interfaces. The server will use an explicit value of time; if it is different than a time value present in the currently used DUID, that value will be replaced, effectively modifying the current server identifier. The following example demonstrates an explicit configuration of a DUID-EN: :: "Dhcp6": { "server-id": { "type": "EN", "enterprise-id": 2495, "identifier": "87ABEF7A5BB545" }, ... } where: - ``enterprise-id`` is a 32-bit unsigned value holding an enterprise number, and - ``identifier`` is a variable- length identifier within DUID-EN. The hexadecimal representation of the DUID-EN created according to the configuration above is: :: 00:02:00:00:09:BF:87:AB:EF:7A:5B:B5:45 |type | ent-id | identifier | As in the case of the DUID-LLT, special values can be used for the configuration of the DUID-EN. If the ``enterprise-id`` is 0, the server will use a value from the existing DUID-EN. If the server is not using any DUID or the existing DUID has a different type, the ISC enterprise id will be used. When an empty string is entered for ``identifier``, the identifier from the existing DUID-EN will be used. If the server is not using any DUID-EN, a new 6-byte-long identifier will be generated. DUID-LL is configured in the same way as DUID-LLT except that the ``time`` parameter has no effect for DUID-LL, because this DUID type only comprises a hardware type and link-layer address. The following example demonstrates how to configure DUID-LL: :: "Dhcp6": { "server-id": { "type": "LL", "htype": 8, "identifier": "A65DC7410F05" }, ... } which will result in the following server identifier: :: 00:03:00:08:A6:5D:C7:41:0F:05 |type |htype| identifier | The server stores the generated server identifier in the following location: [kea-install-dir]/var/lib/kea/kea-dhcp6-serverid. In some uncommon deployments where no stable storage is available, the server should be configured not to try to store the server identifier. This choice is controlled by the value of the ``persist`` boolean parameter: :: "Dhcp6": { "server-id": { "type": "EN", "enterprise-id": 2495, "identifier": "87ABEF7A5BB545", "persist": false }, ... } The default value of the "persist" parameter is ``true``, which configures the server to store the server identifier on a disk. In the example above, the server is configured not to store the generated server identifier on a disk. But if the server identifier is not modified in the configuration, the same value will be used after server restart, because the entire server identifier is explicitly specified in the configuration. .. _data-directory: DHCPv6 Data Directory ===================== The Kea DHCPv6 server puts the server identifier file and the default memory lease file into its data directory. By default this directory is ``prefix/var/lib/kea`` but this location can be changed using the ``data-directory`` global parameter as in: :: "Dhcp6": { "data-directory": "/var/tmp/kea-server6", ... } .. _stateless-dhcp6: Stateless DHCPv6 (Information-Request Message) ============================================== Typically DHCPv6 is used to assign both addresses and options. These assignments (leases) have a state that changes over time, hence their description as stateful. DHCPv6 also supports a stateless mode, where clients request configuration options only. This mode is considered lightweight from the server perspective, as it does not require any state tracking, and carries the stateless name. The Kea server supports stateless mode. Clients can send Information-Request messages and the server sends back answers with the requested options, providing the options are available in the server configuration. The server attempts to use per-subnet options first; if that fails for any reason, it then tries to provide options defined in the global scope. Stateless and stateful mode can be used together. No special configuration directives are required to handle this; simply use the configuration for stateful clients and the stateless clients will get only the options they requested. This usage of global options allows for an interesting case. It is possible to run a server that provides only options and no addresses or prefixes. If the options have the same value in each subnet, the configuration can define required options in the global scope and skip subnet definitions altogether. Here's a simple example of such a configuration: :: "Dhcp6": { "interfaces-config": { "interfaces": [ "ethX" ] }, "option-data": [ { "name": "dns-servers", "data": "2001:db8::1, 2001:db8::2" } ], "lease-database": { "type": "memfile" } } This very simple configuration provides DNS server information to all clients in the network, regardless of their location. Note the specification of the memfile lease database; this is needed as Kea requires a lease database to be specified even if it is not used. .. _dhcp6-rfc7550: Support for RFC 7550 (now part of RFC 8415) =========================================== `RFC 7550 `__ introduced some changes to the previous DHCPv6 specifications, `RFC 3315 `__ and `RFC 3633 `__, to resolve a few issues with the coexistence of multiple stateful options in the messages sent between clients and servers. Those changes were later included in the most recent DHCPv6 protocol specification, `RFC 8415 `__, which obsoleted `RFC 7550 `__. Kea supports `RFC 8415 `__ along with these protocol changes, which are briefly described below. When a client, such as a requesting router, requests an allocation of both addresses and prefixes during the 4-way (SARR) exchange with the server, and the server is not configured to allocate any prefixes but it can allocate some addresses, it will respond with the IA_NA(s) containing allocated addresses and the IA_PD(s) containing the NoPrefixAvail status code. According to the updated specifications, if the client can operate without prefixes it should accept allocated addresses and transition to the "bound" state. When the client subsequently sends Renew/Rebind messages to the server, according to the T1 and T2 times, to extend the lifetimes of the allocated addresses, and if the client is still interested in obtaining prefixes from the server, it may also include an IA_PD in the Renew/Rebind to request allocation of the prefixes. If the server still cannot allocate the prefixes, it will respond with the IA_PD(s) containing the NoPrefixAvail status code. However, if the server can allocate the prefixes it will allocate and send them in the IA_PD(s) to the client. A similar situation occurs when the server is unable to allocate addresses for the client but can delegate prefixes. The client may request allocation of the addresses while renewing the delegated prefixes. Allocating leases for other IA types while renewing existing leases is by default supported by the Kea DHCPv6 server, and the server provides no configuration mechanisms to disable this behavior. The following are the other behaviors first introduced in `RFC 7550 `__ (now part of `RFC 8415 `__) and supported by the Kea DHCPv6 server: - Set T1/T2 timers to the same value for all stateful (IA_NA and IA_PD) options to facilitate renewal of all of a client's leases at the same time (in a single message exchange). - Place NoAddrsAvail and NoPrefixAvail status codes in the IA_NA and IA_PD options in the Advertise message, rather than as the top-level options. .. _dhcp6-relay-override: Using a Specific Relay Agent for a Subnet ========================================= The DHCPv6 server follows the same principles as the DHCPv4 server to select a subnet for the client, with noticeable differences mainly for relays. .. note:: Starting with Kea 1.7.9, the order used to find a subnet which matches required conditions to be selected is the ascending subnet identifier order. When the selected subnet is a member of a shared network the whole shared network is selected. The relay must have an interface connected to the link on which the clients are being configured. Typically the relay has a global IPv6 address configured on that interface, which belongs to the subnet from which the server will assign addresses. Normally, the server is able to use the IPv6 address inserted by the relay (in the link-addr field in RELAY-FORW message) to select the appropriate subnet. However, that is not always the case. The relay address may not match the subnet in certain deployments. This usually means that there is more than one subnet allocated for a given link. The two most common examples where this is the case are long-lasting network renumbering (where both the old and new address spaces are still being used) and a cable network. In a cable network, both cable modems and the devices behind them are physically connected to the same link, yet they use distinct addressing. In such a case, the DHCPv6 server needs additional information (like the value of the interface-id option or the IPv6 address inserted in the link-addr field in the RELAY-FORW message) to properly select an appropriate subnet. The following example assumes that there is a subnet 2001:db8:1::/64 that is accessible via a relay that uses 3000::1 as its IPv6 address. The server is able to select this subnet for any incoming packets that come from a relay that has an address in the 2001:db8:1::/64 subnet. It also selects that subnet for a relay with address 3000::1. :: "Dhcp6": { "subnet6": [ { "subnet": "2001:db8:1::/64", "pools": [ { "pool": "2001:db8:1::1-2001:db8:1::ffff" } ], "relay": { "ip-addresses": [ "3000::1" ] } } ] } If "relay" is specified, the "ip-addresses" parameter within it is mandatory. .. note:: The current version of Kea uses the "ip-addresses" parameter, which supports specifying a list of addresses. .. _dhcp6-client-class-relay: Segregating IPv6 Clients in a Cable Network =========================================== In certain cases, it is useful to mix relay address information (introduced in :ref:`dhcp6-relay-override`), with client classification, explained in :ref:`classify`. One specific example is in a cable network, where modems typically get addresses from a different subnet than all the devices connected behind them. Let us assume that there is one CMTS (Cable Modem Termination System) with one CM MAC (a physical link that modems are connected to). We want the modems to get addresses from the 3000::/64 subnet, while everything connected behind the modems should get addresses from another subnet (2001:db8:1::/64). The CMTS that acts as a relay uses address 3000::1. The following configuration can serve that configuration: :: "Dhcp6": { "subnet6": [ { "subnet": "3000::/64", "pools": [ { "pool": "3000::2 - 3000::ffff" } ], "client-class": "VENDOR_CLASS_docsis3.0", "relay": { "ip-addresses": [ "3000::1" ] } }, { "subnet": "2001:db8:1::/64", "pools": [ { "pool": "2001:db8:1::1-2001:db8:1::ffff" } ], "relay": { "ip-addresses": [ "3000::1" ] } } ] } .. _mac-in-dhcpv6: MAC/Hardware Addresses in DHCPv6 ================================ MAC/hardware addresses are available in DHCPv4 messages from the clients, and administrators frequently use that information to perform certain tasks like per-host configuration and address reservation for specific MAC addresses. Unfortunately, the DHCPv6 protocol does not provide any completely reliable way to retrieve that information. To mitigate that issue, a number of mechanisms have been implemented in Kea. Each of these mechanisms works in certain cases, but may fail in others. Whether the mechanism works in a particular deployment is somewhat dependent on the network topology and the technologies used. Kea allows specification of which of the supported methods should be used and in what order. This configuration may be considered a fine tuning of the DHCP deployment. In a typical deployment the default value of ``"any"`` is sufficient and there is no need to select specific methods. Changing the value of this parameter is most useful in cases when an administrator wants to disable certain methods; for example, if the administrator trusts the network infrastructure more than the information provided by the clients themselves, they may prefer information provided by the relays over that provided by clients. The configuration is controlled by the ``mac-sources`` parameter as follows: :: "Dhcp6": { "mac-sources": [ "method1", "method2", "method3", ... ], "subnet6": [ ... ], ... } When not specified, a special value of "any" is used, which instructs the server to attempt to try all the methods in sequence and use the value returned by the first one that succeeds. If specified, it must have at least one value. Supported methods are: - ``any`` - not an actual method, just a keyword that instructs Kea to try all other methods and use the first one that succeeds. This is the default operation if no ``mac-sources`` are defined. - ``raw`` - in principle, a DHCPv6 server could use raw sockets to receive incoming traffic and extract MAC/hardware address information. This is currently not implemented for DHCPv6 and this value has no effect. - ``duid`` - DHCPv6 uses DUID identifiers instead of MAC addresses. There are currently four DUID types defined, and two of them (DUID-LLT, which is the default, and DUID-LL) convey MAC address information. Although `RFC 8415 `__ forbids it, it is possible to parse those DUIDs and extract necessary information from them. This method is not completely reliable, as clients may use other DUID types, namely DUID-EN or DUID-UUID. - ``ipv6-link-local`` - another possible acquisition method comes from the source IPv6 address. In typical usage, clients are sending their packets from IPv6 link-local addresses. There is a good chance that those addresses are based on EUI-64, which contains a MAC address. This method is not completely reliable, as clients may use other link-local address types. In particular, privacy extensions, defined in `RFC 4941 `__, do not use MAC addresses. Also note that successful extraction requires that the address's u-bit must be set to 1 and its g-bit set to 0, indicating that it is an interface identifier as per `RFC 2373, section 2.5.1 `__. - ``client-link-addr-option`` - one extension defined to alleviate missing MAC issues is the client link-layer address option, defined in `RFC 6939 `__. This is an option that is inserted by a relay and contains information about a client's MAC address. This method requires a relay agent that supports the option and is configured to insert it. This method is useless for directly connected clients. This parameter can also be specified as ``rfc6939``, which is an alias for ``client-link-addr-option``. - ``remote-id`` - `RFC 4649 `__ defines a remote-id option that is inserted by a relay agent. Depending on the relay agent configuration, the inserted option may convey the client's MAC address information. This parameter can also be specified as ``rfc4649``, which is an alias for ``remote-id``. - ``subscriber-id`` - Another option that is somewhat similar to the previous one is subscriber-id, defined in `RFC 4580 `__. It, too, is inserted by a relay agent that is configured to insert it. This parameter can also be specified as ``rfc4580``, which is an alias for ``subscriber-id``. This method is currently not implemented. - ``docsis-cmts`` - Yet another possible source of MAC address information are the DOCSIS options inserted by a CMTS that acts as a DHCPv6 relay agent in cable networks. This method attempts to extract MAC address information from sub-option 1026 (cm mac) of the vendor-specific option with vendor-id=4491. This vendor option is extracted from the relay-forward message, not the original client's message. - ``docsis-modem`` - The final possible source of MAC address information are the DOCSIS options inserted by the cable modem itself. This method attempts to extract MAC address information from sub-option 36 (device id) of the vendor-specific option with vendor-id=4491. This vendor option is extracted from the original client's message, not from any relay options. Empty mac-sources are not allowed. Administrators who do not want to specify it should either simply omit the mac-sources definition or specify it with the "any" value, which is the default. .. _dhcp6-decline: Duplicate Addresses (DECLINE Support) ===================================== The DHCPv6 server is configured with a certain pool of addresses that it is expected to hand out to DHCPv6 clients. It is assumed that the server is authoritative and has complete jurisdiction over those addresses. However, for various reasons, such as misconfiguration or a faulty client implementation that retains its address beyond the valid lifetime, there may be devices connected that use those addresses without the server's approval or knowledge. Such an unwelcome event can be detected by legitimate clients (using Duplicate Address Detection) and reported to the DHCPv6 server using a DHCPDECLINE message. The server will do a sanity check (to see whether the client declining an address really was supposed to use it), and then will conduct a clean-up operation and confirm it by sending back a REPLY message. Any DNS entries related to that address will be removed, the fact will be logged, and hooks will be triggered. After that is complete, the address will be marked as declined (which indicates that it is used by an unknown entity and thus not available for assignment) and a probation time will be set on it. Unless otherwise configured, the probation period lasts 24 hours; after that period, the server will recover the lease (i.e. put it back into the available state) and the address will be available for assignment again. It should be noted that if the underlying issue of a misconfigured device is not resolved, the duplicate-address scenario will repeat. If reconfigured correctly, this mechanism provides an opportunity to recover from such an event automatically, without any system administrator intervention. To configure the decline probation period to a value other than the default, the following syntax can be used: :: "Dhcp6": { "decline-probation-period": 3600, "subnet6": [ ... ], ... } The parameter is expressed in seconds, so the example above will instruct the server to recycle declined leases after one hour. There are several statistics and hook points associated with the decline handling procedure. The lease6_decline hook is triggered after the incoming DHCPDECLINE message has been sanitized and the server is about to decline the lease. The declined-addresses statistic is increased after the hook returns (both global and subnet-specific variants). (See :ref:`dhcp6-stats` and :ref:`hooks-libraries` for more details on DHCPv6 statistics and Kea hook points.) Once the probation time elapses, the declined lease is recovered using the standard expired-lease reclamation procedure, with several additional steps. In particular, both declined-addresses statistics (global and subnet-specific) are decreased. At the same time, reclaimed-declined-addresses statistics (again in two variants, global and subnet-specific) are increased. A note about statistics: The server does not decrease the assigned-nas statistics when a DHCPDECLINE message is received and processed successfully. While technically a declined address is no longer assigned, the primary usage of the assigned-nas statistic is to monitor pool utilization. Most people would forget to include declined-addresses in the calculation, and simply use assigned-nas/total-nas. This would cause a bias towards under-representing pool utilization. As this has a potential for major issues, ISC decided not to decrease assigned-nas immediately after receiving DHCPDECLINE, but to do it later when Kea recovers the address back to the available pool. .. _dhcp6-stats: Statistics in the DHCPv6 Server =============================== The DHCPv6 server supports the following statistics: .. tabularcolumns:: |p{0.2\linewidth}|p{0.1\linewidth}|p{0.7\linewidth}| .. table:: DHCPv6 Statistics :class: longtable :widths: 20 10 70 +-----------------------------------------+-----------------------+------------------------+ | Statistic | Data Type | Description | +=========================================+=======================+========================+ | pkt6-received | integer | Number of DHCPv6 | | | | packets received. | | | | This includes all | | | | packets: valid, | | | | bogus, corrupted, | | | | rejected, etc. This | | | | statistic is expected | | | | to grow rapidly. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-receive-drop | integer | Number of incoming | | | | packets that were | | | | dropped. The exact | | | | reason for dropping | | | | packets is logged, | | | | but the most common | | | | reasons may be: an | | | | unacceptable or not | | | | supported packet type | | | | is received, direct | | | | responses are | | | | forbidden, the | | | | server-id sent by the | | | | client does not match | | | | the server's | | | | server-id, or the | | | | packet is malformed. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-parse-failed | integer | Number of incoming | | | | packets that could | | | | not be parsed. A | | | | non-zero value of | | | | this statistic | | | | indicates that the | | | | server received a | | | | malformed or | | | | truncated packet. | | | | This may indicate | | | | problems in the | | | | network, faulty | | | | clients, faulty relay | | | | agents, or a bug in | | | | the server. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-solicit-received | integer | Number of SOLICIT | | | | packets received. | | | | This statistic is | | | | expected to grow; its | | | | increase means that | | | | clients that just | | | | booted started their | | | | configuration process | | | | and their initial | | | | packets reached the | | | | Kea server. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-advertise-received | integer | Number of ADVERTISE | | | | packets received. | | | | Advertise packets are | | | | sent by the server | | | | and the server is | | | | never expected to | | | | receive them. A | | | | non-zero value of | | | | this statistic | | | | indicates an error | | | | occurring in the | | | | network. One likely | | | | cause would be a | | | | misbehaving relay | | | | agent that | | | | incorrectly forwards | | | | ADVERTISE messages | | | | towards the server, | | | | rather than back to | | | | the clients. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-request-received | integer | Number of DHCPREQUEST | | | | packets received. | | | | This statistic is | | | | expected to grow. Its | | | | increase means that | | | | clients that just | | | | booted received the | | | | server's response | | | | (DHCPADVERTISE) and | | | | accepted it, and are | | | | now requesting an | | | | address | | | | (DHCPREQUEST). | +-----------------------------------------+-----------------------+------------------------+ | pkt6-reply-received | integer | Number of REPLY | | | | packets received. | | | | This statistic is | | | | expected to remain | | | | zero at all times, as | | | | REPLY packets are | | | | sent by the server | | | | and the server is | | | | never expected to | | | | receive them. A | | | | non-zero value | | | | indicates an error. | | | | One likely cause | | | | would be a | | | | misbehaving relay | | | | agent that | | | | incorrectly forwards | | | | REPLY messages | | | | towards the server, | | | | rather than back to | | | | the clients. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-renew-received | integer | Number of RENEW | | | | packets received. | | | | This statistic is | | | | expected to grow; its | | | | increase means that | | | | clients received | | | | their addresses and | | | | prefixes and are | | | | trying to renew them. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-rebind-received | integer | Number of REBIND | | | | packets received. A | | | | non-zero value | | | | indicates that | | | | clients did not | | | | receive responses to | | | | their RENEW messages | | | | (through the regular | | | | lease-renewal | | | | mechanism) and are | | | | attempting to find | | | | any server that is | | | | able to take over | | | | their leases. It may | | | | mean that some | | | | servers' REPLY | | | | messages never | | | | reached the clients. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-release-received | integer | Number of RELEASE | | | | packets received. | | | | This statistic is | | | | expected to grow when | | | | a device is being | | | | shut down in the | | | | network; it indicates | | | | that the address or | | | | prefix assigned is | | | | reported as no longer | | | | needed. Note that | | | | many devices, | | | | especially wireless, | | | | do not send RELEASE | | | | packets either | | | | because of design | | | | choice or due to the | | | | client moving out of | | | | range. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-decline-received | integer | Number of DECLINE | | | | packets received. | | | | This statistic is | | | | expected to remain | | | | close to zero. Its | | | | increase means that a | | | | client leased an | | | | address, but | | | | discovered that the | | | | address is currently | | | | used by an unknown | | | | device in the | | | | network. If this | | | | statistic is growing, | | | | it may indicate a | | | | misconfigured server | | | | or devices that have | | | | statically assigned | | | | conflicting | | | | addresses. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-infrequest-received | integer | Number of | | | | INFORMATION-REQUEST | | | | packets received. | | | | This statistic is | | | | expected to grow if | | | | there are devices | | | | that are using | | | | stateless DHCPv6. | | | | INFORMATION-REQUEST | | | | messages are used by | | | | clients that request | | | | stateless | | | | configuration, i.e. | | | | options and | | | | parameters other than | | | | addresses or | | | | prefixes. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-dhcpv4-query-received | integer | Number of | | | | DHCPv4-QUERY packets | | | | received. This | | | | statistic is expected | | | | to grow if there are | | | | devices that are | | | | using | | | | DHCPv4-over-DHCPv6. | | | | DHCPv4-QUERY messages | | | | are used by DHCPv4 | | | | clients on an | | | | IPv6-only line which | | | | encapsulates the | | | | requests over DHCPv6. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-dhcpv4-response-received | integer | Number of | | | | DHCPv4-RESPONSE | | | | packets received. | | | | This statistic is | | | | expected to remain | | | | zero at all times, as | | | | DHCPv4-RESPONSE | | | | packets are sent by | | | | the server and the | | | | server is never | | | | expected to receive | | | | them. A non-zero | | | | value indicates an | | | | error. One likely | | | | cause would be a | | | | misbehaving relay | | | | agent that | | | | incorrectly forwards | | | | DHCPv4-RESPONSE | | | | message towards the | | | | server rather than | | | | back to the clients. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-unknown-received | integer | Number of packets | | | | received of an | | | | unknown type. A | | | | non-zero value of | | | | this statistic | | | | indicates that the | | | | server received a | | | | packet that it wasn't | | | | able to recognize; | | | | either it had an | | | | unsupported type or | | | | was possibly | | | | malformed. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-sent | integer | Number of DHCPv6 | | | | packets sent. This | | | | statistic is expected | | | | to grow every time | | | | the server transmits | | | | a packet. In general, | | | | it should roughly | | | | match pkt6-received, | | | | as most incoming | | | | packets cause the | | | | server to respond. | | | | There are exceptions | | | | (e.g. server | | | | receiving a REQUEST | | | | with server-id | | | | matching another | | | | server), so do not | | | | worry if it is less | | | | than pkt6-received. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-advertise-sent | integer | Number of ADVERTISE | | | | packets sent. This | | | | statistic is expected | | | | to grow in most cases | | | | after a SOLICIT is | | | | processed. There are | | | | certain uncommon, but | | | | valid, cases where | | | | incoming SOLICIT | | | | packets are dropped, | | | | but in general this | | | | statistic is expected | | | | to be close to | | | | pkt6-solicit-received. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-reply-sent | integer | Number of REPLY | | | | packets sent. This | | | | statistic is expected | | | | to grow in most cases | | | | after a SOLICIT (with | | | | rapid-commit), | | | | REQUEST, RENEW, | | | | REBIND, RELEASE, | | | | DECLINE, or | | | | INFORMATION-REQUEST | | | | is processed. There | | | | are certain cases | | | | where there is no | | | | response. | +-----------------------------------------+-----------------------+------------------------+ | pkt6-dhcpv4-response-sent | integer | Number of | | | | DHCPv4-RESPONSE | | | | packets sent. This | | | | statistic is expected | | | | to grow in most cases | | | | after a DHCPv4-QUERY | | | | is processed. There | | | | are certain cases | | | | where there is no | | | | response. | +-----------------------------------------+-----------------------+------------------------+ | subnet[id].total-nas | integer | Total number of NA | | | | addresses available | | | | for DHCPv6 management | | | | for a given subnet; | | | | in other words, this | | | | is the sum of all | | | | addresses in all | | | | configured pools. | | | | This statistic | | | | changes only during | | | | configuration | | | | changes. Note that it | | | | does not take into | | | | account any addresses | | | | that may be reserved | | | | due to host | | | | reservation. The *id* | | | | is the subnet-id of a | | | | given subnet. This | | | | statistic is exposed | | | | for each subnet | | | | separately, and is | | | | reset during a | | | | reconfiguration | | | | event. | +-----------------------------------------+-----------------------+------------------------+ | cumulative-assigned-nas | integer | Cumulative number of | | | | NA addresses that | | | | have been assigned | | | | since server startup. | | | | It is incremented | | | | each time a NA address | | | | is assigned and is not | | | | reset when the server | | | | is reconfigured. | +-----------------------------------------+-----------------------+------------------------+ | subnet[id].cumulative-assigned-nas | integer | Cumulative number of | | | | NA addresses in a | | | | given subnet that | | | | were assigned. It | | | | increases every time | | | | a new lease is | | | | allocated (as a | | | | result of receiving a | | | | REQUEST message) and | | | | is never decreased. | | | | The *id* is the | | | | subnet-id of a given | | | | subnet. This | | | | statistic is exposed | | | | for each subnet | | | | separately, and is | | | | reset during a | | | | reconfiguration | | | | event. | +-----------------------------------------+-----------------------+------------------------+ | subnet[id].assigned-nas | integer | Number of NA | | | | addresses in a given | | | | subnet that are | | | | assigned. It | | | | increases every time | | | | a new lease is | | | | allocated (as a | | | | result of receiving a | | | | REQUEST message) and | | | | is decreased every | | | | time a lease is | | | | released (a RELEASE | | | | message is received) | | | | or expires. The *id* | | | | is the subnet-id of a | | | | given subnet. This | | | | statistic is exposed | | | | for each subnet | | | | separately, and is | | | | reset during a | | | | reconfiguration | | | | event. | +-----------------------------------------+-----------------------+------------------------+ | subnet[id].total-pds | integer | Total number of PD | | | | prefixes available | | | | for DHCPv6 management | | | | for a given subnet; | | | | in other words, this | | | | is the sum of all | | | | prefixes in all | | | | configured pools. | | | | This statistic | | | | changes only during | | | | configuration | | | | changes. Note it does | | | | not take into account | | | | any prefixes that may | | | | be reserved due to | | | | host reservation. The | | | | *id* is the subnet-id | | | | of a given subnet. | | | | This statistic is | | | | exposed for each | | | | subnet separately, | | | | and is reset during a | | | | reconfiguration | | | | event. | +-----------------------------------------+-----------------------+------------------------+ | cumulative-assigned-pds | integer | Cumulative number of | | | | PD prefixes that | | | | have been assigned | | | | since server startup. | | | | It is incremented | | | | each time a PD prefix | | | | is assigned and is not | | | | reset when the server | | | | is reconfigured. | +-----------------------------------------+-----------------------+------------------------+ | subnet[id].cumulative-assigned-pds | integer | Cumulative number of | | | | PD prefixes in a | | | | given subnet that | | | | were assigned. It | | | | increases every time | | | | a new lease is | | | | allocated (as a | | | | result of receiving a | | | | REQUEST message) and | | | | is never decreased. | | | | The *id* is the | | | | subnet-id of a given | | | | subnet. This | | | | statistic is exposed | | | | for each subnet | | | | separately, and is | | | | reset during a | | | | reconfiguration | | | | event. | +-----------------------------------------+-----------------------+------------------------+ | subnet[id].assigned-pds | integer | Number of PD prefixes | | | | in a given subnet | | | | that are assigned. It | | | | increases every time | | | | a new lease is | | | | allocated (as a | | | | result of receiving a | | | | REQUEST message) and | | | | is decreased every | | | | time a lease is | | | | released (a RELEASE | | | | message is received) | | | | or expires. The *id* | | | | is the subnet-id of a | | | | given subnet. This | | | | statistic is exposed | | | | for each subnet | | | | separately, and is | | | | reset during a | | | | reconfiguration | | | | event. | +-----------------------------------------+-----------------------+------------------------+ | reclaimed-leases | integer | Number of expired | | | | leases that have been | | | | reclaimed since | | | | server startup. It is | | | | incremented each time | | | | an expired lease is | | | | reclaimed (counting | | | | both NA and PD | | | | reclamations). | | | | This statistic never | | | | decreases. It can be | | | | used as a long-term | | | | indicator of how many | | | | actual leases have been| | | | reclaimed. | | | | This is a global | | | | statistic that covers | | | | all subnets. | +-----------------------------------------+-----------------------+------------------------+ | subnet[id].reclaimed-leases | integer | Number of expired | | | | leases associated | | | | with a given subnet | | | | (*"id"* is the | | | | subnet-id) that have | | | | been reclaimed since | | | | server startup. It is | | | | incremented each time | | | | an expired lease is | | | | reclaimed (counting | | | | both NA and PD | | | | reclamations). | | | | The *id* is the | | | | subnet-id of a | | | | given subnet. This | | | | statistic is exposed | | | | for each subnet | | | | separately. | +-----------------------------------------+-----------------------+------------------------+ | declined-addresses | integer | Number of IPv6 | | | | addresses that are | | | | currently declined; a | | | | count of the number | | | | of leases currently | | | | unavailable. Once a | | | | lease is recovered, | | | | this statistic will | | | | be decreased; | | | | ideally, this | | | | statistic should be | | | | zero. If this | | | | statistic is non-zero | | | | or increasing, a | | | | network administrator | | | | should investigate | | | | whether there is a | | | | misbehaving device in | | | | the network. This is | | | | a global statistic | | | | that covers all | | | | subnets. | +-----------------------------------------+-----------------------+------------------------+ | subnet[id].declined-addresses | integer | Number of IPv6 | | | | addresses that are | | | | currently declined in | | | | a given subnet; a | | | | count of the number | | | | of leases currently | | | | unavailable. Once a | | | | lease is recovered, | | | | this statistic will | | | | be decreased; | | | | ideally, this | | | | statistic should be | | | | zero. If this | | | | statistic is non-zero | | | | or increasing, a | | | | network administrator | | | | should investigate | | | | whether there is a | | | | misbehaving device in | | | | the network. The *id* | | | | is the subnet-id of a | | | | given subnet. This | | | | statistic is exposed | | | | for each subnet | | | | separately. | +-----------------------------------------+-----------------------+------------------------+ | reclaimed-declined-addresses | integer | Number of IPv6 | | | | addresses that were | | | | declined, but have | | | | now been recovered. | | | | Unlike | | | | declined-addresses, | | | | this statistic never | | | | decreases. It can be | | | | used as a long-term | | | | indicator of how many | | | | actual valid Declines | | | | were processed and | | | | recovered from. This | | | | is a global statistic | | | | that covers all | | | | subnets. | +-----------------------------------------+-----------------------+------------------------+ | subnet[id].reclaimed-declined-addresses | integer | Number of IPv6 | | | | addresses that were | | | | declined, but have | | | | now been recovered. | | | | Unlike | | | | declined-addresses, | | | | this statistic never | | | | decreases. It can be | | | | used as a long-term | | | | indicator of how many | | | | actual valid Declines | | | | were processed and | | | | recovered from. The | | | | *id* is the subnet-id | | | | of a given subnet. | | | | This statistic is | | | | exposed for each | | | | subnet separately. | +-----------------------------------------+-----------------------+------------------------+ .. note:: This section describes DHCPv6-specific statistics. For a general overview and usage of statistics, see :ref:`stats`. Beginning with Kea 1.7.7, the DHCPv6 server provides two global parameters to control statistics default sample limits: - ``statistic-default-sample-count`` - determines the default maximum number of samples which are kept. The special value of zero indicates that a default maximum age should be used. - ``statistic-default-sample-age`` - determines the default maximum age in seconds of samples which are kept. For instance, to reduce the statistic-keeping overhead, set the default maximum sample count to 1 so that only one sample is kept: :: "Dhcp6": { "statistic-default-sample-count": 1, "subnet6": [ ... ], ... } Statistics can be retrieved periodically to gain more insight into Kea operations. One tool that leverages that capability is ISC Stork. See :ref:`stork` for details. .. _dhcp6-ctrl-channel: Management API for the DHCPv6 Server ==================================== The management API allows the issuing of specific management commands, such as statistics retrieval, reconfiguration, or shutdown. For more details, see :ref:`ctrl-channel`. Currently, the only supported communication channel type is UNIX stream socket. By default there are no sockets open; to instruct Kea to open a socket, the following entry in the configuration file can be used: :: "Dhcp6": { "control-socket": { "socket-type": "unix", "socket-name": "/path/to/the/unix/socket" }, "subnet6": [ ... ], ... } The length of the path specified by the ``socket-name`` parameter is restricted by the maximum length for the UNIX socket name on the administrator's operating system, i.e. the size of the ``sun_path`` field in the ``sockaddr_un`` structure, decreased by 1. This value varies on different operating systems between 91 and 107 characters. Typical values are 107 on Linux and 103 on FreeBSD. Communication over the control channel is conducted using JSON structures. See the `Control Channel section in the Kea Developer's Guide `__ for more details. The DHCPv6 server supports the following operational commands: - build-report - config-get - config-reload - config-set - config-test - config-write - dhcp-disable - dhcp-enable - leases-reclaim - list-commands - shutdown - status-get - version-get as described in :ref:`commands-common`. In addition, it supports the following statistics-related commands: - statistic-get - statistic-reset - statistic-remove - statistic-get-all - statistic-reset-all - statistic-remove-all - statistic-sample-age-set - statistic-sample-age-set-all - statistic-sample-count-set - statistic-sample-count-set-all as described in :ref:`command-stats`. .. _dhcp6-user-contexts: User Contexts in IPv6 ===================== Kea allows loading hook libraries that can sometimes benefit from additional parameters. If such a parameter is specific to the whole library, it is typically defined as a parameter for the hook library. However, sometimes there is a need to specify parameters that are different for each pool. See :ref:`user-context` for additional background regarding the user context idea. See :ref:`user-context-hooks` for a discussion from the hooks perspective. User contexts can be specified at global scope, shared network, subnet, pool, client class, option data, or definition level, and via host reservation. One other useful feature is the ability to store comments or descriptions. Let's consider a lightweight 4over6 deployment as an example. It is an IPv6 transition technology that allows mapping IPv6 prefixes into full or partial IPv4 addresses. In the DHCP context, these are specific parameters that are supposed to be delivered to clients in the form of additional options. Values of these options are correlated to delegated prefixes, so it is reasonable to keep these parameters together with the PD pool. On the other hand, lightweight 4over6 is not a commonly used feature, so it is not a part of the base Kea code. The solution to this problem is to specify a user context. For each PD pool that is expected to be used for lightweight 4over6, a user context with extra parameters is defined. Those extra parameters will be used by a hook library and loaded only when dynamic calculation of the lightweight 4over6 option is actually needed. An example configuration looks as follows: :: "Dhcp6": { "subnet6": [ { "pd-pools": [ { "prefix": "2001:db8::", "prefix-len": 56, "delegated-len": 64, # This is a pool specific context. "user-context": { "threshold-percent": 85, "v4-network": "192.168.0.0/16", "v4-overflow": "10.0.0.0/16", "lw4over6-sharing-ratio": 64, "lw4over6-v4-pool": "192.0.2.0/24", "lw4over6-sysports-exclude": true, "lw4over6-bind-prefix-len": 56 } } ], "subnet": "2001:db8::/32", # This is a subnet-specific context. Any type of # information can be entered here as long as it is valid JSON. "user-context": { "comment": "Those v4-v6 migration technologies are tricky.", "experimental": true, "billing-department": 42, "contact-points": [ "Alice", "Bob" ] } } ] } Kea does not interpret or use the user context information; it simply stores it and makes it available to the hook libraries. It is up to each hook library to extract that information and use it. The parser translates a "comment" entry into a user context with the entry, which allows a comment to be attached inside the configuration itself. .. _dhcp6-std: Supported DHCPv6 Standards ========================== The following standards are currently supported: - *Dynamic Host Configuration Protocol for IPv6*, `RFC 3315 `__: Supported messages are SOLICIT, ADVERTISE, REQUEST, RELEASE, RENEW, REBIND, INFORMATION-REQUEST, CONFIRM, DECLINE and REPLY. The only not supported message is RECONFIGURE. - *Dynamic Host Configuration Protocol (DHCPv6) Options for Session Initiation Protocol (SIP) Servers*, `RFC 3319 `__: All defined options are supported. - *IPv6 Prefix Options for Dynamic Host Configuration Protocol (DHCP) version 6*, `RFC 3633 `__: Supported options are IA_PD and IA_PREFIX. Also supported is the status code NoPrefixAvail. - *DNS Configuration options for Dynamic Host Configuration Protocol for IPv6 (DHCPv6)*, `RFC 3646 `__: All defined options are supported. - *Stateless Dynamic Host Configuration Protocol (DHCP) Service for IPv6*, `RFC 3736 `__: The server operation in stateless mode is supported. Kea is currently server only, so the client side is not implemented. - *Information Refresh Time Option for Dynamic Host Configuration Protocol for IPv6 (DHCPv6)*, `RFC 4242 `__: The sole defined option (information-refresh-time) is supported. - *The Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Relay Agent Remote-ID Option*, `RFC 4649 `__: REMOTE-ID option is supported. - *Resolution of Fully Qualified Domain Name (FQDN) Conflicts among Dynamic Host Configuration Protocol (DHCP) Clients*, `RFC 4703 `__: The DHCPv6 server uses DHCP-DDNS server to resolve conflicts. - *The Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Client Fully Qualified Domain Name (FQDN) Option*, `RFC 4704 `__: Supported option is CLIENT_FQDN. - *Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Option for Dual-Stack Lite*, `RFC 6334 `__: the AFTR-Name DHCPv6 Option is supported. - *Relay-Supplied DHCP Options*, `RFC 6422 `__: Full functionality is supported: OPTION_RSOO, ability of the server to echo back the options, checks whether an option is RSOO-enabled, ability to mark additional options as RSOO-enabled. - *Prefix Exclude Option for DHCPv6-based Prefix Delegation*, `RFC 6603 `__: Prefix Exclude option is supported. - *Client Link-Layer Address Option in DHCPv6*, `RFC 6939 `__: Supported option is client link-layer address option. - *Issues and Recommendations with Multiple Stateful DHCPv6 Options*, `RFC 7550 `__: All recommendations related to the DHCPv6 server operation are supported. - *DHCPv6 Options for Configuration of Softwire Address and Port-Mapped Clients*, `RFC 7598 `__: All options indicated in this specification are supported by the DHCPv6 server. - *Generalized UDP Source Port for DHCP Relay*, `RFC 8357 `__: The Kea server is able to handle Relay Source Port option in a received Relay-Forward message, remembers the UDP port and sends back Relay-Reply with a copy of the option to the relay agent using this UDP port. - *Dynamic Host Configuration Protocol for IPv6 (DHCPv6)*, `RFC 8415 `__: New DHCPv6 protocol specification which obsoletes RFC 3315, RFC 3633, RFC 3736, RFC 4242, RFC 7083, RFC 7283, and RFC 7550. All features, with the exception of Reconfigure mechanism and the now deprecated temporary addresses (IA_TA) mechanism, are supported. .. _dhcp6-limit: DHCPv6 Server Limitations ========================= These are the current limitations of the DHCPv6 server software. Most of them are reflections of the current stage of development and should be treated as “not implemented yet”, rather than actual limitations. - The server will allocate, renew, or rebind a maximum of one lease for a particular IA option (IA_NA or IA_PD) sent by a client. `RFC 8415 `__ allows for multiple addresses or prefixes to be allocated for a single IA. - Temporary addresses are not supported. There is no intention to ever implement this feature, as it is deprecated in RFC8415. - Client reconfiguration (RECONFIGURE) is not yet supported. .. _dhcp6-srv-examples: Kea DHCPv6 Server Examples ========================== A collection of simple-to-use examples for the DHCPv6 component of Kea is available with the source files, located in the doc/examples/kea6 directory. .. _dhcp6-cb: Configuration Backend in DHCPv6 =============================== In the :ref:`config-backend` section we have described the Configuration Backend feature, its applicability, and its limitations. This section focuses on the usage of the CB with the DHCPv6 server. It lists the supported parameters, describes limitations, and gives examples of the DHCPv6 server configuration to take advantage of the CB. Please also refer to the sibling section :ref:`dhcp4-cb` for the DHCPv4-specific usage of the CB. .. _dhcp6-cb-parameters: Supported Parameters -------------------- The ultimate goal for the CB is to serve as a central configuration repository for one or multiple Kea servers connected to the database. In the future, it will be possible to store most of the server's configuration in the database and reduce the configuration file to a bare minimum; the only mandatory parameter will be the ``config-control``, which includes the necessary information to connect to the database. In the present release, however, only a subset of the DHCPv4 server parameters can be stored in the database. All other parameters must be specified in the JSON configuration file, if required. The following table lists DHCPv6 specific parameters supported by the Configuration Backend, with an indication on which level of the hierarchy it is currently supported. The "n/a" marks cases when a given parameter is not applicable at the particular level of the hierarchy or in cases when the server does not support the parameter at this level of the hierarchy. "no" is used when a parameter is supported at the given level of the hierarchy but is not configurable via the Configuration Backend. All supported parameters can be configured via ``cb_cmds`` hooks library described in the :ref:`cb-cmds-library` section. The general rule is that the scalar global parameters are set using the ``remote-global-parameter6-set``; the shared network-specific parameters are set using ``remote-network6-set``; and the subnet- and pool-level parameters are set using ``remote-subnet6-set``. Whenever there is an exception to this general rule, it is highlighted in the table. The non-scalar global parameters have dedicated commands; for example, the global DHCPv6 options (``option-data``) are modified using ``remote-option6-global-set``. Client classes together with class specific option definitions and DHCPv6 options are configured using the ``remote-class6-set`` command. .. table:: List of DHCPv6 Parameters Supported by the Configuration Backend +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | Parameter | Global | Client | Shared | Subnet | Pool | Prefix | | | | Class | Network | | | Delegation | | | | | | | | Pool | +=============================+============================+===========+===========+===========+===========+============+ | cache-max-age | yes | n/a | todo | todo | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | cache-threshold | yes | n/a | todo | todo | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | calculate-tee-times | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | client-class | n/a | n/a | yes | yes | yes | yes | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | ddns-send-update | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | ddns-override-no-update | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | ddns-override-client-update | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | ddns-replace-client-name | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | ddns-generated-prefix | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | ddns-qualifying-suffix | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | decline-probation-period | yes | n/a | n/a | n/a | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | delegated-len | n/a | n/a | n/a | n/a | n/a | yes | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | dhcp4o6-port | yes | n/a | n/a | n/a | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | excluded-prefix | n/a | n/a | n/a | n/a | n/a | yes | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | excluded-prefix-len | n/a | n/a | n/a | n/a | n/a | yes | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | hostname-char-set | no | n/a | no | no | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | hostname-char-replacement | no | n/a | no | no | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | interface | n/a | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | interface-id | n/a | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | max-preferred-lifetime | yes | yes | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | max-valid-lifetime | yes | yes | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | min-preferred-lifetime | yes | yes | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | min-valid-lifetime | yes | yes | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | option-data | yes (via | yes | yes | yes | yes | yes | | | remote-option6-global-set) | | | | | | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | option-def | yes (via | yes | n/a | n/a | n/a | n/a | | | remote-option-def6-set) | | | | | | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | preferred-lifetime | yes | yes | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | prefix | n/a | n/a | n/a | n/a | n/a | yes | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | prefix-len | n/a | n/a | n/a | n/a | n/a | yes | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | rapid-commit | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | rebind-timer | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | relay | n/a | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | renew-timer | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | require-client-classes | n/a | n/a | yes | yes | yes | yes | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | reservation-mode | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | reservations-global | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | reservations-in-subnet | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | reservations-out-of-pool | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | t1-percent | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | t2-percent | yes | n/a | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ | valid-lifetime | yes | yes | yes | yes | n/a | n/a | +-----------------------------+----------------------------+-----------+-----------+-----------+-----------+------------+ .. _dhcp6-cb-json: Enabling Configuration Backend ------------------------------ The following configuration snippet demonstrates how to enable the MySQL Configuration Backend for the DHCPv6 server: :: { "Dhcp6": { "server-tag": "my DHCPv6 server", "config-control": { "config-databases": [ { "type": "mysql", "name": "kea", "user": "kea", "password": "kea", "host": "2001:db8:1::1", "port": 3302 } ], "config-fetch-wait-time": 20 }, "hooks-libraries": [ { "library": "/usr/local/lib/kea/hooks/libdhcp_mysql_cb.so" }, { "library": "/usr/local/lib/kea/hooks/libdhcp_cb_cmds.so" } ], ... } } The configuration structure is almost identical to that of the DHCPv4 server (see :ref:`dhcp4-cb-json` for the detailed description). .. _dhcp6-compatibility: Kea DHCPv6 Compatibility Configuration Parameters ================================================= By default, Kea aims to follow the RFC documents to promote better standards compliance. However, there are buggy implementations out there that cannot be easily fixed or upgraded. Therefore Kea provides an easy to use compatibility mode for broken or non-compliant clients. In that purpose, flags have to be enabled in order to enable uncommon practices: .. code-block:: json { "Dhcp6": { "compatibility": { } } } Lenient Option Parsing ---------------------- By default, DHCPv6 option 16's vendor-class-data field is parsed as a set of length-value pairs. Same for tuple fields defined in custom options. With ``lenient-option-parsing: "true"``, if a length ever exceeds the rest of the option's buffer, Kea no longer complains with the log message ``unable to parse the opaque data tuple, the buffer length is x, but the tuple length is y`` with ``x < y``. Instead, the value is considered to be the rest of the buffer, or in terms of the log message above, the tuple length ``y`` becomes ``x``. Enabling this flag is expected to improve compatibility with devices such as RAD MiNID. .. code-block:: json { "Dhcp6": { "compatibility": { "lenient-option-parsing": true } } } kea-2.0.2/doc/sphinx/arm/logging.rst0000644000175000017500000015577614206773363014251 00000000000000.. _logging: ******* Logging ******* Logging Configuration ===================== During its operation Kea may produce many messages. They differ in severity (some are more important than others) and source (different components, like hooks, produce different messages). It is useful to understand which log messages are critical and which are not, and to configure logging appropriately. For example, debug-level messages can be safely ignored in a typical deployment. They are, however, very useful when debugging a problem. The logging system in Kea is configured through the loggers entry in the server section of your configuration file. In previous Kea releases this entry was in an independent Logging section; this was still supported for backward compatibility until Kea 1.7.9 included. Loggers ------- Within Kea, a message is logged through an entity called a "logger." Different components log messages through different loggers, and each logger can be configured independently of the others. Some components, in particular the DHCP server processes, may use multiple loggers to log messages pertaining to different logical functions of the component. For example, the DHCPv4 server uses one logger for messages about packet reception and transmission, another logger for messages related to lease allocation, and so on. Some of the libraries used by the Kea server, such as libdhcpsrv, use their own loggers. Users implementing hooks libraries (code attached to the server at runtime) are responsible for creating the loggers used by those libraries. Such loggers should have unique names, different from the logger names used by Kea. In this way the messages produced by the hooks library can be distinguished from messages issued by the core Kea code. Unique names also allow the loggers to be configured independently of loggers used by Kea. Whenever it makes sense, a hooks library can use multiple loggers to log messages pertaining to different logical parts of the library. In the server section of a configuration file the configuration for zero or more loggers (including loggers used by the proprietary hooks libraries) can be specified. If there are no loggers specified, the code will use default values; these cause Kea to log messages of INFO severity or greater to standard output. There is a small time window after Kea has been started but before it has read its configuration; logging in this short period can be controlled using environment variables. For details, see :ref:`logging-during-startup`. The three main elements of a logger configuration are: ``name`` (the component that is generating the messages), ``severity`` (what to log), and ``output_commands`` (where to log). There is also a ``debuglevel`` element, which is only relevant if debug-level logging has been selected. The name (string) Logger ~~~~~~~~~~~~~~~~~~~~~~~~ Each logger in the system has a name: that of the component binary file using it to log messages. For instance, to configure logging for the DHCPv4 server, add an entry for a logger named “kea-dhcp4”. This configuration will then be used by the loggers in the DHCPv4 server and all the libraries used by it, unless a library defines its own logger and there is a specific logger configuration that applies to that logger. When tracking down an issue with the server's operation, use of DEBUG logging is required to obtain the verbose output needed for problem diagnosis. However, the high verbosity is likely to overwhelm the logging system in cases where the server is processing high-volume traffic. To mitigate this problem, Kea can use multiple loggers, for different functional parts of the server, that can each be configured independently. If the user is reasonably confident that a problem originates in a specific function of the server, or that the problem is related to a specific type of operation, they may enable high verbosity only for the relevant logger, thereby limiting the debug messages to the required minimum. The loggers are associated with a particular library or binary of Kea. However, each library or binary may (and usually does) include multiple loggers. For example, the DHCPv4 server binary contains separate loggers for packet parsing, dropped packets, callouts, etc. The loggers form a hierarchy. For each program in Kea, there is a "root" logger, named after the program (e.g. the root logger for kea-dhcp, the DHCPv4 server) is named kea-dhcp4. All other loggers are children of this logger and are named accordingly, e.g. the allocation engine in the DHCPv4 server logs messages using a logger called kea-dhcp4.alloc-engine. This relationship is important, as each child logger derives its default configuration from its parent root logger. In the typical case, the root logger configuration is the only logging configuration specified in the configuration file and so applies to all loggers. If an entry is made for a given logger, any attributes specified override those of the root logger, whereas any not specified are inherited from it. To illustrate this, suppose we are using the DHCPv4 server with the root logger “kea-dhcp4” logging at the INFO level. In order to enable DEBUG verbosity for DHCPv4 packet drops, we must create a configuration entry for the logger called “kea-dhcp4.bad-packets” and specify severity DEBUG for this logger. All other configuration parameters may be omitted for this logger if the logger should use the default values specified in the root logger's configuration. If there are multiple logger specifications in the configuration that might match a particular logger, the specification with the more specific logger name takes precedence. For example, if there are entries for both “kea-dhcp4” and “kea-dhcp4.dhcpsrv”, the main DHCPv4 server program — and all libraries it uses other than the dhcpsrv library (libdhcpsrv) — will log messages according to the configuration in the first entry (“kea-dhcp4”). Messages generated by the dhcpsrv library will be logged according to the configuration set by the second entry. Currently defined loggers are defined in the following table. The "Software Package" column of this table specifies whether the particular loggers belong to the core Kea code (open source Kea binaries and libraries), or hooks libraries (open source or premium). .. tabularcolumns:: |p{0.2\linewidth}|p{0.2\linewidth}|p{0.6\linewidth}| .. table:: List of Loggers Supported by Kea Servers and Hooks Libraries Shipped With Kea and Premium Packages :class: longtable :widths: 20 20 60 +----------------------------------+------------------------+--------------------------------+ | Logger Name | Software Package | Description | +==================================+========================+================================+ | ``kea-ctrl-agent`` | core | The root logger for | | | | the Control Agent | | | | exposing the RESTful | | | | control API. All | | | | components used by | | | | the Control Agent | | | | inherit the settings | | | | from this logger. | +----------------------------------+------------------------+--------------------------------+ | ``kea-ctrl-agent.auth`` | core | A logger which covers | | | | access control details, such as| | | | a result of the basic HTTP | | | | authentication. | +----------------------------------+------------------------+--------------------------------+ | ``kea-ctrl-agent.http`` | core | A logger which | | | | outputs log messages | | | | related to receiving, | | | | parsing, and sending | | | | HTTP messages. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4`` | core | The root logger for | | | | the DHCPv4 server. | | | | All components used | | | | by the DHCPv4 server | | | | inherit the settings | | | | from this logger. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp6`` | core | The root logger for | | | | the DHCPv6 server. | | | | All components used | | | | by the DHCPv6 server | | | | inherit the settings | | | | from this logger. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.alloc-engine``, | core | Used by the lease | | ``kea-dhcp6.alloc-engine`` | | allocation engine, | | | | which is responsible | | | | for managing leases | | | | in the lease | | | | database, i.e. | | | | creating, modifying, | | | | and removing DHCP | | | | leases as a result of | | | | processing messages | | | | from clients. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.bad-packets``, | core | Used by the DHCP | | ``kea-dhcp6.bad-packets`` | | servers for logging | | | | inbound client | | | | packets that were | | | | dropped or to which | | | | the server responded | | | | with a DHCPNAK. It | | | | allows administrators | | | | to configure a | | | | separate log output | | | | that contains only | | | | packet drop and | | | | reject entries. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.bootp-hooks`` | libdhcp_bootp | This logger is used to log | | | hook library | messages related to the | | | | operation of the BOOTP hook | | | | library. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.callouts``, | core | Used to log messages | | ``kea-dhcp6.callouts`` | | pertaining to the | | | | callouts registration | | | | and execution for the | | | | particular hook | | | | point. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.commands``, | core | Used to log messages | | ``kea-dhcp6.commands`` | | relating to the | | | | handling of commands | | | | received by the DHCP | | | | server over the | | | | command channel. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.database``, | core | Used to log messages | | ``kea-dhcp6.database`` | | relating to general | | | | operations on the | | | | relational databases | | | | and Cassandra. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.ddns``, | core | Used by the DHCP | | ``kea-dhcp6.ddns`` | | server to log | | | | messages related to | | | | Client FQDN and | | | | Hostname option | | | | processing. It also | | | | includes log messages | | | | related to the | | | | relevant DNS updates. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.dhcp4`` | core | Used by the DHCPv4 | | | | server daemon to log | | | | basic operations. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.dhcpsrv``, | core | The base loggers for | | ``kea-dhcp6.dhcpsrv`` | | the libkea-dhcpsrv | | | | library. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.eval``, | core | Used to log messages | | ``kea-dhcp6.eval`` | | relating to the | | | | client classification | | | | expression evaluation | | | | code. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.host-cache-hooks``, | libdhcp_host_cache | This logger is used | | ``kea-dhcp6.host-cache-hooks`` | premium hook library | to log messages | | | | related to the | | | | operation of the Host | | | | Cache hooks library. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.flex-id-hooks``, | libdhcp_flex_id | This logger is used | | ``kea-dhcp6.flex-id-hooks`` | premium hook library | to log messages | | | | related to the | | | | operation of the | | | | Flexible Identifiers | | | | hooks library. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.ha-hooks``, | libdhcp_ha hook | This logger is used | | ``kea-dhcp6.ha-hooks`` | library | to log messages | | | | related to the | | | | operation of the High | | | | Availability hooks | | | | library. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.hooks``, | core | Used to log messages | | ``kea-dhcp6.hooks`` | | related to the | | | | management of hooks | | | | libraries, e.g. | | | | registration and | | | | deregistration of the | | | | libraries, and to the | | | | initialization of the | | | | callouts execution | | | | for various hook | | | | points within the | | | | DHCP server. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.host-cmds-hooks``, | libdhcp_host_cmds | This logger is used | | ``kea-dhcp6.host-cmds-hooks`` | premium hook library | to log messages | | | | related to the | | | | operation of the Host | | | | Commands hooks | | | | library. In general, | | | | these will pertain to | | | | the loading and | | | | unloading of the | | | | library and the | | | | execution of commands | | | | by the library. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.hosts``, | core | Used within the | | ``kea-dhcp6.hosts`` | | libdhcpsrv, it logs | | | | messages related to | | | | the management of | | | | DHCP host | | | | reservations, i.e. | | | | retrieving | | | | reservations and | | | | adding new | | | | reservations. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.lease-cmds-hooks``, | libdhcp_lease_cmds | This logger is used | | ``kea-dhcp6.lease-cmds-hooks`` | hook library | to log messages | | | | related to the | | | | operation of the | | | | Lease Commands hooks | | | | library. In general, | | | | these will pertain to | | | | the loading and | | | | unloading of the | | | | library and the | | | | execution of commands | | | | by the library. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.leases``, | core | Used by the DHCP | | ``kea-dhcp6.leases`` | | server to log | | | | messages related to | | | | lease allocation. The | | | | messages include | | | | detailed information | | | | about the allocated | | | | or offered leases, | | | | errors during the | | | | lease allocation, | | | | etc. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.legal-log-hooks``, | libdhcp_legal_log | This logger is used | | ``kea-dhcp6.legal-log-hooks`` | premium hook library | to log messages | | | | related to the | | | | operation of the | | | | Forensic Logging | | | | hooks library. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.options``, | core | Used by the DHCP | | ``kea-dhcp6.options`` | | server to log | | | | messages related to | | | | the processing of | | | | options in the DHCP | | | | messages, i.e. | | | | parsing options, | | | | encoding options into | | | | on-wire format, and | | | | packet classification | | | | using options | | | | contained in the | | | | received packets. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.packets``, | core | This logger is mostly | | ``kea-dhcp6.packets`` | | used to log messages | | | | related to | | | | transmission of the | | | | DHCP packets, i.e. | | | | packet reception and | | | | the sending of a | | | | response. Such | | | | messages include | | | | information about the | | | | source and | | | | destination IP | | | | addresses and | | | | interfaces used to | | | | transmit packets. The | | | | logger is also used | | | | to log messages | | | | related to subnet | | | | selection, as this | | | | selection is usually | | | | based on the IP | | | | addresses, relay | | | | addresses, and/or | | | | interface names, | | | | which can be | | | | retrieved from the | | | | received packet even | | | | before the DHCP | | | | message carried in | | | | the packet is parsed. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.radius-hooks``, | libdhcp_radius | This logger is used | | ``kea-dhcp6.radius-hooks`` | premium hook library | to log messages | | | | related to the | | | | operation of the | | | | RADIUS hooks library. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.stat-cmds-hooks``, | libdhcp_stat_cmds | This logger is used | | ``kea-dhcp6.stat-cmds-hooks`` | hook library | to log messages | | | | related to the | | | | operation of the | | | | Statistics Commands | | | | hooks library. In | | | | general, these will | | | | pertain to loading | | | | and unloading the | | | | library and the | | | | execution of commands | | | | by the library. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.subnet-cmds-hooks``, | libdhcp_subnet_cmds | This logger is used | | ``kea-dhcp6.subnet-cmds-hooks`` | hook library | to log messages | | | | related to the | | | | operation of the | | | | Subnet Commands hooks | | | | library. In general, | | | | these will pertain to | | | | loading and unloading | | | | the library and the | | | | execution of commands | | | | by the library. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.mysql-cb-hooks``, | libdhcp_mysql_cb_hooks | This logger is used | | ``kea-dhcp6.mysql-cb-hooks`` | hook library | to log messages | | | | related to the | | | | operation of the | | | | MySQL Configuration | | | | Backend hooks | | | | library. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp-ddns`` | core | The root logger for | | | | the kea-dhcp-ddns | | | | daemon. All | | | | components used by | | | | this daemon inherit | | | | the settings from | | | | this logger unless | | | | there are | | | | configurations for | | | | more specialized | | | | loggers. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp-ddns.dctl`` | core | The logger used by | | | | the kea-dhcp-ddns | | | | daemon for logging | | | | basic information | | | | about the process, | | | | received signals, and | | | | triggered | | | | reconfigurations. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp-ddns.dhcpddns`` | core | The logger used by | | | | the kea-dhcp-ddns | | | | daemon for logging | | | | events related to | | | | DDNS operations. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp-ddns.dhcp-to-d2`` | core | Used by the | | | | kea-dhcp-ddns daemon | | | | for logging | | | | information about | | | | events dealing with | | | | receiving messages | | | | from the DHCP servers | | | | and adding them to | | | | the queue for | | | | processing. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp-ddns.d2-to-dns`` | core | Used by the | | | | kea-dhcp-ddns daemon | | | | for logging | | | | information about | | | | events dealing with | | | | sending and receiving | | | | messages to and from | | | | the DNS servers. | +----------------------------------+------------------------+--------------------------------+ | ``kea-netconf`` | core | The root logger for | | | | the NETCONF agent. | | | | All components used | | | | by NETCONF inherit | | | | the settings from | | | | this logger if there | | | | is no specialized | | | | logger provided. | +----------------------------------+------------------------+--------------------------------+ | ``kea-dhcp4.lease-query-hooks``, | libdhcp_lease_query | This logger is used | | ``kea-dhcp6.lease-query-hooks`` | hook library | to log messages | | | | related to the | | | | operation of the | | | | Leasequery hooks library | +----------------------------------+------------------------+--------------------------------+ Note that user-defined hook libraries should not use any of the loggers mentioned above, but should instead define new loggers with names that correspond to the libraries using them. Suppose that a user created a library called “libdhcp-packet-capture” to dump packets received and transmitted by the server to a file. An appropriate name for the logger could be ``kea-dhcp4.packet-capture-hooks``. (Note that the hook library implementer only specifies the second part of this name, i.e. “packet-capture”. The first part is a root-logger name and is prepended by the Kea logging system.) It is also important to note that since this new logger is a child of a root logger, it inherits the configuration from the root logger, something that can be overridden by an entry in the configuration file. The easiest way to find a logger name is to configure all logging to go to a single destination and look there for specific logger names. See :ref:`logging-message-format` for details. The severity (string) Logger ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This specifies the category of messages logged. Each message is logged with an associated severity, which may be one of the following (in descending order of severity): - FATAL - associated with messages generated by a condition that is so serious that the server cannot continue executing. - ERROR - associated with messages generated by an error condition. The server will continue executing, but the results may not be as expected. - WARN - indicates an out-of-the-ordinary condition. However, the server will continue executing normally. - INFO - an informational message marking some event. - DEBUG - messages produced for debugging purposes. When the severity of a logger is set to one of these values, it will only log messages of that severity and above (e.g. setting the logging severity to INFO will log INFO, WARN, ERROR, and FATAL messages). The severity may also be set to NONE, in which case all messages from that logger are inhibited. .. note:: The ``keactrl`` tool, described in :ref:`keactrl`, can be configured to start the servers in verbose mode. If this is the case, the settings of the logging severity in the configuration file will have no effect; the servers will use a logging severity of DEBUG regardless of the logging settings specified in the configuration file. To control severity via the configuration file, please make sure that the ``kea_verbose`` value is set to "no" within the ``keactrl`` configuration. .. _debuglevel: The debuglevel (integer) Logger ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When a logger's severity is set to DEBUG, this value specifies what level of debug messages should be printed. It ranges from 0 (least verbose) to 99 (most verbose). If severity for the logger is not DEBUG, this value is ignored. The output_options (list) Logger ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Each logger can have zero or more ``output_options``. These specify where log messages are sent and are explained in detail below. The output (string) Option ^^^^^^^^^^^^^^^^^^^^^^^^^^ This value determines the type of output. There are several special values allowed here: ``stdout`` (messages are printed on standard output), ``stderr`` (messages are printed on stderr), ``syslog`` (messages are logged to syslog using the default name), ``syslog:name`` (messages are logged to syslog using a specified name). Any other value is interpreted as a filename to which messages should be written. The flush (true or false) Option ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Flush buffers after each log message. Doing this will reduce performance but will ensure that if the program terminates abnormally, all messages up to the point of termination are output. The default is "true". The maxsize (integer) Option ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This option is only relevant when the destination is a file; this is the maximum size in bytes that a log file may reach. When the maximum size is reached, the file is renamed and a new file opened. For example, a ".1" is appended to the name; if a ".1" file exists, it is renamed ".2", etc. This is referred to as rotation. The default value is 10240000 (10MB). The smallest value that can be specified without disabling rotation is 204800. Any value less than this, including 0, disables rotation. .. note:: Due to a limitation of the underlying logging library (log4cplus), rolling over the log files (from ".1" to ".2", etc) may show odd results; there can be multiple small files at the timing of rollover. This can happen when multiple processes try to roll over the files simultaneously. Version 1.1.0 of log4cplus solved this problem, so if this version or later of log4cplus is used to build Kea, the issue should not occur. Even for older versions, it is normally expected to happen rarely unless the log messages are produced very frequently by multiple different processes. The maxver (integer) Option ^^^^^^^^^^^^^^^^^^^^^^^^^^^ This option is only relevant when the destination is a file and rotation is enabled (i.e. maxsize is large enough). This is the maximum number of rotated versions that will be kept. Once that number of files has been reached, the oldest file, "log-name.maxver", will be discarded each time the log rotates. In other words, at most there will be the active log file plus maxver rotated files. The minimum and default value is 1. The pattern (string) Option ^^^^^^^^^^^^^^^^^^^^^^^^^^^ This option can be used to specify the layout pattern of log messages for a logger. Kea logging is implemented using the Log4Cplus library and whose output formatting is based, conceptually, on the printf formatting from C and is discussed in detail in the next section :ref:`logging-message-format`. Each output type (stdout, file, or syslog) has a default ``pattern`` which describes the content of its log messages. This parameter can be used to specify a desired pattern. The pattern for each logger is governed individually so each configured logger can have its own pattern. Omitting the ``pattern`` parameter or setting it to an empty string, "", causes Kea to use the default pattern for that logger's output type. In addition to the log text itself, the default patterns used for ``stdout`` and files contain information such as date and time, logger level, and process information. The default pattern for ``syslog`` is limited primarily to log level, source, and the log text. This avoids duplicating information which is usually supplied by syslog. .. warning:: You are strongly encouraged to test your pattern(s) on a local, non-production instance of Kea, running in the foreground and logging to ``stdout``. .. _logging-message-format: Logging Message Format ---------------------- As mentioned above, Kea log message content is controlled via a scheme similar to the C language's printf formatting. The "pattern" used for each message is described by a string containing one or more format components as part of a text string. In addition to the components the string may contain any other arbitrary text you find useful. The Log4Cplus documentation provides a concise discussion of the supported components and formatting behavior and can be seen here: https://log4cplus.sourceforge.io/docs/html/classlog4cplus_1_1PatternLayout.html It is probably easiest to understand this by examining the default pattern for stdout and files (currently they are the same). That pattern is shown below: :: "%D{%Y-%m-%d %H:%M:%S.%q} %-5p [%c/%i.%t] %m\n"; and a typical log produced by this pattern looks something like this: :: 2019-08-05 14:27:45.871 DEBUG [kea-dhcp4.dhcpsrv/8475.12345] DHCPSRV_TIMERMGR_START_TIMER starting timer: reclaim-expired-leases That breaks down as like so: - ``%D{%Y-%m-%d %H:%M:%S.%q}`` '%D' is the date and time in local time that the log message is generated, while everything between the curly braces, '{}' are date and time components. From the example log above this produces: ``2019-08-05 14:27:45.871`` - ``%-5p`` The severity of message, output as a minimum of five characters, using right-padding with spaces. In our example log: ``DEBUG`` - ``%c`` The log source. This includes two elements: the Kea process generating the message, in this case, ``kea-dhcp4``; and the component within the program from which the message originated, ``dhcpsrv`` (e.g. the name of the library used by DHCP server implementations). - ``%i`` The process ID. From the example log: ``8475`` - ``%t`` The thread ID. From the example log: ``12345``. Note the format of the thread ID is OS dependent: e.g. on some systems it is an address so it is displayed in hexadecimal. - ``%m`` The log message itself. Kea log messages all begin with a message identifier followed by arbitrary log text. Every message in Kea has a unique identifier, which can be used as an index to the :ref:`kea-messages`, where more information can be obtained. In our example log above, the identifier is ``DHCPSRV_TIMERMGR_START_TIMER``. The log text is typically a brief description detailing the condition that caused the message to be logged. In our example, the information logged, ``starting timer: reclaim-expired-leases``, explains that the timer for the expired lease reclamation cycle has been started. .. Warning:: Omitting ``%m`` will omit the log message text from your output making it rather useless. You should consider ``%m`` mandatory. Finally, note that spacing between components, the square brackets around the log source and PID, and the final carriage return '\n' are all literal text specified as part of the pattern. .. Warning:: In order to ensure each log entry is a separate line, your patterns must end with an ``\n``. There may be use cases where it is not desired so we do not enforce its inclusion. Be aware that if you omit it from your pattern that to common text tools or displays, the log entries will run together in one long, endless "line". The default for pattern for syslog output is as follows: :: "%-5p [%c.%t] %m\n"; You can see that it omits the date and time as well the process ID as this information is typically output by syslog. Note that Kea uses the pattern to construct the text it sends to syslog (or any other destination). It has no influence on the content syslog may add or formatting it may do. Consult your OS documentation for "syslog" behavior as there are multiple implementations. Example Logger Configurations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In this example we want to set the server logging to write to the console using standard output. :: "Server": { "loggers": [ { "name": "kea-dhcp4", "output_options": [ { "output": "stdout" } ], "severity": "WARN" } ] } In this second example, we want to store debug log messages in a file that is at most 2MB and keep up to eight copies of old log files. Once the logfile grows to 2MB, it will be renamed and a new file will be created. :: "Server": { "loggers": [ { "name": "kea-dhcp6", "output_options": [ { "output": "/var/log/kea-debug.log", "maxver": 8, "maxsize": 204800, "flush": true "pattern": "%d{%j %H:%M:%S.%q} %c %m\n" } ], "severity": "DEBUG", "debuglevel": 99 } ] } Notice that the above configuration uses a custom pattern which produces output like this: :: 220 13:50:31.783 kea-dhcp4.dhcp4 DHCP4_STARTED Kea DHCPv4 server version 1.6.0-beta2-git started .. _logging-during-startup: Logging During Kea Startup -------------------------- The logging configuration is specified in the configuration file. However, when Kea starts, the configuration file is not read until partway into the initialization process. Prior to that, the logging settings are set to default values, although it is possible to modify some aspects of the settings by means of environment variables. Note that in the absence of any logging configuration in the configuration file, the settings of the (possibly modified) default configuration will persist while the program is running. The following environment variables can be used to control the behavior of logging during startup: KEA_LOCKFILE_DIR Specifies a directory where the logging system should create its lock file. If not specified, it is prefix/var/run/kea, where "prefix" defaults to /usr/local. This variable must not end with a slash. There is one special value: "none", which instructs Kea not to create a lock file at all. This may cause issues if several processes log to the same file. KEA_LOGGER_DESTINATION Specifies logging output. There are several special values: ``stdout`` Log to standard output. ``stderr`` Log to standard error. ``syslog[:fac]`` Log via syslog. The optional fac (which is separated from the word "syslog" by a colon) specifies the facility to be used for the log messages. Unless specified, messages will be logged using the facility "local0". Any other value is treated as a name of the output file. If not specified otherwise, Kea will log to standard output. Logging levels ============== All Kea servers follow the overall intention to strike a balance between letting the user know what is going on and not overloading the logging system with too much information as that could easily be used as a Denial Of Service attack. A wealth of information is available on debug level. Opposed to ``FATAL``, ``ERROR``, ``WARN`` and ``INFO`` levels, ``DEBUG`` has additional debuglevel parameters. The following table offers a rough idea of what kind of information is logged on which level. Sadly, that information is not very consistent. Future Kea versions may attempt to improve consistency in this regard. Also, keep in mind that sometimes the circumstances determine if an information is logged on higher or lower level. For example, if packet is being dropped due to configured classification, that is an execution of the configured policy and would be logged on debuglevel 15. However, if the packet is dropped due to an exception being thrown, it is much more important, as it may indicate software bug, serious problems with memory, database connectivity and similar. As such it may be logged on much higher levels, such as ``WARN`` or even ``ERROR``. - 0 - singular messages printed during start or shutdown of the server. - 10 - logs information about received API commands. - 15 - information about reasons why a packet was dropped. - 40 - a lot of tracing information, including processing decisions, results of expression evaluations and more. - 45 - similar to level 40, but with more details, e.g. the subnet being selected for incoming packet. - 50 - evaluations of expressions, status received from hook points, lease processing, packet processing details, including unpacking, packing, sending etc. - 55 - includes all details available, including full packet contents with all options printed. The debug levels apply only to messages logged on ``DEBUG``. The debug levels are configured using the ``debuglevel`` option. See Section :ref:`debuglevel` for details.kea-2.0.2/doc/sphinx/arm/ext-netconf.rst0000644000175000017500000011467714206773363015050 00000000000000.. _netconf: YANG/NETCONF ============ .. _netconf-overview: Overview -------- The Network Configuration Protocol, or NETCONF, is a network management protocol defined in `RFC 4741 `__. It uses YANG modeling language, defined in `RFC 6020 `__, to provide a uniform way of handling configuration syntax of varied networking devices. Kea provides optional support for a YANG/NETCONF interface with the ``kea-netconf`` agent. .. _netconf-install: Installing NETCONF ------------------ To get its NETCONF capabilities, Kea uses libyang v1.0.240 and sysrepo v1.4.140. Use packages if they are provided on your system. There is always the alternative method of building from sources which should work on all popular OSs: .. _libyang-install-sources: Installing libyang From Sources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: console $ git clone https://github.com/CESNET/libyang.git $ cd libyang $ git checkout v1.0.240 $ mkdir build $ cd build $ cmake .. -DGEN_CPP_BINDINGS=ON -DGEN_LANGUAGE_BINDINGS=ON -DGEN_PYTHON_BINDINGS=OFF $ make $ make install # without sudo if you're doing development and want to run unit tests .. _sysrepo-install-sources: Installing sysrepo From Sources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: console $ git clone https://github.com/sysrepo/sysrepo.git $ cd sysrepo $ git checkout v1.4.140 $ mkdir build $ cd build $ cmake .. -DGEN_CPP_BINDINGS=ON -DGEN_LANGUAGE_BINDINGS=ON -DGEN_PYTHON_BINDINGS=OFF $ make $ make install # without sudo if you're doing development and want to run unit tests .. _sysrepo-overview: Quick Sysrepo Overview ---------------------- This section offers a rather brief overview of a subset of available functions in Sysrepo. For more complete information, see the `Sysrepo homepage `__. In YANG, configurations and state data are described in the YANG syntax in module files named: ``"module-name"``\ ``[@"revision"]``.yang The revision part is optional and has YYYY-MM-DD format. An alternate XML syntax YIN is defined but less user-friendly. Top-level modules are named in Kea models (a short version of schema models). There are two major modules that Kea is able to support: kea-dhcp4-server and kea-dhcp6-server. While there is an active effort in the DHC working group at IETF to develop a DHCPv6 YANG model, a similar initiative in the past for DHCPv4 failed. Therefore, Kea uses its own dedicated models for DHCPv4 and DHCPv6 but partially supports the IETF model for DHCPv6. All of the models have extra modules as dependencies. The dependency modules are also provided in ``src/share/yang/modules`` in sources and in ``share/kea/yang/modules`` in the installation directory. To install modules from sources, do the following to install all modules: .. code-block:: console $ ./src/share/yang/modules/utils/reinstall.sh OR if you installed sysrepo in a custom path: .. code-block:: console $ ./src/share/yang/modules/utils/reinstall.sh -s /path/to/sysrepo If you want to individually install all modules: .. code-block:: console $ cd ./src/share/yang/modules $ sysrepoctl -i ./ietf-dhcpv6-server*.yang $ sysrepoctl -i ./kea-dhcp4-server*.yang $ sysrepoctl -i ./kea-dhcp6-server*.yang ... The installation should look similar to the following: .. code-block:: console $ ./src/share/yang/modules/utils/reinstall.sh [INF]: Libyang internal module "yang" was installed. [INF]: File "ietf-datastores@2018-02-14.yang" was installed. [INF]: Sysrepo internal dependency module "ietf-datastores" was installed. [INF]: File "ietf-yang-library@2019-01-04.yang" was installed. [INF]: Sysrepo internal module "ietf-yang-library" was installed. [INF]: File "sysrepo-monitoring@2021-01-15.yang" was installed. [INF]: Sysrepo internal module "sysrepo-monitoring" was installed. [INF]: File "sysrepo-plugind@2020-12-10.yang" was installed. [INF]: Sysrepo internal module "sysrepo-plugind" was installed. [INF]: File "ietf-netconf@2011-06-01.yang" was installed. [INF]: Sysrepo internal dependency module "ietf-netconf" was installed. [INF]: File "ietf-netconf-with-defaults@2011-06-01.yang" was installed. [INF]: Sysrepo internal module "ietf-netconf-with-defaults" was installed. [INF]: File "ietf-netconf-notifications@2012-02-06.yang" was installed. [INF]: Sysrepo internal module "ietf-netconf-notifications" was installed. [INF]: File "ietf-origin@2018-02-14.yang" was installed. [INF]: Sysrepo internal module "ietf-origin" was installed. [INF]: Connection 20 created. [INF]: Module "keatest-module" scheduled for installation. [INF]: Applying scheduled changes. [INF]: File "keatest-module@2018-11-20.yang" was installed. [INF]: Module "keatest-module" was installed. [INF]: Scheduled changes applied. [INF]: Connection 21 created. [INF]: Module "ietf-interfaces" scheduled for installation. [INF]: Applying scheduled changes. [INF]: File "ietf-interfaces@2018-02-20.yang" was installed. [INF]: Module "ietf-interfaces" was installed. [INF]: Scheduled changes applied. [INF]: Connection 22 created. [INF]: Module "ietf-dhcpv6-client" scheduled for installation. [INF]: File "ietf-dhcpv6-options@2018-09-04.yang" was installed. [INF]: File "ietf-dhcpv6-types@2018-09-04.yang" was installed. [INF]: Applying scheduled changes. [INF]: File "ietf-dhcpv6-client@2018-09-04.yang" was installed. [INF]: Module "ietf-dhcpv6-client" was installed. [INF]: Scheduled changes applied. [INF]: Connection 23 created. [INF]: Module "ietf-dhcpv6-relay" scheduled for installation. [INF]: Applying scheduled changes. [INF]: File "ietf-dhcpv6-relay@2018-09-04.yang" was installed. [INF]: Module "ietf-dhcpv6-relay" was installed. [INF]: Scheduled changes applied. [INF]: Connection 24 created. [INF]: Module "ietf-dhcpv6-server" scheduled for installation. [INF]: Applying scheduled changes. [INF]: File "ietf-dhcpv6-server@2018-09-04.yang" was installed. [INF]: Module "ietf-dhcpv6-server" was installed. [INF]: Scheduled changes applied. [INF]: Connection 25 created. [INF]: Module "ietf-yang-types" scheduled for installation. [INF]: Applying scheduled changes. [INF]: Module "ietf-yang-types" was installed. [INF]: Scheduled changes applied. [INF]: Connection 26 created. [INF]: Module "ietf-dhcpv6-options" scheduled for installation. [INF]: Applying scheduled changes. [INF]: Module "ietf-dhcpv6-options" was installed. [INF]: Scheduled changes applied. [INF]: Connection 27 created. [INF]: Module "ietf-dhcpv6-types" scheduled for installation. [INF]: Applying scheduled changes. [INF]: Module "ietf-dhcpv6-types" was installed. [INF]: Scheduled changes applied. [INF]: Connection 28 created. [INF]: Module "ietf-inet-types" scheduled for installation. [INF]: Applying scheduled changes. [INF]: Module "ietf-inet-types" was installed. [INF]: Scheduled changes applied. [INF]: Connection 29 created. [INF]: Module "kea-types" scheduled for installation. [INF]: Applying scheduled changes. [INF]: File "kea-types@2019-08-12.yang" was installed. [INF]: Module "kea-types" was installed. [INF]: Scheduled changes applied. [INF]: Connection 30 created. [INF]: Module "kea-dhcp-types" scheduled for installation. [INF]: Applying scheduled changes. [INF]: File "kea-dhcp-types@2019-08-12.yang" was installed. [INF]: Module "kea-dhcp-types" was installed. [INF]: Scheduled changes applied. [INF]: Connection 31 created. [INF]: Module "kea-dhcp-ddns" scheduled for installation. [INF]: Applying scheduled changes. [INF]: File "kea-dhcp-ddns@2019-08-12.yang" was installed. [INF]: Module "kea-dhcp-ddns" was installed. [INF]: Scheduled changes applied. [INF]: Connection 32 created. [INF]: Module "kea-ctrl-agent" scheduled for installation. [INF]: Applying scheduled changes. [INF]: File "kea-ctrl-agent@2019-08-12.yang" was installed. [INF]: Module "kea-ctrl-agent" was installed. [INF]: Scheduled changes applied. [INF]: Connection 33 created. [INF]: Module "kea-dhcp4-server" scheduled for installation. [INF]: Applying scheduled changes. [INF]: File "kea-dhcp4-server@2019-08-12.yang" was installed. [INF]: Module "kea-dhcp4-server" was installed. [INF]: Scheduled changes applied. [INF]: Connection 34 created. [INF]: Module "kea-dhcp6-server" scheduled for installation. It is possible to confirm whether the models are imported correctly. To list the currently installed YANG modules: .. code-block:: console $ sysrepoctl -l After installation the result should be similar to this: :: Sysrepo repository: /etc/sysrepo Module Name | Revision | Flags | Owner | Permissions | Submodules | Features ----------------------------------------------------------------------------------------------------- ietf-datastores | 2018-02-14 | I | user:user | 664 | | ietf-dhcpv6-client | 2018-09-04 | I | user:user | 600 | | ietf-dhcpv6-options | 2018-09-04 | I | user:user | 600 | | ietf-dhcpv6-relay | 2018-09-04 | I | user:user | 600 | | ietf-dhcpv6-server | 2018-09-04 | I | user:user | 600 | | ietf-dhcpv6-types | 2018-09-04 | I | user:user | 600 | | ietf-inet-types | 2013-07-15 | I | user:user | 664 | | ietf-interfaces | 2018-02-20 | I | user:user | 600 | | ietf-netconf | 2011-06-01 | I | user:user | 664 | | ietf-netconf-notifications | 2012-02-06 | I | user:user | 664 | | ietf-netconf-with-defaults | 2011-06-01 | I | user:user | 664 | | ietf-origin | 2018-02-14 | I | user:user | 664 | | ietf-yang-library | 2019-01-04 | I | user:user | 664 | | ietf-yang-metadata | 2016-08-05 | i | | | | ietf-yang-types | 2013-07-15 | I | user:user | 664 | | kea-ctrl-agent | 2019-08-12 | I | user:user | 600 | | kea-dhcp-ddns | 2019-08-12 | I | user:user | 600 | | kea-dhcp-types | 2019-08-12 | I | user:user | 600 | | kea-dhcp4-server | 2019-08-12 | I | user:user | 600 | | kea-dhcp6-server | 2019-08-12 | I | user:user | 600 | | kea-types | 2019-08-12 | I | user:user | 600 | | keatest-module | 2018-11-20 | I | user:user | 600 | | sysrepo-monitoring | 2021-01-15 | I | user:user | 600 | | sysrepo-plugind | 2020-12-10 | I | user:user | 664 | | yang | 2017-02-20 | I | user:user | 664 | | Flags meaning: I - Installed/i - Imported; R - Replay support; N - New/X - Removed/U - Updated; F - Feature changes Features: ! - Means that the feature is effectively disabled because of its false if-feature(s) To reinstall a module, if the revision YANG entry was bumped, simply installing it will update it automatically. Otherwise, it must first be uninstalled: .. code-block:: console $ sysrepoctl -u kea-dhcp4-server If the module is used (i.e. imported) by other modules, it can be uninstalled only after the dependant modules have been uninstalled first. Installation and uninstallation must be done in dependency order and reverse-dependency order accordingly. .. _netconf-models: Supported YANG Models --------------------- The only currently supported models are ``kea-dhcp4-server`` and ``kea-dhcp6-server``. There is partial support for ``ietf-dhcpv6-server``, but the primary focus of testing has been on Kea DHCP servers. Several other models (``kea-dhcp-ddns`` and ``kea-ctrl-agent``) are currently not supported. .. _using-netconf: Using the NETCONF Agent ----------------------- The NETCONF agent follows this algorithm: - For each managed server, get the initial configuration from the server through the control socket. - Open a connection with the Sysrepo environment and establish two sessions with the startup and running datastores. - Check that used (not essential) and required (essential) modules are installed in the Sysrepo repository at the right revision. If an essential module - that is, a module where the configuration schema for a managed server is defined - is not installed, raise a fatal error. - For each managed server, get the YANG configuration from the startup datastore, translate it to JSON, and load it onto the server being configured. - For each managed server, subscribe a module change callback using its model name. - When a running configuration is changed, try to validate or load the updated configuration via the callback to the managed server. .. _netconf-configuration: Configuration ------------- The behavior described in :ref:`using-netconf` is controlled by a few configuration flags, which can be set in the global scope or in a specific managed-server scope. In the second case, the value defined in the managed-server scope takes precedence. These flags are: - ``boot-update`` - controls the initial configuration phase; when true (the default), the initial configuration retrieved from the classic Kea server JSON configuration file is loaded first, and then the startup YANG model is loaded. This setting lets administrators define a control socket in the local JSON file and then download the configuration from YANG. When set to false, this phase is skipped. - ``subscribe-changes`` - controls the module change subscription; when true (the default), a module change callback is subscribed, but when false the phase is skipped and running configuration updates are disabled. When set to true, the running datastore is used to subscribe for changes. - ``validate-changes`` - controls how Kea monitors changes in the Sysrepo configuration. Sysrepo offers two stages where Kea can interact: validation and application. At the validation (or SR_EV_CHANGE event, in the Sysrepo naming convention) stage, Kea retrieves the newly committed configuration and verifies it. If the configuration is incorrect for any reason, the Kea servers reject it and the error is propagated back to the Sysrepo, which then returns an error. This step only takes place if validate-changes is set to true. In the application (or SR_EV_UPDATE event in the Sysrepo naming convention) stage, the actual configuration is applied. At this stage Kea can receive the configuration, but it is too late to signal back any errors as the configuration has already been committed. The idea behind the initial configuration phase is to boot Kea servers with a minimal configuration which includes only a control socket, making them manageable. For instance, for the DHCPv4 server: .. code-block:: json { "Dhcp4": { "control-socket": { "socket-name": "/tmp/kea-dhcp4-ctrl.sock", "socket-type": "unix" } } } With module change subscriptions enabled, the ``kea-netconf`` daemon will monitor any configuration changes as they appear in the Sysrepo. Such changes can be done using the ``sysrepocfg`` tool or remotely using any NETCONF client. For details, please see the Sysrepo documentation or :ref:`operation-example`. Those tools can be used to modify YANG configurations in the running datastore. Note that committed configurations are only updated in the running datastore; to keep them between server reboots they must be copied to the startup datastore. When module changes are tracked (using ``subscribe-changes`` set to true) and the running configuration has changed (e.g. using ``sysrepocfg`` or any NETCONF client), the callback validates the modified configuration (if ``validate-changes`` was not set to false) and runs a second time to apply the new configuration. If the validation fails, the callback is still called again but with an SR_EV_ABORT (vs. SR_EV_DONE) event with rollback changes. The returned code of the callback on an SR_EV_DONE event is ignored, as it is too late to refuse a bad configuration. There are four ways in which a modified YANG configuration could possibly be incorrect: 1. It can be non-compliant with the schema, e.g. an unknown entry, missing a mandatory entry, a value with a bad type, or not matching a constraint. 2. It can fail to be translated from YANG to JSON, e.g. an invalid user context. 3. It can fail Kea server sanity checks, e.g. an out-of-subnet-pool range or an unsupported database type. 4. The syntax may be correct and pass server sanity checks but the configuration fails to run, e.g. the configuration specifies database credentials but the database refuses the connection. The first case is handled by Sysrepo. The second and third cases are handled by kea-netconf in the validation phase (if not disabled by setting ``validate-changes`` to true). The last case causes the application phase to fail without a sensible report to Sysrepo. The managed Kea servers or agents are described in the ``managed-servers`` section. Each sub-section begins by the service name: ``dhcp4``, ``dhcp6``, ``d2`` (the DHCP-DDNS server does not support the control channel feature yet), and ``ca`` (the control agent). Each managed server entry contains optionally: - ``boot-update``, ``subscribe-changes``, and ``validate-changes`` - control flags. - ``model`` - specifies the YANG model / module name. For each service, the default is the corresponding Kea YANG model, e.g. for ``"dhcp4"`` it is ``"kea-dhcp4-server"``. - ``control-socket`` - specifies the control socket for managing the service configuration. A control socket is specified by: - ``socket-type`` - the socket type is either ``stdout``, ``unix``, or ``http``. ``stdout`` is the default; it is not really a socket, but it allows ``kea-netconf`` to run in debugging mode where everything is printed on stdout, and it can also be used to redirect commands easily. ``unix`` is the standard direct server control channel, which uses UNIX sockets, and ``http`` uses a control agent, which accepts HTTP connections. - ``socket-name`` - the local socket name for the ``unix`` socket type (default empty string). - ``socket-url`` - the HTTP URL for the ``http`` socket type (default ``http://127.0.0.1:8000/``). User contexts can store arbitrary data as long as they are in valid JSON syntax and their top-level element is a map (i.e. the data must be enclosed in curly brackets). They are accepted at the NETCONF entry, i.e. below the top-level, managed-service entry, and control-socket entry scopes. Hooks libraries can be loaded by the NETCONF agent just as with other servers or agents; however, currently no hook points are defined. The ``hooks-libraries`` list contains the list of hooks libraries that should be loaded by kea-netconf, along with their configuration information specified with ``parameters``. Please consult :ref:`logging` for details on how to configure logging. The name of the NETCONF agent's main logger is ``kea-netconf``, as given in the example above. .. _netconf-example: A kea-netconf Configuration Example ----------------------------------- The following example demonstrates the basic NETCONF configuration. More examples are available in the ``doc/examples/netconf`` directory in the Kea sources. .. code-block:: javascript // This is a simple example of a configuration for the NETCONF agent. // This server provides a YANG interface for all Kea servers and the agent. { "Netconf": { // Control flags can be defined in the global scope or // in a managed server scope. Precedences are: // - use the default value (true) // - use the global value // - use the local value. // So this overwrites the default value: "boot-update": false, // This map specifies how each server is managed. For each server there // is a name of the YANG model to be used and the control channel. // // Currently three control channel types are supported: // "stdout" which outputs the configuration on the standard output, // "unix" which uses the local control channel supported by the // "dhcp4" and "dhcp6" servers ("d2" support is not yet available), // and "http" which uses the Control Agent "ca" to manage itself or // to forward commands to "dhcp4" or "dhcp6". "managed-servers": { // This is how kea-netconf can communicate with the DHCPv4 server. "dhcp4": { "comment": "DHCP4 server", "model": "kea-dhcp4-server", "control-socket": { "socket-type": "unix", "socket-name": "/tmp/kea4-ctrl-socket" } }, // DHCPv6 parameters. "dhcp6": { "model": "kea-dhcp6-server", "control-socket": { "socket-type": "unix", "socket-name": "/tmp/kea6-ctrl-socket" } }, // Currently the DHCP-DDNS (nicknamed D2) server does not support // a command channel. "d2": { "model": "kea-dhcp-ddns", "control-socket": { "socket-type": "stdout", "user-context": { "in-use": false } } }, // Of course the Control Agent (CA) supports HTTP. "ca": { "model": "kea-ctrl-agent", "control-socket": { "socket-type": "http", "socket-url": "http://127.0.0.1:8000/" } } }, // kea-netconf is able to load hooks libraries that augment its operation. // Currently there are no hook points defined in kea-netconf // processing. "hooks-libraries": [ // The hooks libraries list may contain more than one library. { // The only necessary parameter is the library filename. "library": "/opt/local/netconf-commands.so", // Some libraries may support parameters. Make sure you // type this section carefully, as kea-netconf does not // validate it (because the format is library-specific). "parameters": { "param1": "foo" } } ], // Similar to other Kea components, NETCONF also uses logging. "loggers": [ { "name": "kea-netconf", "output_options": [ { "output": "/var/log/kea-netconf.log", // Several additional parameters are possible in // addition to the typical output. // Flush determines whether logger flushes output // to a file. // Maxsize determines maximum filesize before // the file is being rotated. // Maxver specifies the maximum number of // rotated files being kept. "flush": true, "maxsize": 204800, "maxver": 4 } ], "severity": "INFO", "debuglevel": 0 } ] } } .. _netconf-start-stop: Starting and Stopping the NETCONF Agent --------------------------------------- kea-netconf accepts the following command-line switches: - ``-c file`` - specifies the configuration file. - ``-d`` - specifies whether the agent logging should be switched to debug/verbose mode. In verbose mode, the logging severity and debuglevel specified in the configuration file are ignored and "debug" severity and the maximum debuglevel (99) are assumed. The flag is convenient for temporarily switching the server into maximum verbosity, e.g. when debugging. - ``-t file`` - specifies the configuration file to be tested. Kea-netconf attempts to load it and conducts sanity checks; note that certain checks are possible only while running the actual server. The actual status is reported with exit code (0 = configuration looks ok, 1 = error encountered). Kea will print out log messages to standard output and error to standard error when testing configuration. - ``-v`` - displays the version of kea-netconf and exits. - ``-V`` - displays the extended version information for kea-netconf and exits. The listing includes the versions of the libraries dynamically linked to Kea. - ``-W`` - displays the Kea configuration report and exits. The report is a copy of the ``config.report`` file produced by ``./configure``; it is embedded in the executable binary. .. _operation-example: A Step-by-Step NETCONF Agent Operation Example ---------------------------------------------- .. note:: Copies of example configurations presented within this section can be found in the Kea source code, under ``doc/examples/netconf/kea-dhcp6-operations``. .. _operation-example-setup: Setup of NETCONF Agent Operation Example ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The test box has an Ethernet interface named eth1. On some systems it is possible to rename interfaces, for instance on a Linux with an ens38 interface: .. code-block:: console # ip link set down dev ens38 # ip link set name eth1 dev ens38 # ip link set up dev eth1 The interface must have an address in the test prefix: .. code-block:: console # ip -6 addr add 2001:db8::1/64 dev eth1 The Kea DHCPv6 server must be launched with the configuration specifying a control socket used to receive control commands. The ``kea-netconf`` process uses this socket to communicate with the DHCPv6 server, i.e. it pushes translated configurations to that server using control commands. The following is the example control socket specification for the Kea DHCPv6 server: .. code-block:: json { "Dhcp6": { "control-socket": { "socket-name": "/tmp/kea-dhcp6-ctrl.sock", "socket-type": "unix" } } } In order to launch the Kea DHCPv6 server using the configuration contained within the ``boot.json`` file, run: .. code-block:: console # kea-dhcp6 -d -c boot.json The current configuration of the server can be fetched via control socket by running: .. code-block:: console # echo '{ "command": "config-get" }' | socat UNIX:/tmp/kea-dhcp6-ctrl.sock '-,ignoreeof' The following is the example ``netconf.json`` configuration for ``kea-netconf``, to manage the Kea DHCPv6 server: .. code-block:: json { "Netconf": { "loggers": [ { "debuglevel": 99, "name": "kea-netconf", "output_options": [ { "output": "stderr" } ], "severity": "DEBUG" } ], "managed-servers": { "dhcp6": { "control-socket": { "socket-name": "/tmp/kea-dhcp6-ctrl.sock", "socket-type": "unix" } } } } } Note that in production there should not be a need to log at the DEBUG level. The Kea NETCONF agent is launched by: .. code-block:: console # kea-netconf -d -c netconf.json Now that both ``kea-netconf`` and ``kea-dhcp6`` are running, it is possible to populate updates to the configuration to the DHCPv6 server. The following is the configuration extracted from ``startup.xml``: .. code-block:: xml 1 2001:db8::1:0 2001:db8::1:ffff 2001:db8::1:0/112 2001:db8::/64 eth1 /tmp/kea-dhcp6-ctrl.sock unix To populate this new configuration: .. code-block:: console $ sysrepocfg -d startup -f xml -m kea-dhcp6-server --edit=startup.xml ``kea-netconf`` pushes the configuration found in the Sysrepo startup datastore to all Kea servers during its initialization phase, after it subscribes to module changes in the Sysrepo running datastore. This action copies the configuration from the startup datastore to the running datastore and enables the running datastore, making it available. Changes to the running datastore are applied after validation to the Kea servers. Note that they are not by default copied back to the startup datastore, i.e. changes are not permanent. .. _operation-example-errors: Error Handling in NETCONF Operation Example ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are four classes of issues with the configurations applied via NETCONF: 1. The configuration does not comply with the YANG schema. 2. The configuration cannot be translated from YANG to the Kea JSON. 3. The configuration is rejected by the Kea server. 4. The configuration was validated by the Kea server but cannot be applied. In the first case, consider the following ``BAD-schema.xml`` configuration file: .. code-block:: xml 1 2001:db8::1:0 2001:db8::1:ffff 2001:db8::1:0/112 2001:db8::/64 eth1 /tmp/kea-dhcp6-ctrl.sock unix It is directly rejected by ``sysrepocfg``: .. code-block:: console $ sysrepocfg -d running -f xml -m kea-dhcp6-server --edit=BAD-schema.xml In the second case, the configuration is rejected by ``kea-netconf``. For example, consider this ``BAD-translator.xml`` file: .. code-block:: xml 1 2001:db8::1:0 2001:db8::1:ffff 2001:db8::1:0/112 2001:db8::/64 eth1 /tmp/kea-dhcp6-ctrl.sock unix bad In the third case, the configuration is presented to the Kea DHCPv6 server and fails to validate as in this ``BAD-config.xml`` file: .. code-block:: xml 1 2001:db8:1::0 2001:db8:1::ffff 2001:db8:1::0/112 2001:db8::/64 eth1 /tmp/kea-dhcp6-ctrl.sock unix In the last case, the misconfiguration is detected too late and the change must be reverted in Sysrepo, e.g. using the startup datastore as a backup. .. _operation-example-2pools: NETCONF Operation Example with Two Pools ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This example adds a second pool to the initial (i.e. startup) configuration in the ``twopools.xml`` file: .. code-block:: xml 1 2001:db8::1:0 2001:db8::1:ffff 2001:db8::1:0/112 2001:db8::2:0 2001:db8::2:ffff 2001:db8::2:0/112 2001:db8::/64 eth1 /tmp/kea-dhcp6-ctrl.sock unix This configuration is installed by: .. code-block:: console $ sysrepocfg -d running -f xml -m kea-dhcp6-server --edit=twopools.xml .. _operation-example-2subnets: NETCONF Operation Example with Two Subnets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This example specifies two subnets in the ``twosubnets.xml`` file: .. code-block:: xml 1 2001:db8:1:: 2001:db8:1::ffff 2001:db8:1::/112 2001:db8:1::/64 2 2001:db8:2:: 2001:db8:2::ffff 2001:db8:2::/112 2001:db8:2::/64 eth1 /tmp/kea-dhcp6-ctrl.sock unix This configuration is installed by: .. code-block:: console $ sysrepocfg -d running -f xml -m kea-dhcp6-server --edit=twosubnets.xml .. _operation-example-logging: NETCONF Operation Example with Logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This example adds a logger entry to the initial (i.e. startup) configuration in the ``logging.xml`` file: .. code-block:: xml eth1 1 2001:db8::1:0 2001:db8::1:ffff 2001:db8::1:0/112 2001:db8::/64 /tmp/kea-dhcp6-ctrl.sock unix kea-dhcp6 stderr 99 DEBUG The corresponding Kea configuration in JSON is: .. code-block:: json { "Dhcp6": { "control-socket": { "socket-name": "/tmp/kea-dhcp6-ctrl.sock", "socket-type": "unix" }, "interfaces-config": { "interfaces": [ "eth1" ] }, "subnet6": [ { "id": 1, "pools": [ { "pool": "2001:db8::1:0/112" } ], "subnet": "2001:db8::/64" } ], "loggers": [ { "name": "kea-dhcp6", "output_options": [ { "output": "stderr" } ], "severity": "DEBUG", "debuglevel": 99 } ] } } Finally, any of the previous examples can be replayed by using ``sysrepocfg`` in edit mode as follows: .. code-block:: console $ sysrepocfg -d running -f xml -m kea-dhcp6-server --edit or by using a NETCONF client like ``netopeer2-cli`` from the `Netopeer2 `__ NETCONF Toolset. .. _migrating-yang-v0-to-v1: Migrating YANG data from sysrepo v0.x to v1.x ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Start the migration after turning off kea-netconf to make sure that backups done for both datastores are done at the same configuration state and no change happens between exporting them. Unfortunately, sysrepo v0.x does not support import/export of all YANG modules. This was added in sysrepo v1.x. You will need to do per-module backup. It's probably for the best, for isolating potential failures and preventing them from affecting all your modules. With sysrepo v0.x: .. code-block:: console $ sysrepocfg --datastore running --export=save.xml --format=xml kea-dhcp6-server $ sysrepocfg --datastore startup --export=save.xml --format=xml kea-dhcp6-server Install sysrepo v1.x and then: .. code-block:: console $ sysrepocfg --datastore running --edit=save.xml $ sysrepocfg --datastore startup --edit=save.xml Module name and format are optional for v1.x, they are detected automatically. In case of trouble, they can be provided with the ``--format xml`` and ``--module kea-dhcp6-server`` flags. If you upgraded after a long time, there might also be changes to the YANG modules themselves. In that case the backups will need some minor massaging. But this will be a dilligence that will have to be carried out occasionally and completely independent of sysrepo upgrades. kea-2.0.2/doc/sphinx/arm/admin.rst0000644000175000017500000006403114206773363013672 00000000000000.. _admin: *************************** Kea Database Administration *************************** .. _kea-database-version: Databases and Database Version Numbers ====================================== Kea may be configured to use a database as storage for leases or as a source of servers' configurations and host reservations (i.e. static assignments of addresses, prefixes, options, etc.). As Kea is updated, new database schemas are introduced to facilitate new features and correct discovered issues with the existing schemas. Each version of Kea expects a particular structure in the backend and checks for this by examining the version of the database it is using. Separate version numbers are maintained for the backends, independent of the version of Kea itself. It is possible that the backend version will stay the same through several Kea revisions; similarly, it is possible that the version of the backend may go up several revisions during a single Kea version upgrade. Versions for each backend are also independent, so an increment in the MySQL backend version does not imply an increment in that of PostgreSQL. Backend versions are specified in a major.minor format. The minor number is increased when there are backward-compatible changes introduced: for example, when a new index is added. It is desirable but not mandatory to apply such a change; running an older backend version is possible. (Although, in the example given, running without the new index may introduce a performance penalty.) On the other hand, the major number is increased when an incompatible change is introduced: for example, an extra column is added to a table. If Kea attempts to run on a backend that is too old (as indicated by a mismatched backend major version number), it will fail; administrative action is required to upgrade the backend. .. _kea-admin: The kea-admin Tool ================== To manage the databases, Kea provides the ``kea-admin`` tool. It can initialize a new backend, check its version number, perform a backend upgrade, and dump lease data to a text file. ``kea-admin`` takes two mandatory parameters: ``command`` and ``backend``. Additional, non-mandatory options may be specified. The currently supported commands are: - ``db-init`` — Initializes a new database schema. This is useful during a new Kea installation. The database is initialized to the latest version supported by the version of the software being installed. - ``db-version`` — Reports the database backend version number. This is not necessarily equal to the Kea version number, as each backend has its own versioning scheme. - ``db-upgrade`` — Conducts a database schema upgrade. This is useful when upgrading Kea. - ``lease-dump`` — Dumps the contents of the lease database (for MySQL, PostgreSQL, or CQL backends) to a CSV (comma-separated values) text file. The first line of the file contains the column names. This is meant to be used as a diagnostic tool, so it provides a portable, human-readable form of the lease data. .. note:: In versions of Kea earlier than 1.6.0, the `db-init`, `db-version`, and `db-upgrade` commands were named `lease-init`, `lease-version`, and `lease-upgrade`, respectively. ``backend`` specifies the type of backend database. The currently supported types are: - ``memfile`` — Lease information is stored on disk in a text file. - ``mysql`` — Information is stored in a MySQL relational database. - ``pgsql`` — Information is stored in a PostgreSQL relational database. - ``cql`` — Information is stored in an Apache Cassandra database. This backend is deprecated. Additional parameters may be needed, depending on the setup and specific operation: username, password, and database name or the directory where specific files are located. See the appropriate manual page for details (``man 8 kea-admin``). .. _supported-databases: Supported Backends ================== The following table presents the capabilities of available backends. Please refer to the specific sections dedicated to each backend to better understand their capabilities and limitations. Choosing the right backend is essential for the success of the deployment. .. table:: List of available backends +---------------+----------------+----------------+---------------+--------------+ | Feature | Memfile | MySQL | PostgreSQL | CQL | | | | | | (Cassandra) | +===============+================+================+===============+==============+ | Status | Stable | Stable | Stable | Deprecated | | | | | | | +---------------+----------------+----------------+---------------+--------------+ | Data format | CSV file | SQL RMDB | SQL RMDB | NoSQL | | | | | | database | | | | | | (Cassandra) | +---------------+----------------+----------------+---------------+--------------+ | Leases | yes | yes | yes | yes | +---------------+----------------+----------------+---------------+--------------+ | Host | no | yes | yes | yes | | reservations | | | | | | | | | | | +---------------+----------------+----------------+---------------+--------------+ | Options | no | yes | yes | yes | | defined on | | | | | | per host | | | | | | basis | | | | | +---------------+----------------+----------------+---------------+--------------+ | Configuration | no | yes | no | no | | backend | | | | | | | | | | | +---------------+----------------+----------------+---------------+--------------+ Memfile ------- The memfile backend is able to store lease information, but cannot store host reservation details; these must be stored in the configuration file. (There are no plans to add a host reservations storage capability to this backend.) No special initialization steps are necessary for the memfile backend. During the first run, both ``kea-dhcp4`` and ``kea-dhcp6`` create an empty lease file if one is not present. Necessary disk-write permission is required. .. _memfile-upgrade: Upgrading Memfile Lease Files From an Earlier Version of Kea ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are no special steps required to upgrade memfile lease files between versions of Kea. During startup, the servers check the schema version of the lease files against their own. If there is a mismatch, the servers automatically launch the LFC process to convert the files to the server's schema version. While this mechanism is primarily meant to ease the process of upgrading to newer versions of Kea, it can also be used for downgrading should the need arise. When upgrading, any values not present in the original lease files are assigned appropriate default values. When downgrading, any data present in the files but not in the server's schema are dropped. To convert the files manually prior to starting the servers, run the lease file cleanup (LFC) process. See :ref:`kea-lfc` for more information. .. _mysql-database: MySQL ----- MySQL is able to store leases, host reservations, options defined on a per-host basis, and a subset of the server configuration parameters (serving as a configuration backend). .. _mysql-database-create: First-Time Creation of the MySQL Database ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Before preparing any Kea-specific database and tables, the MySQL database must be configured to use the system timezone. It is recommended to use UTC as the timezone for both the system and the MySQL database. To check the system timezone: .. code-block:: console date +%Z To check the MySQL timezone: .. code-block:: mysql mysql> SELECT @@system_time_zone; mysql> SELECT @@global.time_zone; mysql> SELECT @@session.time_zone; To configure the MySQL timezone for a specific server, please refer to the installed version documentation. Usually the setting is configured in the [mysqld] section in /etc/mysql/my.cnf, /etc/mysql/mysql.cnf, /etc/mysql/mysqld.cnf, or /etc/mysql/mysql.conf.d/mysqld.cnf. .. code-block:: ini [mysqld] # using default-time-zone default-time-zone='+00:00' # or using timezone timezone='UTC' When setting up the MySQL database for the first time, the database area must be created within MySQL, and the MySQL user ID under which Kea will access the database must be set up. This needs to be done manually, rather than via ``kea-admin``. To create the database: 1. Log into MySQL as "root": .. code-block:: console $ mysql -u root -p Enter password: mysql> 2. Create the MySQL database: .. code-block:: mysql mysql> CREATE DATABASE database_name; (``database_name`` is the name chosen for the database.) 3. Create the user under which Kea will access the database (and give it a password), then grant it access to the database tables: .. code-block:: mysql mysql> CREATE USER 'user-name'@'localhost' IDENTIFIED BY 'password'; mysql> GRANT ALL ON database-name.* TO 'user-name'@'localhost'; (``user-name`` and ``password`` are the user ID and password used to allow Kea access to the MySQL instance. All apostrophes in the command lines above are required.) 4. Create the database. Exit the MySQL client .. code-block:: mysql mysql> quit Bye Then use the ``kea-admin`` tool to create the database. .. code-block:: console $ kea-admin db-init mysql -u database-user -p database-password -n database-name While it is possible to create the database from within the MySQL client, we recommend using the ``kea-admin`` tool as it performs some necessary validations to ensure Kea can access the database at runtime. Among those checks is verification that the schema does not contain any pre-existing tables; any pre-existing tables must be removed manually. An additional check examines the user's ability to create functions and triggers. The following error indicates that the user does not have the necessary permissions to create functions or triggers: .. code-block:: console ERROR 1419 (HY000) at line 1: You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable) ERROR/kea-admin: mysql_can_create cannot trigger, check user permissions, mysql status = 1 mysql: [Warning] Using a password on the command line interface can be insecure. ERROR/kea-admin: Create failed, the user, keatest, has insufficient privileges. The simplest way around this is to set the global MySQL variable, ``log_bin_trust_function_creators``, to 1 via the MySQL client. Note this must be done as a user with SUPER privileges: .. code-block:: mysql mysql> set @@global.log_bin_trust_function_creators = 1; Query OK, 0 rows affected (0.00 sec) To create the database with MySQL directly, follow these steps: .. code-block:: mysql mysql> CONNECT database-name; mysql> SOURCE path-to-kea/share/kea/scripts/mysql/dhcpdb_create.mysql (where "path-to-kea" is the location where Kea is installed.) The database may also be dropped manually as follows: .. code-block:: mysql mysql> CONNECT database-name; mysql> SOURCE path-to-kea/share/kea/scripts/mysql/dhcpdb_drop.mysql (where "path-to-kea" is the location where Kea is installed.) .. warning:: Dropping the database results in the unrecoverable loss of any data it contains. 5. Exit MySQL: .. code-block:: mysql mysql> quit Bye If the tables were not created in Step 4, run the ``kea-admin`` tool to create them now: .. code-block:: console $ kea-admin db-init mysql -u database-user -p database-password -n database-name Do not do this if the tables were created in Step 4. ``kea-admin`` implements rudimentary checks; it will refuse to initialize a database that contains any existing tables. To start from scratch, all data must be removed manually. (This process is a manual operation on purpose, to avoid accidentally irretrievable mistakes by ``kea-admin``.) .. _mysql-upgrade: Upgrading a MySQL Database From an Earlier Version of Kea ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sometimes a new Kea version uses a newer database schema, so the existing database needs to be upgraded. This can be done using the ``kea-admin db-upgrade`` command. To check the current version of the database, use the following command: .. code-block:: console $ kea-admin db-version mysql -u database-user -p database-password -n database-name (See :ref:`kea-database-version` for a discussion about versioning.) If the version does not match the minimum required for the new version of Kea (as described in the release notes), the database needs to be upgraded. Before upgrading, please make sure that the database is backed up. The upgrade process does not discard any data, but depending on the nature of the changes, it may be impossible to subsequently downgrade to an earlier version. To perform an upgrade, issue the following command: .. code-block:: console $ kea-admin db-upgrade mysql -u database-user -p database-password -n database-name .. note:: To search host reservations by hostname, it is critical that the collation of the hostname column in the host table be case-insensitive. Fortunately, that is the default in MySQL, but it can be verified via this command: .. code-block:: mysql mysql> SELECT COLLATION(''); +-----------------+ | COLLATION('') | +-----------------+ | utf8_general_ci | +-----------------+ According to mysql's naming convention, when the name ends in ``_ci``, the collation is case-insensitive. .. _mysql-performance: Improved Performance With MySQL ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Changing the MySQL internal value ``innodb_flush_log_at_trx_commit`` from the default value of ``1`` to ``2`` can result in a huge gain in Kea performance. In some deployments, the gain was over 1000% (10 times faster when set to 2, compared to the default value of 1). It can be set per-session for testing: .. code-block:: mysql mysql> SET GLOBAL innodb_flush_log_at_trx_commit=2; mysql> SHOW SESSION VARIABLES LIKE 'innodb_flush_log%'; or permanently in ``/etc/mysql/my.cnf``: .. code-block:: ini [mysqld] innodb_flush_log_at_trx_commit=2 Be aware that changing this value can cause problems during data recovery after a crash, so we recommend checking the `MySQL documentation `__. With the default value of 1, MySQL writes changes to disk after every INSERT or UPDATE query (in Kea terms, every time a client gets a new lease or renews an existing lease). When ``innodb_flush_log_at_trx_commit`` is set to 2, MySQL writes the changes at intervals no longer than 1 second. Batching writes gives a substantial performance boost. The trade-off, however, is that in the worst-case scenario, all changes in the last second before crash could be lost. Given the fact that Kea is stable software and crashes very rarely, most deployments find it a beneficial trade-off. .. _pgsql-database: PostgreSQL ---------- PostgreSQL can store leases, host reservations, and options defined on a per-host basis. .. _pgsql-database-create: First-Time Creation of the PostgreSQL Database ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Before preparing any Kea-specific database and tables, the PostgreSQL database must be configured to use the system timezone. It is recommended to use UTC as the timezone for both the system and the PostgreSQL database. To check the system timezone: .. code-block:: console date +%Z To check the PostgreSQL timezone: .. code-block:: psql postgres=# show timezone; postgres=# SELECT * FROM pg_timezone_names WHERE name = current_setting('TIMEZONE'); To configure the PostgreSQL timezone for a specific server, please refer to the installed version documentation. Usually the setting is configured in the ``postgresql.conf`` with the varying version path ``/etc/postgresql//main/postgresql.conf``, but on some systems the files may be located in ``/var/lib/pgsql/data``. .. code-block:: ini timezone = 'UTC' The first task is to create both the database and the user under which the servers will access it. A number of steps are required: 1. Log into PostgreSQL as "root": .. code-block:: console $ sudo -u postgres psql postgres Enter password: postgres=# 2. Create the database: .. code-block:: psql postgres=# CREATE DATABASE database-name; CREATE DATABASE postgres=# (``database-name`` is the name chosen for the database.) 3. Create the user under which Kea will access the database (and give it a password), then grant it access to the database: .. code-block:: psql postgres=# CREATE USER user-name WITH PASSWORD 'password'; CREATE ROLE postgres=# GRANT ALL PRIVILEGES ON DATABASE database-name TO user-name; GRANT postgres=# 4. Exit PostgreSQL: .. code-block:: psql postgres=# \q Bye $ 5. At this point, create the database tables either using the ``kea-admin`` tool, as explained in the next section (recommended), or manually. To create the tables manually, enter the following command. PostgreSQL will prompt the administrator to enter the new user's password that was specified in Step 3. When the command completes, Kea will return to the shell prompt. The output should be similar to the following: .. code-block:: console $ psql -d database-name -U user-name -f path-to-kea/share/kea/scripts/pgsql/dhcpdb_create.pgsql Password for user user-name: CREATE TABLE CREATE INDEX CREATE INDEX CREATE TABLE CREATE INDEX CREATE TABLE START TRANSACTION INSERT 0 1 INSERT 0 1 INSERT 0 1 COMMIT CREATE TABLE START TRANSACTION INSERT 0 1 COMMIT $ ("path-to-kea" is the location where Kea is installed.) If instead an error is encountered, such as: :: psql: FATAL: no pg_hba.conf entry for host "[local]", user "user-name", database "database-name", SSL off ... the PostgreSQL configuration will need to be altered. Kea uses password authentication when connecting to the database and must have the appropriate entries added to PostgreSQL's pg_hba.conf file. This file is normally located in the primary data directory for the PostgreSQL server. The precise path may vary depending on the operating system and version, but the default location for PostgreSQL is ``/etc/postgresql/*/main/postgresql.conf``. However, on some systems (notably CentOS 8), the file may reside in ``/var/lib/pgsql/data``. Assuming Kea is running on the same host as PostgreSQL, adding lines similar to the following should be sufficient to provide password-authenticated access to Kea's database: :: local database-name user-name password host database-name user-name 127.0.0.1/32 password host database-name user-name ::1/128 password These edits are primarily intended as a starting point, and are not a definitive reference on PostgreSQL administration or database security. Please consult the PostgreSQL user manual before making these changes, as they may expose other databases that are running. It may be necessary to restart PostgreSQL for the changes to take effect. Initialize the PostgreSQL Database Using ``kea-admin`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If the tables were not created manually, do so now by running the ``kea-admin`` tool: .. code-block:: console $ kea-admin db-init pgsql -u database-user -p database-password -n database-name Do not do this if the tables were already created manually. ``kea-admin`` implements rudimentary checks; it will refuse to initialize a database that contains any existing tables. To start from scratch, all data must be removed manually. (This process is a manual operation on purpose, to avoid accidentally irretrievable mistakes by ``kea-admin``.) .. _pgsql-upgrade: Upgrading a PostgreSQL Database From an Earlier Version of Kea ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The PostgreSQL database schema can be upgraded using the same tool and commands as described in :ref:`mysql-upgrade`, with the exception that the "pgsql" database backend type must be used in the commands. Use the following command to check the current schema version: .. code-block:: console $ kea-admin db-version pgsql -u database-user -p database-password -n database-name Use the following command to perform an upgrade: .. code-block:: console $ kea-admin db-upgrade pgsql -u database-user -p database-password -n database-name .. _cql-database: Cassandra --------- Cassandra (sometimes referred to as CQL) is the newest backend added to Kea; initial development was contributed by Deutsche Telekom. The Cassandra backend is able to store leases, host reservations, and options defined on a per-host basis. .. note:: The Cassandra backend was deprecated in Kea 1.9.9. New users are discouraged from using Cassandra and existing users should consider a migration strategy. See :ref:`deprecated` for details. .. _cql-database-create: First-Time Creation of the Cassandra Database ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When setting up the Cassandra database for the first time, the keyspace area within it must be created. This needs to be done manually; it cannot be performed by ``kea-admin``. To create the database: 1. Export ``CQLSH_HOST`` environment variable: .. code-block:: console $ export CQLSH_HOST=localhost 2. Log into CQL: .. code-block:: console $ cqlsh cql> 3. Create the CQL keyspace: :: cql> CREATE KEYSPACE keyspace-name WITH replication = {'class' : 'SimpleStrategy','replication_factor' : 1}; (``keyspace-name`` is the name chosen for the keyspace.) 4. At this point, the database tables can be created. To do this: :: cqlsh -k keyspace-name -f path-to-kea/share/kea/scripts/cql/dhcpdb_create.cql (path-to-kea is the location where Kea is installed.) It is also possible to exit Cassandra and create the tables using the ``kea-admin`` tool. If the tables were not created in Step 4, do so now by running the ``kea-admin`` tool: .. code-block:: console $ kea-admin db-init cql -n database-name Do not do this if the tables were created in Step 4. ``kea-admin`` implements rudimentary checks; it will refuse to initialize a database that contains any existing tables. To start from scratch, all data must be removed manually. (This process is a manual operation on purpose, to avoid accidentally irretrievable mistakes by ``kea-admin``.) .. _cql-upgrade: Upgrading a Cassandra Database From an Earlier Version of Kea ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sometimes a new Kea version uses a newer database schema, so the existing database needs to be upgraded. This can be done using the ``kea-admin db-upgrade`` command. To check the current version of the database, use the following command: .. code-block:: console $ kea-admin db-version cql -n database-name (See :ref:`kea-database-version` for a discussion about versioning.) If the version does not match the minimum required for the new version of Kea (as described in the release notes), the database needs to be upgraded. Before upgrading, please make sure that the database is backed up. The upgrade process does not discard any data, but depending on the nature of the changes, it may be impossible to subsequently downgrade to an earlier version. To perform an upgrade, issue the following command: .. code-block:: console $ kea-admin db-upgrade cql -n database-name Using Read-Only Databases With Host Reservations ------------------------------------------------ If a read-only database is used for storing host reservations, Kea must be explicitly configured to operate on the database in read-only mode. Sections :ref:`read-only-database-configuration4` and :ref:`read-only-database-configuration6` describe when such a configuration may be required, and how to configure Kea to operate in this way for both DHCPv4 and DHCPv6. Limitations Related to the Use of SQL Databases ----------------------------------------------- Year 2038 Issue ~~~~~~~~~~~~~~~ The lease expiration time in Kea is stored in the SQL database for each lease as a timestamp value. Kea developers have observed that the MySQL database does not accept timestamps beyond 2147483647 seconds (the maximum signed 32-bit number) from the beginning of the UNIX epoch (00:00:00 on 1 January 1970). Some versions of PostgreSQL do accept greater values, but the value is altered when it is read back. For this reason, the lease database backends put a restriction on the maximum timestamp to be stored in the database, which is equal to the maximum signed 32-bit number. This effectively means that the current Kea version cannot store leases whose expiration time is later than 2147483647 seconds since the beginning of the epoch (around the year 2038). This will be fixed when database support for longer timestamps is available. kea-2.0.2/doc/sphinx/arm/intro.rst0000644000175000017500000000454014206773363013734 00000000000000.. _intro: ************ Introduction ************ Kea is the next generation of DHCP software, developed by Internet Systems Consortium (ISC). It supports both the DHCPv4 and DHCPv6 protocols along with their extensions, e.g. prefix delegation and dynamic updates to DNS. This guide covers Kea version |release|. For information about supported platforms see :ref:`platforms`. .. include:: platforms.rst .. _kea_software: Kea Software ============ Kea is a modular DHCP server solution. This modularity is accomplished using multiple cooperating processes which, together, provide the server functionality. The following software is included with Kea: - ``keactrl`` — This tool starts, stops, reconfigures, and reports the status of the Kea servers. - ``kea-dhcp4`` — The DHCPv4 server process. This process responds to DHCPv4 queries from clients. - ``kea-dhcp6`` — The DHCPv6 server process. This process responds to DHCPv6 queries from clients. - ``kea-dhcp-ddns`` — The DHCP Dynamic DNS process. This process acts as an intermediary between the DHCP servers and external DNS servers. It receives name update requests from the DHCP servers and sends DNS update messages to the DNS servers. - ``kea-admin`` — This is a useful tool for database backend maintenance (creating a new database, checking versions, upgrading, etc.). - ``kea-lfc`` — This process removes redundant information from the files used to provide persistent storage for the memfile database backend. While it can be run standalone, it is normally run as and when required by the Kea DHCP servers. - ``kea-ctrl-agent`` — The Kea Control Agent (CA) is a daemon that exposes a RESTful control interface for managing Kea servers. - ``kea-netconf`` - kea-netconf is an agent that provides a YANG/NETCONF interface for configuring Kea. - ``kea-shell`` — This simple text client uses the REST interface to connect to the Kea Control Agent. - ``perfdhcp`` — This is a DHCP benchmarking tool which simulates multiple clients to test both DHCPv4 and DHCPv6 server performance. The tools and modules are covered in full detail in this guide. In addition, manual pages are also provided in the default installation. Kea also provides C++ libraries and programmer interfaces for DHCP. These include detailed developer documentation and code examples. kea-2.0.2/doc/sphinx/arm/config-backend.rst0000644000175000017500000004055214206773363015436 00000000000000.. _config-backend: Kea Configuration Backend ========================= .. _cb-applicability: Applicability ------------- Kea Configuration Backend (CB or config backend) is a feature, first introduced in Kea 1.6.0, that gives Kea servers the ability to manage and fetch their configuration from one or more databases. In this documentation, the term "Configuration Backend" may also refer to the particular Kea module providing support to manage and fetch the configuration information from the particular database type. For example, MySQL Configuration Backend is the logic implemented within the ``mysql_cb`` hook library, which provides a complete set of functions to manage and fetch the configuration information from the MySQL database. In small deployments, e.g. those comprising a single DHCP server instance with limited and infrequently changing number of subnets, it may be impractical to use the CB as a configuration repository because it requires additional third-party software to be installed and configured - in particular the MySQL server and MySQL client. Once the number of DHCP servers and/or the number of managed subnets in the network grows, the usefulness of the CB becomes obvious. One use case for the CB is a pair of Kea DHCP servers that are configured to support High Availability as described in :ref:`high-availability-library`. The configurations of both servers (including the value of the ``server-tag`` parameter) are almost exactly the same: they may differ by the server identifier and designation of the server as a primary or standby (or secondary), and/or by their interfaces' configuration. Typically, the subnets, shared networks, option definitions, and global parameters are the same for both servers and can be sourced from a single database instance to both Kea servers. Using the database as a single source of configuration for subnets and/or other configuration information supported by the CB has the advantage that any modifications to the configuration in the database are automatically applied to both servers. Another case when the centralized configuration repository is useful is in deployments including a large number of DHCP servers, possibly using a common lease database to provide redundancy. New servers can be added to the pool frequently to fulfill growing scalability requirements. Adding a new server does not require replicating the entire configuration to the new server when a common database is used. Using the database as a configuration repository for Kea servers also brings other benefits, such as: - the ability to use database specific tools to access the configuration information; - the ability to create customized statistics based on the information stored in the database; and - the ability to backup the configuration information using the database's built-in replication mechanisms. .. _cb-limitations: CB Capabilities and Limitations ------------------------------- Currently, the Kea CB has the following limitations: - It is only supported for the MySQL database. - It is only supported for the DHCPv4 and DHCPv6 daemons; the Control Agent, D2 daemon, and the NETCONF daemon cannot be configured from the database, - Only certain DHCP configuration parameters can be set in the database: global parameters, option definitions, global options, client classes, shared networks, and subnets. Other configuration parameters must be sourced from a JSON configuration file. Kea CB stores data in a MySQL schema that is public. It is possible to insert configuration data into the MySQL tables manually or automatically using SQL scripts, but this requires SQL and schema knowledge. The supported method for managing the data is through the ``cb-cmds`` hook library, which provides management commands for config backends. It simplifies many typical operations, such as listing, adding, retrieving, and deleting global parameters, shared networks, subnets, pools, options, option definitions, and client classes. In addition, it provides essential business logic that ensures the logical integrity of the data. See commands starting with ``remote-`` in Appendix A of this manual for a complete list. .. note:: The ``cb_cmds`` hook library is available only to ISC support subscribers. For more information on subscription options, please complete the form at https://www.isc.org/contact. The schema creation script can be found at `dhcpdb_create.mysql `__; other related design documents are stored in our GitLab: `CB Design `__ and `Client Classes in CB Design `__. We strongly recommend against duplication of configuration information in both the file and the database. For example, when specifying subnets for the DHCP server, please store them in either the configuration backend or in the configuration file, not both. Storing some subnets in the database and others in the file may put users at risk of potential configuration conflicts. Note that the configuration instructions from the database take precedence over instructions from the file, so parts of the configuration specified in the file may be overridden if contradicted by information in the database. Although it is not recommended, it is possible to specify certain parameter types both in a configuration file and the database. For example, a subnet can be specified in the configuration file and another subnet in the database; in this case, the server will use both subnets. DHCP client classes, however, must not be specified in both the configuration file and the database, even if they do not overlap. If any client classes are specified in the database for a particular DHCP server, this server will use these classes and ignore all classes present in its configuration file. This behavior was introduced to ensure that the server receives a consistent set of client classes specified in an expected order with all inter-class dependencies fulfilled. It is impossible to guarantee consistency when client classes are specified in two independent configuration sources. .. note:: It is recommended that the ``subnet_cmds`` hook library not be used to manage subnets when the configuration backend is used as a source of information about the subnets. The ``subnet_cmds`` hook library modifies the local subnets configuration in the server's memory, not in the database. Use the ``cb_cmds`` hook library to manage the subnets information in the database instead. .. note:: Using custom option formats requires creating definitions for these options. Suppose a user wishes to set option data in the configuration backend. In that case, we recommend specifying the definition for that option in the configuration backend as well. It is essential when multiple servers are managed via the configuration backend, and may differ in their configurations. The option data parser can search for an option definition appropriate for the server for which the option data is specified. In a single-server deployment, or when all servers share the same configuration file information, it is possible to specify option definitions in the configuration files and option data in the configuration backend. The server receiving a command to set option data must have a valid definition in its configuration file, even when it sets option data for another server. It is not supported to specify option definitions in the configuration backend and the corresponding option data in the server configuration files. CB Components ------------- Kea 1.6.0 version or later is required to use the configuration backend. The ``mysql_cb`` open source hook library implementing the configuration backend for MySQL must be compiled and loaded by the DHCP servers. This hook library is compiled when the ``--with-mysql`` configuration switch is used during the Kea build. The MySQL C client libraries must be installed, as explained in :ref:`dhcp-install-configure`. .. note:: Any existing MySQL schema must be upgraded to the latest schema required by the particular Kea version using the ``kea-admin`` tool, as described in :ref:`kea-admin`. The ``cb_cmds`` premium hook library, which is available to ISC's paid support customers, provides a complete set of commands to manage the servers' configuration information within the database. This library can be attached to both DHCPv4 and DHCPv6 server instances. It is possible to manage the configuration information without the ``cb_cmds`` hook library with commonly available tools, such as MySQL Workbench or the command-line MySQL client, by directly working with the database. Refer to :ref:`cb-cmds-library` for the details regarding the ``cb_cmds`` hook library. The DHCPv4 and DHCPv6 server-specific configurations of the CB, as well as the list of supported configuration parameters, can be found in :ref:`dhcp4-cb` and :ref:`dhcp6-cb`, respectively. .. _cb-sharing: Configuration Sharing and Server Tags ------------------------------------- The configuration database is designed to store configuration information for multiple Kea servers. Depending on the use case, the entire configuration may be shared by all servers; parts of the configuration may be shared by multiple servers and the rest of the configuration may be different for these servers; or each server may have its own non-shared configuration. The configuration elements in the database are associated with the servers by "server tags." The server tag is an arbitrary string holding the name of the Kea server instance. The tags of the DHCPv4 and DHCPv6 servers are independent in the database, i.e. the same server tag can be created for both the DHCPv4 and the DHCPv6 server. The value is configured using the ``server-tag`` parameter in the Dhcp4 or Dhcp6 scope. The current server tag can be checked with the ``server-tag-get`` command. The server definition, which consists of the server tag and the server description, must be stored in the configuration database prior to creating the dedicated configuration for that server. In cases when all servers use the same configuration, e.g. a pair of servers running as High Availability peers, there is no need to configure the server tags for these servers in the database. Commands which contain the logical server `all` are applied to all servers connecting to the database. The `all` server cannot be deleted or modified, and it is not returned among other servers as a result of the ``remote-server[46]-get-all`` command. In most cases, there are no server tags defined in the configuration database; all connecting servers get the same configuration regardless of the server tag they use. The server tag that a particular Kea instance presents to the database to fetch its configuration is specified in the Kea configuration file, using the `config-control` map (please refer to the :ref:`dhcp4-cb-json` and :ref:`dhcp6-cb-json` for details). All Kea instances presenting the same server tag to the configuration database are given the same configuration. It is the administrator's choice whether multiple Kea instances use the same server tag or each Kea instance uses a different server tag. There is no requirement that the instances running on the same physical or virtual machine use the same server tag. It is even possible to configure the Kea server without assigning it a server tag. In such a case the server will be given the configuration specified for `all` servers. To differentiate between different Kea server configurations, a list of the server tags used by the servers must be stored in the database. For the DHCPv4 and DHCPv6 servers, it can be done using the commands described in :ref:`command-remote-server4-set` and :ref:`command-remote-server6-set`. The server tags can then be used to associate the configuration information with the servers. However, it is important to note that some DHCP configuration elements may be associated with multiple server tags (known as "shareable" elements), while other configuration elements may be associated with only one server tag ("non-shareable" elements). The :ref:`dhcp4-cb` and :ref:`dhcp6-cb` sections list the DHCP-specific shareable and non-shareable configuration elements; however, in this section we briefly explain the differences between them. A shareable configuration element is one which has some unique property identifying it, and which may appear only once in the database. An example of a shareable DHCP element is a subnet instance: the subnet is a part of the network topology and we assume that any particular subnet may have only one definition within this network. Each subnet has two unique identifiers: the subnet identifier and the subnet prefix. The subnet identifier is used in Kea to uniquely identify the subnet within the network and to connect it with other configuration elements, e.g. in host reservations. Some commands provided by the ``cb_cmds`` hook library allow the subnet information to be accessed by either subnet identifier or prefix, and explicitly prohibit using the server tag to access the subnet. This is because, in general, the subnet definition is associated with multiple servers rather than a single server. In fact, it may even be associated with no servers (unassigned). Still, the unassigned subnet has an identifier and prefix which can be used to access the subnet. A shareable configuration element may be associated with multiple servers, one server, or no servers. Deletion of the server which is associated with the shareable element does not cause the deletion of the shareable element. It merely deletes the association of the deleted server with the element. Unlike a shareable element, a non-shareable element must not be explicitly associated with more than one server and must not exist after the server is deleted (must not remain unassigned). A non-shareable element only exists within the context of the server. An example of a non-shareable element in DHCP is a global parameter, e.g. `renew-timer`. The renew timer is the value to be used by a particular server and only this server. Other servers may have their respective renew timers set to the same or different values. The renew timer parameter has no unique identifier by which it could be accessed, modified, or otherwise used. Global parameters like the renew timer can be accessed by the parameter name and the tag of the server for which they are configured. For example: the commands described in :ref:`command-remote-global-parameter4-get` allow the value of the global parameter to be fetched by the parameter name and the server name. Getting the global parameter only by its name (without specifying the server tag) is not possible, because there may be many global parameters with a given name in the database. When the server associated with a non-shareable configuration element is deleted, the configuration element is automatically deleted from the database along with the server because the non-shareable element must be always assigned to a server (or the logical server `all`). The terms "shareable" and "non-shareable" only apply to associations with user-defined servers; all configuration elements associated with the logical server `all` are by definition shareable. For example: the `renew-timer` associated with `all` servers is used by all servers connecting to the database which do not have their specific renew timers defined. In a special case, when none of the configuration elements are associated with user-defined servers, the entire configuration in the database is shareable because all its pieces belong to `all` servers. .. note:: Be very careful when associating configuration elements with different server tags. The configuration backend does not protect against some possible misconfigurations that may arise from the wrong server tags' assignments. For example: if a shared network is assigned to one server and the subnets belonging to this shared network to another server, the servers will fail upon trying to fetch and use this configuration. The server fetching the subnets will be aware that the subnets are associated with the shared network, but the shared network will not be found by this server since it doesn't belong to it. In such a case, both the shared network and the subnets should be assigned to the same set of servers. kea-2.0.2/doc/sphinx/conf.py0000644000175000017500000002337114206773363012572 00000000000000# -*- coding: utf-8 -*- # # Configuration file for the Sphinx documentation builder. # # This file does only contain a selection of the most common options. For a # full list see the documentation: # http://www.sphinx-doc.org/en/master/config # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os # import sys # sys.path.insert(0, os.path.abspath('.')) # to avoid "sphinx.errors.SphinxParallelError: RecursionError: maximum recursion depth exceeded while pickling an object" import sys sys.setrecursionlimit(5000) # -- Project information ----------------------------------------------------- project = 'Kea' copyright = '2019-2020, Internet Systems Consortium' author = 'Internet Systems Consortium' # get current kea version config_ac_path = '../../configure.ac' changelog_path = '../../ChangeLog' release = 'UNRELEASED' with open(config_ac_path) as f: for line in f.readlines(): if line.startswith('AC_INIT(kea'): parts = line.split(',') release = parts[1] # If the first line of the ChangeLog announces release, it means # that this is the final release. dash_parts = release.split('-') candidate_release = dash_parts[0] with open(changelog_path) as changelog_file: first_line = changelog_file.readline() if candidate_release in first_line and "released" in first_line: release = candidate_release break version = release # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.todo', 'sphinx.ext.mathjax', ] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # Additional docs messages_doc = 'kea-messages' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [ '_build', 'Thumbs.db', '.DS_Store', # included files need to be excluded to avoid duplicate labels 'arm/platforms.rst', 'arm/hooks-bootp.rst', 'arm/hooks-class-cmds.rst', 'arm/hooks-cb-cmds.rst', 'arm/config-backend.rst', 'arm/hooks-ha.rst', 'arm/hooks-host-cache.rst', 'arm/hooks-lease-cmds.rst', 'arm/hooks-lease-query.rst', 'arm/hooks-radius.rst', 'arm/hooks-run-script.rst', 'arm/hooks-stat-cmds.rst', 'arm/hammer.rst', 'arm/ext-netconf.rst', 'arm/ext-gss-tsig.rst', 'grammar/grammar-ca-parser.rst', 'grammar/grammar-d2-parser.rst', 'grammar/grammar-dhcp4-parser.rst', 'grammar/grammar-dhcp6-parser.rst', 'grammar/grammar-netconf-parser.rst', ] # The name of the Pygments (syntax highlighting) style to use. pygments_style = None # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # #html_theme = 'alabaster' html_theme = 'sphinx_rtd_theme' html_logo = 'static/kea-imageonly-100bw.png' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # #html_theme_options = { # "logo": "kea-logo-100x70.png", #} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # The default sidebars (for documents that don't match any pattern) are # defined by theme itself. Builtin themes are using these templates by # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', # 'searchbox.html']``. # # html_sidebars = {} # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. #htmlhelp_basename = 'KeaAdministratorReferenceManualdoc' # -- Options for LaTeX output ------------------------------------------------ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'kea-arm.tex', 'Kea Administrator Reference Manual Documentation', author, 'manual'), ] latex_logo = 'static/kea-logo-200.png' if os.getenv("READTHEDOCS", "False") == "False": latex_documents.append((messages_doc, 'kea-messages.tex', 'Kea Messages Manual', author, 'manual')) # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'kea-arm', 'Kea Administrator Reference Manual Documentation', [author], 1), ('man/kea-admin.8', 'kea-admin', 'Shell script for managing Kea databases', author, 8), ('man/keactrl.8', 'keactrl', 'Shell script for managing Kea', author, 8), ('man/kea-ctrl-agent.8', 'kea-ctrl-agent', 'Control Agent process in Kea', author, 8), ('man/kea-dhcp4.8', 'kea-dhcp4', 'DHCPv4 server in Kea', author, 8), ('man/kea-dhcp6.8', 'kea-dhcp6', 'DHCPv6 server in Kea', author, 8), ('man/kea-dhcp-ddns.8', 'kea-dhcp-ddns', 'DHCP-DDNS process in Kea', author, 8), ('man/kea-lfc.8', 'kea-lfc', 'Lease File Cleanup process in Kea', author, 8), ('man/kea-netconf.8', 'kea-netconf', 'NETCONF agent for configuring Kea', author, 8), ('man/kea-shell.8', 'kea-shell', 'Text client for Control Agent process', author, 8), ('man/perfdhcp.8', 'perfdhcp', 'DHCP benchmarking tool', author, 8), ] # -- Extension configuration ------------------------------------------------- # -- Options for todo extension ---------------------------------------------- # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True # Do generation of api.rst and kea-messages.rst here in conf.py instead of Makefile.am # so they are available on ReadTheDocs as there makefiles are not used for building docs. def run_generate_docs(_): import os import sys src_dir = os.path.abspath(os.path.dirname(__file__)) print(src_dir) sys.path.append(src_dir) import api2doc with open(os.path.join(src_dir, 'api-files.txt')) as af: api_files = af.read().split() api_files = [os.path.abspath(os.path.join(src_dir, '../..', af)) for af in api_files] api2doc.generate(api_files, os.path.join(src_dir, 'api.rst')) import mes2doc with open(os.path.join(src_dir, 'mes-files.txt')) as mf: mes_files = mf.read().split() mes_files = [os.path.abspath(os.path.join(src_dir, '../..', mf)) for mf in mes_files] mes2doc.generate(mes_files, os.path.join(src_dir, 'kea-messages.rst')) # Sphinx has some limitations. It can't import files from outside its directory, which # in our case is src/sphinx. On the other hand, we need to have platforms.rst file # in top level directory, so it's easily accessible by prospective and first time # users. Furthermore, ReadTheDocs does not use the makefile system at all and they rely # on sphinx-build only. As a result we need to conduct some Makefile-like operations # here. This requires us to copy (or link) the file from the top level to sphinx subdir. # # The first entry on this list is the actual file to copy, the second is a unique name # that will be used when copied over to arm/ directory. FILES_TO_COPY = [ [ '../../platforms.rst', 'platforms.rst' ], [ '../examples/template-power-user-home/info.md', 'template-power-user-home.md' ], [ '../examples/template-power-user-home/kea-ca-1.conf', 'template-power-user-home-ca-1.conf' ], [ '../examples/template-power-user-home/kea-ca-2.conf', 'template-power-user-home-ca-2.conf' ], [ '../examples/template-power-user-home/kea-dhcp4-1.conf', 'template-power-user-home-dhcp4-1.conf' ], [ '../examples/template-power-user-home/kea-dhcp4-2.conf', 'template-power-user-home-dhcp4-2.conf' ] ] from shutil import copyfile for [a, b] in FILES_TO_COPY: src = os.path.join(src_dir, a) dst = os.path.join(src_dir, 'arm', b) print("Copying %s to %s" % (src, dst)) copyfile(src, dst) # custom setup hook def setup(app): if hasattr(app, 'add_css_file'): app.add_css_file('kea.css') else: app.add_stylesheet('kea.css') app.connect('builder-inited', run_generate_docs) kea-2.0.2/doc/sphinx/_build/0000755000175000017500000000000014206773520012576 500000000000000kea-2.0.2/doc/sphinx/_build/man/0000755000175000017500000000000014206773520013351 500000000000000kea-2.0.2/doc/sphinx/_build/man/kea-netconf.80000644000175000017500000000674314206773520015566 00000000000000.\" Man page generated from reStructuredText. . .TH "KEA-NETCONF" "8" "Feb 27, 2022" "2.0.2" "Kea" .SH NAME kea-netconf \- NETCONF agent for configuring Kea . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp \fBkea\-netconf\fP [\fB\-v\fP] [\fB\-V\fP] [\fB\-W\fP] [\fB\-d\fP] [\fB\-c\fP config\-file] [\fB\-t\fP config\-file] .SH DESCRIPTION .sp The \fBkea\-netconf\fP agent provides a YANG/NETCONF interface for the Kea environment. .SH ARGUMENTS .sp The arguments are as follows: .INDENT 0.0 .TP .B \fB\-v\fP Displays the version. .TP .B \fB\-V\fP Displays the extended version. .TP .B \fB\-W\fP Displays the configuration report. .TP .B \fB\-d\fP Enables the debug mode with extra verbosity. .TP .B \fB\-c config\-file\fP Specifies the file with the configuration for the NETCONF agent. .TP .B \fB\-t config\-file\fP Checks the syntax of the configuration file and reports the first error, if any. Note that not all parameters are completely checked; in particular, service and client sockets are not opened, and hook libraries are not loaded. .UNINDENT .SH DOCUMENTATION .sp Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software \- compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at \fI\%https://kea.readthedocs.io\fP\&. .sp Kea source code is documented in the Kea Developer\(aqs Guide. Its online version is available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&. .sp The Kea project website is available at \fI\%https://kea.isc.org\fP\&. .SH MAILING LISTS AND SUPPORT .sp There are two public mailing lists available for the Kea project. \fBkea\-users\fP (kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP (kea\-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at \fI\%https://lists.isc.org\fP\&. The community provides best\-effort support on both of those lists. .sp ISC provides professional support for Kea services. See \fI\%https://www.isc.org/kea/\fP for details. .SH HISTORY .sp Early prototypes of \fBkea\-netconf\fP implementation were written during IETF Hackathons in Berlin, London, and Montreal. An actual production\-ready implementation was started in August 2018 by Tomek Mrugalski and Francis Dupont. .SH SEE ALSO .sp \fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP, \fBkea\-dhcp\-ddns(8)\fP, \fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkeactrl(8)\fP, \fBperfdhcp(8)\fP, \fBkea\-lfc(8)\fP, Kea Administrator Reference Manual. .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT 2019-2020, Internet Systems Consortium .\" Generated by docutils manpage writer. . kea-2.0.2/doc/sphinx/_build/man/kea-ctrl-agent.80000644000175000017500000000736414206773520016172 00000000000000.\" Man page generated from reStructuredText. . .TH "KEA-CTRL-AGENT" "8" "Feb 27, 2022" "2.0.2" "Kea" .SH NAME kea-ctrl-agent \- Control Agent process in Kea . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp \fBkea\-ctrl\-agent\fP [\fB\-v\fP] [\fB\-V\fP] [\fB\-W\fP] [\fB\-d\fP] [\fB\-c\fP config\-file] [\fB\-t\fP config\-file] .SH DESCRIPTION .sp The \fBkea\-ctrl\-agent\fP provides a REST service for controlling Kea services. The received HTTP requests are decapsulated and forwarded to the respective Kea services in JSON format. Received JSON responses are encapsulated within HTTP responses and returned to the controlling entity. Some commands may be handled by the Control Agent directly, and not forwarded to any Kea service. .SH ARGUMENTS .sp The arguments are as follows: .INDENT 0.0 .TP .B \fB\-v\fP Displays the version. .TP .B \fB\-V\fP Displays the extended version. .TP .B \fB\-W\fP Displays the configuration report. .TP .B \fB\-d\fP Sets the logging level to debug with extra verbosity. This is primarily for development purposes in stand\-alone mode. .TP .B \fB\-c config\-file\fP Specifies the file with the configuration for the Control Agent server. It may also contain configuration entries for other Kea services. .TP .B \fB\-t config\-file\fP Checks the syntax of the configuration file and reports the first error, if any. Note that not all parameters are completely checked; in particular, service and client sockets are not opened, and hook libraries are not loaded. .UNINDENT .SH DOCUMENTATION .sp Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software \- compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at \fI\%https://kea.readthedocs.io\fP\&. .sp Kea source code is documented in the Kea Developer\(aqs Guide. Its online version is available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&. .sp The Kea project website is available at \fI\%https://kea.isc.org\fP\&. .SH MAILING LISTS AND SUPPORT .sp There are two public mailing lists available for the Kea project. \fBkea\-users\fP (kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP (kea\-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at \fI\%https://lists.isc.org\fP\&. The community provides best\-effort support on both of those lists. .sp ISC provides professional support for Kea services. See \fI\%https://www.isc.org/kea/\fP for details. .SH HISTORY .sp The \fBkea\-ctrl\-agent\fP was first coded in December 2016 by Marcin Siodelski. .SH SEE ALSO .sp \fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP, \fBkea\-dhcp\-ddns(8)\fP, \fBkea\-admin(8)\fP, \fBkeactrl(8)\fP, \fBperfdhcp(8)\fP, \fBkea\-lfc(8)\fP, Kea Administrator Reference Manual. .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT 2019-2020, Internet Systems Consortium .\" Generated by docutils manpage writer. . kea-2.0.2/doc/sphinx/_build/man/kea-dhcp-ddns.80000644000175000017500000000735514206773520015776 00000000000000.\" Man page generated from reStructuredText. . .TH "KEA-DHCP-DDNS" "8" "Feb 27, 2022" "2.0.2" "Kea" .SH NAME kea-dhcp-ddns \- DHCP-DDNS process in Kea . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp \fBkea\-dhcp\-ddns\fP [\fB\-v\fP] [\fB\-V\fP] [\fB\-W\fP] [\fB\-d\fP] [\fB\-c\fP config\-file] [\fB\-t\fP config\-file] .SH DESCRIPTION .sp The \fBkea\-dhcp\-ddns\fP service process requests an update of DNS mapping based on DHCP lease change events. It runs as a separate process that expects to receive Name Change Requests from Kea DHCP servers. .SH ARGUMENTS .sp The arguments are as follows: .INDENT 0.0 .TP .B \fB\-v\fP Displays the version. .TP .B \fB\-V\fP Displays the extended version. .TP .B \fB\-W\fP Displays the configuration report. .TP .B \fB\-d\fP Sets the logging level to debug with extra verbosity. This is primarily for development purposes in stand\-alone mode. .TP .B \fB\-c config\-file\fP Specifies the configuration file with the configuration for the DHCP\-DDNS server. It may also contain configuration entries for other Kea services. .TP .B \fB\-t config\-file\fP Checks the syntax of the configuration file and reports the first error if any. Note that not all parameters are completely checked, in particular, service socket is not opened. .UNINDENT .SH DOCUMENTATION .sp Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software \- compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at \fI\%https://kea.readthedocs.io\fP\&. .sp Kea source code is documented in the Kea Developer\(aqs Guide. Its online version is available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&. .sp The Kea project website is available at \fI\%https://kea.isc.org\fP\&. .SH MAILING LISTS AND SUPPORT .sp There are two public mailing lists available for the Kea project. \fBkea\-users\fP (kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP (kea\-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at \fI\%https://lists.isc.org\fP\&. The community provides best\-effort support on both of those lists. .sp ISC provides professional support for Kea services. See \fI\%https://www.isc.org/kea/\fP for details. .SH HISTORY .sp The \fBb10\-dhcp\-ddns\fP process was first coded in May 2013 by Thomas Markwalder. .sp Kea became a standalone server and the BIND 10 framework was removed. The DHCP\-DDNS server binary was renamed to kea\-dhcp\-ddns in July 2014. Kea 1.0.0 was released in December 2015. .SH SEE ALSO .sp \fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP, \fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkeactrl(8)\fP, \fBperfdhcp(8)\fP, \fBkea\-netconf(8)\fP, \fBkea\-lfc(8)\fP, Kea Administrator Reference Manual. .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT 2019-2020, Internet Systems Consortium .\" Generated by docutils manpage writer. . kea-2.0.2/doc/sphinx/_build/man/keactrl.80000644000175000017500000001107614206773520015014 00000000000000.\" Man page generated from reStructuredText. . .TH "KEACTRL" "8" "Feb 27, 2022" "2.0.2" "Kea" .SH NAME keactrl \- Shell script for managing Kea . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp \fBkeactrl\fP [\fBcommand\fP] [\fB\-c\fP keactrl\-config\-file] [\fB\-s\fP server[,server,...]] [\fB\-v\fP] .SH DESCRIPTION .sp \fBkeactrl\fP is a shell script which controls the startup, shutdown, and reconfiguration of the Kea servers (\fBkea\-dhcp4\fP, \fBkea\-dhcp6\fP, \fBkea\-dhcp\-ddns\fP, \fBkea\-ctrl\-agent\fP, and \fBkea\-netconf\fP). It also provides the means for checking the current status of the servers and determining the configuration files in use. .SH CONFIGURATION FILE .sp Depending on the user\(aqs requirements, not all of the available servers need be run. The \fBkeactrl\fP configuration file specifies which servers are enabled and which are disabled. By default the configuration file is \fB[kea\-install\-dir]/etc/kea/keactrl.conf\fP\&. .sp See the Kea Administrator Reference Manual for documentation of the parameters in the \fBkeactrl\fP configuration file. .SH OPTIONS .INDENT 0.0 .TP .B \fBcommand\fP Specifies the command to be issued to the servers. It can be one of the following: .INDENT 7.0 .TP \fBstart\fP Starts the servers. .TP \fBstop\fP Stops the servers. .TP \fBreload\fP Instructs the servers to re\-read the Kea configuration file. This command is not supported by the Netconf agent. .TP \fBstatus\fP Prints the status of the servers. .UNINDENT .TP .B \fB\-c|\-\-ctrl\-config keactrl\-config\-file\fP Specifies the \fBkeactrl\fP configuration file. Without this switch, \fBkeactrl\fP attempts to use the file \fB[kea\-install\-dir]/etc/kea/keactrl.conf\fP\&. .TP .B \fB\-s|\-\-server server[,server,...]\fP Specifies a subset of the enabled servers to which the command should be issued. The list of servers should be separated by commas with no intervening spaces. Acceptable values are: .INDENT 7.0 .TP \fBdhcp4\fP DHCPv4 server (\fBkea\-dhcp4\fP). .TP \fBdhcp6\fP DHCPv6 server (\fBkea\-dhcp6\fP). .TP \fBdhcp_ddns\fP DHCP DDNS server (\fBkea\-dhcp\-ddns\fP). .TP \fBctrl_agent\fP Control Agent (\fBkea\-ctrl\-agent\fP). .TP \fBnetconf\fP Netconf agent (\fBkea\-netconf\fP). .TP \fBall\fP All servers, including Netconf if it was configured to be built. This is the default. .UNINDENT .TP .B \fB\-v|\-\-version\fP Prints the \fBkeactrl\fP version and quits. .UNINDENT .SH DOCUMENTATION .sp Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software \- compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at \fI\%https://kea.readthedocs.io\fP\&. .sp Kea source code is documented in the Kea Developer\(aqs Guide. Its online version is available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&. .sp The Kea project website is available at \fI\%https://kea.isc.org\fP\&. .SH MAILING LISTS AND SUPPORT .sp There are two public mailing lists available for the Kea project. \fBkea\-users\fP (kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP (kea\-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at \fI\%https://lists.isc.org\fP\&. The community provides best\-effort support on both of those lists. .sp ISC provides professional support for Kea services. See \fI\%https://www.isc.org/kea/\fP for details. .SH SEE ALSO .sp \fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP, \fBkea\-dhcp\-ddns(8)\fP, \fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkea\-netconf(8)\fP, \fBperfdhcp(8)\fP, \fBkea\-lfc(8)\fP, Kea Administrator Reference Manual. .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT 2019-2020, Internet Systems Consortium .\" Generated by docutils manpage writer. . kea-2.0.2/doc/sphinx/_build/man/perfdhcp.80000644000175000017500000005553514206773520015172 00000000000000.\" Man page generated from reStructuredText. . .TH "PERFDHCP" "8" "Feb 27, 2022" "2.0.2" "Kea" .SH NAME perfdhcp \- DHCP benchmarking tool . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp \fBperfdhcp\fP [\fB\-1\fP] [\fB\-4\fP | \fB\-6\fP] [\fB\-A\fP encapsulation\-level] [\fB\-b\fP base] [\fB\-B\fP] [\fB\-c\fP] [\fB\-C\fP separator] [\fB\-d\fP drop\-time] [\fB\-D\fP max\-drop] [\-e lease\-type] [\fB\-E\fP time\-offset] [\fB\-f\fP renew\-rate] [\fB\-F\fP release\-rate] [\fB\-g\fP thread\-mode] [\fB\-h\fP] [\fB\-i\fP] [\fB\-I\fP ip\-offset] [\fB\-J\fP remote\-address\-list\-file] [\fB\-l\fP local\-address|interface] [\fB\-L\fP local\-port] [\fB\-M\fP mac\-list\-file] [\fB\-n\fP num\-request] [\fB\-N\fP remote\-port] [\fB\-O\fP random\-offset] [\fB\-o\fP code,hexstring] [\fB\-p\fP test\-period] [\fB\-P\fP preload] [\fB\-r\fP rate] [\fB\-R\fP num\-clients] [\fB\-s\fP seed] [\fB\-S\fP srvid\-offset] [\fB\-\-scenario\fP name] [\fB\-t\fP report] [\fB\-T\fP template\-file] [\fB\-u\fP] [\fB\-v\fP] [\fB\-W\fP exit\-wait\-time] [\fB\-w\fP script_name] [\fB\-x\fP diagnostic\-selector] [\fB\-X\fP xid\-offset] [server] .SH DESCRIPTION .sp \fBperfdhcp\fP is a DHCP benchmarking tool. It provides a way of measuring the performance of DHCP servers by generating large amounts of traffic from simulated multiple clients. It is able to test both IPv4 and IPv6 servers, and provides statistics concerning response times and the number of requests that are dropped. .sp The tool supports scenarios, which offer certain behaviours. By default (basic scenario) tests are run using the full four\-packet exchange sequence (DORA for DHCPv4, SARR for DHCPv6). An option is provided to run tests using the initial two\-packet exchange (DO and SA) instead. It is also possible to configure \fBperfdhcp\fP to send DHCPv6 RENEW and RELEASE messages at a specified rate in parallel with the DHCPv6 four\-way exchanges. By default, if there is no response received with 1 second, a response is considered lost and perfdhcp continues with other transactions. .sp Second scenario is called avalanche, which is selected by \fB\-\-scenario avalanche\fP\&. It first sends as many Discovery or Solicit messages as request in \-R option then a retransmission (with exponential back off mechanism) is used for each simulated client until all requests are answered. It will generate report when all clients get their addresses or when it will be manually stopped. This scenario attempts to replicate a case where the server is not able to handle the traffic swiftly enough. Real clients will assume the packet or the response was lost and will retransmit, further increasing DHCP traffic. This is sometimes called avalanche effect, thus the scenario name. Option \fB\-p\fP is ignored in avalanche scenario. .sp When running a performance test, \fBperfdhcp\fP will exchange packets with the server under test as fast as possible unless the \fB\-r\fP parameter is used to limit the request rate. The length of the test can be limited by setting a threshold on any or all of the number of requests made by \fBperfdhcp\fP, the elapsed time, or the number of requests dropped by the server. .SH TEMPLATES .sp To allow the contents of packets sent to the server to be customized, \fBperfdhcp\fP allows the specification of template files that determine the contents of the packets. For example, the customized packet may contain a DHCPv6 ORO to request a set of options to be returned by the server, or it may contain the Client FQDN option to request that the server perform DNS updates. This may be used to discover performance bottlenecks for different server configurations (e.g. DDNS enabled or disabled). .sp Up to two template files can be specified on the command line, each file representing the contents of a particular type of packet, the type being determined by the test being carried out. For example, if testing DHCPv6: .INDENT 0.0 .IP \(bu 2 With no template files specified on the command line, \fBperfdhcp\fP will generate both SOLICIT and REQUEST packets. .IP \(bu 2 With one template file specified, that file will be used as the pattern for SOLICIT packets: \fBperfdhcp\fP will generate the REQUEST packets. .IP \(bu 2 With two template files given on the command line, the first will be used as the pattern for SOLICIT packets, the second as the pattern for REQUEST packets. .UNINDENT .sp (Similar determination applies to DHCPv4\(aqs DISCOVER and REQUEST packets.) .sp The template file holds the DHCP packet represented as a stream of ASCII hexadecimal digits and it excludes any IP/UDP stack headers. The template file must not contain any characters other than hexadecimal digits and spaces. Spaces are discarded when the template file is parsed; in the file, \(aq12B4\(aq is the same as \(aq12 B4\(aq which is the same as \(aq1 2 B 4\(aq. .sp The template files should be used in conjunction with the command\-line parameters which specify offsets of the data fields being modified in outbound packets. For example, the \fB\-E time\-offset\fP switch specifies the offset of the DHCPv6 Elapsed Time option in the packet template. If the offset is specified, \fBperfdhcp\fP will inject the current elapsed\-time value into this field before sending the packet to the server. .sp In many scenarios, \fBperfdhcp\fP needs to simulate multiple clients, each having a unique client identifier. Since packets for each client are generated from the same template file, it is necessary to randomize the client identifier (or HW address in DHCPv4) in the packet created from it. The \fB\-O random\-offset\fP option allows specification of the offset in the template where randomization should be performed. It is important to note that this offset points to the end (not the beginning) of the client identifier (or HW address field). The number of bytes being randomized depends on the number of simulated clients. If the number of simulated clients is between 1 and 255, only one byte (to which the randomization offset points) will be randomized. If the number of simulated clients is between 256 and 65535, two bytes will be randomized. Note that the last two bytes of the client identifier will be randomized in this case: the byte which the randomization offset parameter points to, and the one which precedes it (random\-offset \- 1). If the number of simulated clients exceeds 65535, three bytes will be randomized, and so on. .sp Perfdhcp can now simulate traffic from multiple subnets by enabling option \-J and passing path to file that contains v4 or v6 addresses that will be used as relay in generated messages. That enable testing of vast numbers of Kea shared networks. While testing Kea v4 it should be started with KEA_TEST_SEND_RESPONSES_TO_SOURCE environment variable to force Kea to send generated messages to source address of incoming packet. .sp Templates may currently be used to generate packets being sent to the server in 4\-way exchanges, i.e. SOLICIT, REQUEST (DHCPv6) and DISCOVER, REQUEST (DHCPv4). They cannot be used when RENEW or RELEASE packets are being sent. .SH OPTIONS .INDENT 0.0 .TP .B \fB\-1\fP Takes the server\-ID option from the first received message. .TP .B \fB\-4\fP Establishes DHCPv4 operation; this is the default. It is incompatible with the \fB\-6\fP option. .TP .B \fB\-6\fP Establishes DHCPv6 operation. This is incompatible with the \fB\-4\fP option. .TP .B \fB\-b basetype=value\fP Indicates the base MAC or DUID used to simulate different clients. The basetype may be "mac" or "duid". (The keyword "ether" may alternatively used for MAC.) The \fB\-b\fP option can be specified multiple times. The MAC address must consist of six octets separated by single (:) or double (::) colons, for example: mac=00:0c:01:02:03:04. The DUID value is a hexadecimal string; it must be at least six octets long and not longer than 64 bytes, and the length must be less than 128 hexadecimal digits, for example: duid=0101010101010101010110111F14. .TP .B \fB\-d drop\-time\fP Specifies the time after which a request is treated as having been lost. The value is given in seconds and may contain a fractional component. The default is 1 second. .TP .B \fB\-e lease\-type\fP Specifies the type of lease being requested from the server. It may be one of the following: .INDENT 7.0 .TP \fBaddress\-only\fP Only regular addresses (v4 or v6) will be requested. .TP \fBprefix\-only\fP Only IPv6 prefixes will be requested. .TP \fBaddress\-and\-prefix\fP Both IPv6 addresses and prefixes will be requested. .UNINDENT .sp The \fB\-e prefix\-only\fP and \fB\-e address\-and\-prefix\fP forms may not be used with the \fB\-4\fP option. .TP .B \fB\-F release\-rate\fP Specifies the rate at which RELEASE requests are sent to a server. This value is only valid when used in conjunction with the exchange rate (given by \fB\-r rate\fP). Furthermore, the sum of this value and the renew\-rate (given by \fB\-f rate\fP) must be equal to or less than the exchange rate value. .TP .B \fB\-f renew\-rate\fP Specifies the rate at which DHCPv4 or DHCPv6 renew requests are sent to a server. This value is only valid when used in conjunction with the exchange rate (given by \fB\-r rate\fP). Furthermore, the sum of this value and the release\-rate (given by \fB\-F rate\fP) must be equal to or less than the exchange rate. .TP .B \fB\-g thread\-mode\fP Allows selection of thread\-mode, which can be either \(aqsingle\(aq or \(aqmulti\(aq. In multi\-thread mode packets are received in a separate thread, which allows better utilisation of CPUs. In a single\-CPU system it is better to run in one thread to avoid threads blocking each other. If more than one CPU is present in the system, multi\-thread mode is the default; otherwise single\-thread is the default. .TP .B \fB\-h\fP Prints help and exits. .TP .B \fB\-i\fP Performs only the initial part of the exchange: DISCOVER\-OFFER if \fB\-4\fP is selected, SOLICIT\-ADVERTISE if \fB\-6\fP is chosen. .sp \fB\-i\fP is incompatible with the following options: \fB\-1\fP, \fB\-d\fP, \fB\-D\fP, \fB\-E\fP, \fB\-S\fP, \fB\-I\fP and \fB\-F\fP\&. In addition, it cannot be used with multiple instances of \fB\-O\fP, \fB\-T\fP and \fB\-X\fP\&. .TP .B \fB\-J remote\-address\-list\-file\fP Text file that include multiple addresses. If provided perfdhcp will choose randomly one of addresses for each exchange. This is used to generate traffic from multiple subnets. Designed to test shared\-networks. While testing kea v4 it should be started with KEA_TEST_SEND_RESPONSES_TO_SOURCE=ENABLE env variable otherwise perfdhcp will not be able to receive responses. .TP .B \fB\-l local\-addr|interface\fP For DHCPv4 operation, specifies the local hostname/address to use when communicating with the server. By default, the interface address through which traffic would normally be routed to the server is used. For DHCPv6 operation, specifies the name of the network interface through which exchanges are initiated. .TP .B \fB\-L local\-port\fP Specifies the local port to use. This must be zero or a positive integer up to 65535. A value of 0 (the default) allows \fBperfdhcp\fP to choose its own port. .TP .B \fB\-M mac\-list\-file\fP Specifies a text file containing a list of MAC addresses, one per line. If provided, a MAC address will be chosen randomly from this list for every new exchange. In DHCPv6, MAC addresses are used to generate DUID\-LLs. This parameter must not be used in conjunction with the \-b parameter. .TP .B \fB\-N remote\-port\fP Specifies the remote port to use. This must be zero or a positive integer up to 65535. A value of 0 (the default) allows \fBperfdhcp\fP to choose the standard service port. .TP .B \fB\-o code,hexstring\fP Forces \fBperfdhcp\fP to insert the specified extra option (or options if used several times) into packets being transmitted. The code specifies the option code and the hexstring is a hexadecimal string that defines the content of the option. Care should be taken as \fBperfdhcp\fP does not offer any kind of logic behind those options; they are simply inserted into packets and sent as is. Be careful not to duplicate options that are already inserted. For example, to insert client class identifier (option code 60) with a string \(aqdocsis\(aq, use \-o 60,646f63736973. The \fB\-o\fP may be used multiple times. It is necessary to specify the protocol family (either \fB\-4\fP or \fB\-6\fP) before using \fB\-o\fP\&. .TP .B \fB\-P preload\fP Initiates preload exchanges back\-to\-back at startup. Must be 0 (the default) or a positive integer. .TP .B \fB\-r rate\fP Initiates the rate of DORA/SARR (or if \fB\-i\fP is given, DO/SA) exchanges per second. A periodic report is generated showing the number of exchanges which were not completed, as well as the average response latency. The program continues until interrupted, at which point a final report is generated. .TP .B \fB\-R num\-clients\fP Specifies how many different clients are used. With a value of 1 (the default), all requests seem to come from the same client. Must be a positive number. .TP .B \fB\-s seed\fP Specifies the seed for randomization, making runs of \fBperfdhcp\fP repeatable. This must be 0 or a positive integer. The value 0 means that a seed is not used; this is the default. .TP .B \fB\-\-scenario name\fP Specifies type of the scenario, can be \fBbasic\fP (default) or \fBavalanche\fP\&. .TP .B \fB\-T template\-file\fP Specifies a file containing the template to use as a stream of hexadecimal digits. This may be specified up to two times and controls the contents of the packets sent (see the "Templates" section above). .TP .B \fB\-u\fP Enable checking address uniqueness. Lease valid lifetime should not be shorter than test duration and clients should not request address more than once without releasing it first. .TP .B \fB\-v\fP Prints the version of this program. .TP .B \fB\-W exit\-wait\-time\fP Specifies the exit\-wait\-time parameter, which causes \fBperfdhcp\fP to wait for exit\-wait\-time after an exit condition has been met, to receive all packets without sending any new packets. Expressed in microseconds. If not specified, 0 is used (i.e. exit immediately after exit conditions are met). .TP .B \fB\-w script_name\fP Specifies the name of the script to be run before/after \fBperfdhcp\fP\&. When called, the script is passed a single parameter, either "start" or "stop", indicating whether it is being called before or after \fBperfdhcp\fP\&. .TP .B \fB\-x diagnostic\-selector\fP Includes extended diagnostics in the output. This is a string of single keywords specifying the operations for which verbose output is desired. The selector key letters are: .INDENT 7.0 .TP \fBa\fP Prints the decoded command line arguments. .TP \fBe\fP Prints the exit reason. .TP \fBi\fP Prints the rate processing details. .TP \fBl\fP Prints the received leases. .TP \fBs\fP Prints the first server\-ID. .TP \fBt\fP When finished, prints timers of all successful exchanges. .TP \fBT\fP When finished, prints templates. .UNINDENT .TP .B \fB\-y seconds\fP Time in seconds after which perfdhcp will start simulating the client waiting longer for server responses. This increase the secs field in DHCPv4 and sends increased values in Elapsed option in DHCPv6. Must be used with \(aq\-Y\(aq. .TP .B \fB\-Y seconds\fP Period of time in seconds in which perfdhcp will be simulating the client waiting longer for server responses. This increase the secs field in DHCPv4 and sends increased values in Elapsed option in DHCPv6. Must be used with \(aq\-y\(aq. .UNINDENT .SH DHCPV4-ONLY OPTIONS .sp The following options only apply for DHCPv4 (i.e. when \fB\-4\fP is given). .INDENT 0.0 .TP .B \fB\-B\fP Forces broadcast handling. .UNINDENT .SH DHCPV6-ONLY OPTIONS .sp The following options only apply for DHCPv6 (i.e. when \fB\-6\fP is given). .INDENT 0.0 .TP .B \fB\-c\fP Adds a rapid\-commit option (exchanges will be SOLICIT\-ADVERTISE). .TP .B \fB\-A encapsulation\-level\fP Specifies that relayed traffic must be generated. The argument specifies the level of encapsulation, i.e. how many relay agents are simulated. Currently the only supported encapsulation\-level value is 1, which means that the generated traffic is equivalent to the amount of traffic passing through a single relay agent. .UNINDENT .SH TEMPLATE-RELATED OPTIONS .sp The following options may only be used in conjunction with \fB\-T\fP and control how \fBperfdhcp\fP modifies the template. The options may be specified multiple times on the command line; each occurrence affects the corresponding template file (see "Templates" above). .INDENT 0.0 .TP .B \fB\-E time\-offset\fP Specifies the offset of the secs field (DHCPv4) or elapsed\-time option (DHCPv6) in the second (i.e. REQUEST) template; must be 0 or a positive integer. A value of 0 disables this. .TP .B \fB\-I ip\-offset\fP Specifies the offset of the IP address (DHCPv4) in the requested\-IP option or IA_NA option (DHCPv6) in the second (REQUEST) template. .TP .B \fB\-O random\-offset\fP Specifies the offset of the last octet to randomize in the template. This must be an integer greater than 3. The \fB\-T\fP switch must be given to use this option. .TP .B \fB\-S srvid\-offset\fP Specifies the offset of the server\-ID option in the second (REQUEST) template. This must be a positive integer, and the switch can only be used when the template option (\fB\-T\fP) is also given. .TP .B \fB\-X xid\-offset\fP Specifies the offset of the transaction ID (xid) in the template. This must be a positive integer, and the switch can only be used when the template option (\fB\-T\fP) is also given. .UNINDENT .SH OPTIONS CONTROLLING A TEST .INDENT 0.0 .TP .B \fB\-D max\-drop\fP Aborts the test immediately if \fBmax\-drop\fP requests have been dropped. Use \fB\-D 0\fP to abort if even a single request has been dropped. \fBmax\-drop\fP must be a positive integer. If \fBmax\-drop\fP includes the suffix \(aq%\(aq, it specifies a maximum percentage of requests that may be dropped before abort. In this case, testing of the threshold begins after 10 requests have been expected to be received. .TP .B \fB\-n num\-requests\fP Initiates \fBnum\-request\fP transactions. No report is generated until all transactions have been initiated/waited\-for, after which a report is generated and the program terminates. .TP .B \fB\-p test\-period\fP Sends requests for \fBtest\-period\fP, which is specified in the same manner as \fB\-d\fP\&. This can be used as an alternative to \fB\-n\fP or both options can be given, in which case the testing is completed when either limit is reached. .TP .B \fB\-t interval\fP Sets the delay (in seconds) between two successive reports. .TP .B \fB\-C separator\fP Output reduced, an argument is a separator for periodic (\-t) reports generated in easy parsable mode. Data output won\(aqt be changed, remain identical as in \-t option. .UNINDENT .SH ARGUMENTS .INDENT 0.0 .TP .B server Indicates the server to test, specified as an IP address. In the DHCPv6 case, the special name \(aqall\(aq can be used to refer to All_DHCP_Relay_Agents_and_Servers (the multicast address FF02::1:2), or the special name \(aqservers\(aq to refer to All_DHCP_Servers (the multicast address FF05::1:3). The server is mandatory except where the \fB\-l\fP option is given to specify an interface, in which case it defaults to \(aqall\(aq. .UNINDENT .SH ERRORS .sp \fBperfdhcp\fP can report the following errors in the packet exchange: .INDENT 0.0 .TP .B tooshort A message was received that was too short. .TP .B orphans A message was received which does not match one sent to the server (i.e. it is a duplicate message, a message that has arrived after an excessive delay, or one that is just not recognized). .TP .B locallimit Local system limits have been reached when sending a message. .UNINDENT .SH EXIT STATUS .sp \fBperfdhcp\fP can exit with one of the following status codes: .INDENT 0.0 .TP .B 0 Success. .TP .B 1 General error. .TP .B 2 Error in command\-line arguments. .TP .B 3 No general failures in operation, but one or more exchanges were unsuccessful. .UNINDENT .SH USAGE EXAMPLES .sp Simulate regular DHCPv4 traffic: 100 DHCPv4 devices (\-R 100), 10 packets per second (\-r 10), show the query/response rate details (\-xi), the report should be shown every 2 seconds (\-t 2), send the packets to the IP 192.0.2.1: .sp sudo perfdhcp \-xi \-t 2 \-r 10 \-R 100 192.0.2.1 .sp Here\(aqs a similar case, but for DHCPv6. Note that DHCPv6 protocol uses link\-local addresses, so you need to specify the interface (eth0 in this example) to send the traffic. \(aqall\(aq is a convenience alias for All_DHCP_Relay_Agents_and_Servers (the multicast address FF02::1:2). Alternatively, you can use \(aqservers\(aq alias to refer to All_DHCP_Servers (the multicast address FF05::1:3), or skip it all together and the default value (all) will be used. .sp sudo perfdhcp \-6 \-xi \-t 1 \-r 1 \-R 10 \-l eth0 all .sp The following examples simulate normal DHCPv4 and DHCPv6 traffic that after 3 seconds starts pretending to not receive any responses from the server for 10 seconds. DHCPv4 protocol signals this by increased secs field and DHCPv6 uses elapsed option for that. In real networks this indicates that the clients are not getting responses in a timely matter. This can be used to simulate some HA scenarios, as Kea uses secs field and elapsed option value as one of the indicators that the HA partner is not responding. When enabled with \-y and \-Y, the secs and elapsed time value increased steadily. .sp sudo perfdhcp \-xi \-t 1 \-r 1 \-y 10 \-Y 3 192.0.2.1 .sp sudo perfdhcp \-6 \-xi \-t 1 \-r 1 \-y 10 \-Y 3 2001:db8::1 .SH MAILING LISTS AND SUPPORT .sp There are two public mailing lists available for the Kea project. \fBkea\-users\fP (kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP (kea\-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at \fI\%https://lists.isc.org\fP\&. The community provides best\-effort support on both of those lists. .sp ISC provides professional support for Kea services. See \fI\%https://www.isc.org/kea/\fP for details. .SH HISTORY .sp The \fBperfdhcp\fP tool was initially coded in October 2011 by John DuBois, Francis Dupont, and Marcin Siodelski of ISC. Kea 1.0.0, which included \fBperfdhcp\fP, was released in December 2015. .SH SEE ALSO .sp \fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP, \fBkea\-dhcp\-ddns(8)\fP, \fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkea\-netconf(8)\fP, \fBkeactrl(8)\fP, \fBkea\-lfc(8)\fP, Kea Administrator Reference Manual. .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT 2019-2020, Internet Systems Consortium .\" Generated by docutils manpage writer. . kea-2.0.2/doc/sphinx/_build/man/kea-lfc.80000644000175000017500000001272714206773520014675 00000000000000.\" Man page generated from reStructuredText. . .TH "KEA-LFC" "8" "Feb 27, 2022" "2.0.2" "Kea" .SH NAME kea-lfc \- Lease File Cleanup process in Kea . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp \fBkea\-lfc\fP [\fB\-4**|\fP\-6**] [\fB\-c\fP config\-file] [\fB\-p\fP pid\-file] [\fB\-x\fP previous\-file] [\fB\-i\fP copy\-file] [\fB\-o\fP output\-file] [\fB\-f\fP finish\-file] [\fB\-v\fP] [\fB\-V\fP] [\fB\-W\fP] [\fB\-d\fP] [\fB\-h\fP] .SH DESCRIPTION .sp The \fBkea\-lfc\fP service process removes redundant information from the files used to provide persistent storage for the memfile database backend. The service is written to run as a stand\-alone process. While it can be started externally, there is usually no need to do this. It is run periodically by the Kea DHCP servers. .SH ARGUMENTS .sp The arguments are as follows: .INDENT 0.0 .TP .B \fB\-4 | \-6\fP Indicates the protocol version of the lease files; must be either 4 or 6. .TP .B \fB\-c config\-file\fP Specifies the file with the configuration for the \fBkea\-lfc\fP process. It may also contain configuration entries for other Kea services. Currently \fBkea\-lfc\fP gets all of its arguments from the command line; in the future it will be extended to obtain some arguments from the configuration file. .TP .B \fB\-p pid\-file\fP Specifies the PID file. When the \fBkea\-lfc\fP process starts, it attempts to determine if another instance of the process is already running by examining the PID file. If one is already running, the new process is terminated. If one is not running, Kea writes its PID into the PID file. .TP .B \fB\-x previous\-file\fP Specifies the previous or ex\-lease file. When \fBkea\-lfc\fP starts, this is the result of any previous run of \fBkea\-lfc\fP; when \fBkea\-lfc\fP finishes, it is the result of this run. If \fBkea\-lfc\fP is interrupted before completing, this file may not exist. .TP .B \fB\-i copy\-file\fP Specifies the input or copy of lease file. Before the DHCP server invokes \fBkea\-lfc\fP, it will move the current lease file here and then call \fBkea\-lfc\fP with this file. .TP .B \fB\-o output\-file\fP Specifies the output lease file, which is the temporary file \fBkea\-lfc\fP should use to write the leases. Once this file is finished writing, it is moved to the finish file (see below). .TP .B \fB\-f finish\-file\fP Specifies the finish or completion file, another temporary file \fBkea\-lfc\fP uses for bookkeeping. When \fBkea\-lfc\fP finishes writing the output file, it moves it to this file name. After \fBkea\-lfc\fP finishes deleting the other files (previous and input), it moves this file to the previous lease file. By moving the files in this fashion, the \fBkea\-lfc\fP and the DHCP server processes can determine the correct file to use even if one of the processes was interrupted before completing its task. .TP .B \fB\-v\fP Causes the version stamp to be printed. .TP .B \fB\-V\fP Causes a longer form of the version stamp to be printed. .TP .B \fB\-W\fP Displays the configuration report. .TP .B \fB\-d\fP Sets the logging level to debug with extra verbosity. This is primarily for development purposes in stand\-alone mode. .TP .B \fB\-h\fP Causes the usage string to be printed. .UNINDENT .SH DOCUMENTATION .sp Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software \- compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at \fI\%https://kea.readthedocs.io\fP\&. .sp Kea source code is documented in the Kea Developer\(aqs Guide. Its online version is available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&. .sp The Kea project website is available at \fI\%https://kea.isc.org\fP\&. .SH MAILING LISTS AND SUPPORT .sp There are two public mailing lists available for the Kea project. \fBkea\-users\fP (kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP (kea\-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at \fI\%https://lists.isc.org\fP\&. The community provides best\-effort support on both of those lists. .sp ISC provides professional support for Kea services. See \fI\%https://www.isc.org/kea/\fP for details. .SH HISTORY .sp The \fBkea\-lfc\fP process was first coded in January 2015 by the ISC Kea/DHCP team. .SH SEE ALSO .sp \fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP, \fBkea\-dhcp\-ddns(8)\fP, \fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkeactrl(8)\fP, \fBperfdhcp(8)\fP, \fBkea\-netconf(8)\fP, Kea Administrator Reference Manual. .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT 2019-2020, Internet Systems Consortium .\" Generated by docutils manpage writer. . kea-2.0.2/doc/sphinx/_build/man/kea-shell.80000644000175000017500000001172114206773520015231 00000000000000.\" Man page generated from reStructuredText. . .TH "KEA-SHELL" "8" "Feb 27, 2022" "2.0.2" "Kea" .SH NAME kea-shell \- Text client for Control Agent process . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp \fBkea\-shell\fP [\fB\-h\fP] [\fB\-v\fP] [\fB\-\-host\fP] [\fB\-\-port\fP] [\fB\-\-path\fP] [\fB\-\-ca\fP] [\fB\-\-cert\fP] [\fB\-\-key\fP] [\fB\-\-auth\-user\fP] [\fB\-\-auth\-password\fP] [\fB\-\-timeout\fP] [\fB\-\-service\fP] [command] .SH DESCRIPTION .sp The \fBkea\-shell\fP provides a REST client for the Kea Control Agent (CA). It takes command as a command\-line parameter that is being sent to CA with proper JSON encapsulation. Optional arguments may be specified on the standard input. The request is sent via HTTP and a response is retrieved, displayed on the standard output. Basic HTTP authentication and HTTPS i.e. TLS transport are supported. .SH ARGUMENTS .sp The arguments are as follows: .INDENT 0.0 .TP .B \fB\-h\fP Displays help regarding command\-line parameters. .TP .B \fB\-v\fP Displays the version. .TP .B \fB\-\-host\fP Specifies the host to connect to. Control Agent must be running at the specified host. If not specified, 127.0.0.1 is used. .TP .B \fB\-\-port\fP Specifies the TCP port to connect to. Control Agent must be listening at the specified port. If not specified, 8000 is used. .TP .B \fB\-\-path\fP Specifies the path in the URL to connect to. If not specified, an empty path is used. As Control Agent listens at the empty path, this parameter is useful only with a reverse proxy. .TP .B \fB\-\-ca\fP Specifies the file or directory name of the Certification Authority. If not specified HTTPS is not used. .TP .B \fB\-\-cert\fP Specifies the file name of the user end\-entity public key certificate. If specified the file name of the user key must be specified too. .TP .B \fB\-\-key\fP Specifies the file name of the user key file. If specified the file name of the user certificate must be specified too. Note that encrypted key files are not supported. .TP .B \fB\-\-auth\-user\fP Specifies the user id for basic HTTP authentication. If not specified or specified as the empty string authentication is not used. .TP .B \fB\-\-auth\-password\fP Specifies the password for basic HTTP authentication. If not specified but the user id is specified an empty password is used. .TP .B \fB\-\-timeout\fP Specifies the connection timeout in seconds. If not specified, 10 (seconds) is used. .TP .B \fB\-\-service\fP Specifies the service that is the target of a command. If not specified, Control Agent will be targeted. May be used more than once to specify multiple targets. .TP .B \fBcommand\fP Specifies the command to be sent to CA. If not specified, "list\-commands" is used. .UNINDENT .SH DOCUMENTATION .sp Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software \- compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at \fI\%https://kea.readthedocs.io\fP\&. .sp Kea source code is documented in the Kea Developer\(aqs Guide. Its online version is available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&. .sp The Kea project website is available at \fI\%https://kea.isc.org\fP\&. .SH MAILING LISTS AND SUPPORT .sp There are two public mailing lists available for the Kea project. \fBkea\-users\fP (kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP (kea\-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at \fI\%https://lists.isc.org\fP\&. The community provides best\-effort support on both of those lists. .sp ISC provides professional support for Kea services. See \fI\%https://www.isc.org/kea/\fP for details. .SH HISTORY .sp The \fBkea\-shell\fP was first coded in March 2017 by Tomek Mrugalski. .SH SEE ALSO .sp \fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP, \fBkea\-dhcp\-ddns(8)\fP, \fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkeactrl(8)\fP, \fBperfdhcp(8)\fP, \fBkea\-lfc(8)\fP, Kea Administrator Reference Manual. .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT 2019-2020, Internet Systems Consortium .\" Generated by docutils manpage writer. . kea-2.0.2/doc/sphinx/_build/man/kea-admin.80000644000175000017500000001312314206773520015210 00000000000000.\" Man page generated from reStructuredText. . .TH "KEA-ADMIN" "8" "Feb 27, 2022" "2.0.2" "Kea" .SH NAME kea-admin \- Shell script for managing Kea databases . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp \fBkea\-admin\fP [command] [backend] [\fB\-h\fP database_host] [\fB\-P\fP database_port] [\fB\-u\fP database_username] [\fB\-p\fP [database_password]] [\fB\-n\fP database_name] [\fB\-d\fP script_directory] [\fB\-v\fP] [\fB\-4\fP | \fB\-6\fP] [\fB\-o\fP output_file] .SH DESCRIPTION .sp \fBkea\-admin\fP is a shell script which offers database maintenance. In particular, it features database initialization, database version checking, and database schema upgrade. .SH ARGUMENTS .INDENT 0.0 .TP .B \fBcommand\fP Specifies the command to be issued to the servers. It can be one of the following: .INDENT 7.0 .TP \fBdb\-init\fP Initializes a new database schema. This is useful during a new Kea installation. The database is initialized to the latest version supported by the version of the software being installed. .TP \fBdb\-version\fP Reports the database backend version number. This is not necessarily equal to the Kea version number as each backend has its own versioning scheme. .TP \fBdb\-upgrade\fP Conducts a database schema upgrade. This is useful when upgrading Kea. .TP \fBlease\-dump\fP Dumps the contents of the lease database (for MySQL, PostgreSQL, or CQL backends) to a CSV (comma\-separated values) text file. The first line of the file contains the column names. This is meant to be used as a diagnostic tool, so it provides a portable, human\-readable form of the lease data. .TP \fBstats\-recount\fP Recounts lease statistics for MySQL or PostgreSQL database. .UNINDENT .TP .B \fBbackend\fP Specifies the backend type. Currently allowed backends are: memfile, mysql, pgsql, and cql. .TP .B \fB\-h|\-\-host hostname\fP Specifies the hostname when connecting to a database. If not specified, the default value of \fBlocalhost\fP is used. .TP .B \fB\-P|\-\-port port\fP Specifies the port when connecting to a database. If not specified, the default value chosen by the database client is used. .TP .B \fB\-u|\-\-user username\fP Specifies the username when connecting to a database. If not specified, the default value of \fBkeatest\fP is used. .TP .B \fB\-p|\-\-password password\fP Specifies the password when connecting to a database. If only \fB\-p\fP or \fB\-\-password\fP is given, the user is prompted for a password. If not specified at all, the \fBKEA_ADMIN_DB_PASSWORD\fP environment variable is checked for a value and used if it exists. Otherwise the default value of \fBkeatest\fP is used. .TP .B \fB\-n|\-\-name database\-name\fP Specifies the name of the database to connect to. If not specified, the default value of \fBkeatest\fP is used. .TP .B \fB\-d|\-\-directory script\-directory\fP Specifies the override scripts directory. That script is used during upgrades, database initialization, and possibly other operations. If not specified, the default value of \fB(prefix)/share/kea/scripts/\fP is used. .TP .B \fB\-o|\-\-output output_file\fP Specifies the file to which the lease data will be dumped. Required for lease\-dump. .TP .B \fB\-v|\-\-version\fP Prints the \fBkea\-admin\fP version and quits. .TP .B \fB\-4\fP Directs \fBkea\-admin\fP to lease\-dump the DHCPv4 leases. Incompatible with the \-6 option. .TP .B \fB\-6\fP Directs \fBkea\-admin\fP to lease\-dump the DHCPv6 leases. Incompatible with the \-4 option. .UNINDENT .SH DOCUMENTATION .sp Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software \- compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at \fI\%https://kea.readthedocs.io\fP\&. .sp Kea source code is documented in the Kea Developer\(aqs Guide. Its online version is available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&. .sp The Kea project website is available at \fI\%https://kea.isc.org\fP\&. .SH MAILING LISTS AND SUPPORT .sp There are two public mailing lists available for the Kea project. \fBkea\-users\fP (kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP (kea\-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at \fI\%https://lists.isc.org\fP\&. The community provides best\-effort support on both of those lists. .sp ISC provides professional support for Kea services. See \fI\%https://www.isc.org/kea/\fP for details. .SH SEE ALSO .sp \fBkea\-dhcp4(8)\fP, \fBkea\-dhcp6(8)\fP, \fBkea\-dhcp\-ddns(8)\fP, \fBkea\-ctrl\-agent(8)\fP, \fBkeactrl(8)\fP, \fBperfdhcp(8)\fP, \fBkea\-netconf(8)\fP, Kea Administrator Reference Manual. .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT 2019-2020, Internet Systems Consortium .\" Generated by docutils manpage writer. . kea-2.0.2/doc/sphinx/_build/man/kea-dhcp4.80000644000175000017500000000765314206773520015135 00000000000000.\" Man page generated from reStructuredText. . .TH "KEA-DHCP4" "8" "Feb 27, 2022" "2.0.2" "Kea" .SH NAME kea-dhcp4 \- DHCPv4 server in Kea . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp \fBkea\-dhcp4\fP [\fB\-v\fP] [\fB\-V\fP] [\fB\-W\fP] [\fB\-d\fP] [\fB\-c\fP config\-file] [\fB\-t\fP config\-file] [\fB\-p\fP server\-port\-number] [\fB\-P\fP client\-port\-number] .SH DESCRIPTION .sp The \fBkea\-dhcp4\fP daemon provides the DHCPv4 server implementation. .SH ARGUMENTS .sp The arguments are as follows: .INDENT 0.0 .TP .B \fB\-v\fP Displays the version. .TP .B \fB\-V\fP Displays the extended version. .TP .B \fB\-W\fP Displays the configuration report. .TP .B \fB\-d\fP Enables the debug mode with extra verbosity. .TP .B \fB\-c config\-file\fP Specifies the configuration file with the configuration for the DHCPv4 server. It may also contain configuration entries for other Kea services. .TP .B \fB\-t config\-file\fP Checks the configuration file and reports the first error, if any. Note that not all parameters are completely checked; in particular, service and control channel sockets are not opened, and hook libraries are not loaded. .TP .B \fB\-p server\-port\-number\fP Specifies the server port number (1\-65535) on which the server listens. This is useful for testing purposes only. .TP .B \fB\-P client\-port\-number\fP Specifies the client port number (1\-65535) to which the server responds. This is useful for testing purposes only. .UNINDENT .SH DOCUMENTATION .sp Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software \- compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at \fI\%https://kea.readthedocs.io\fP\&. .sp Kea source code is documented in the Kea Developer\(aqs Guide. Its online version is available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&. .sp The Kea project website is available at \fI\%https://kea.isc.org\fP\&. .SH MAILING LISTS AND SUPPORT .sp There are two public mailing lists available for the Kea project. \fBkea\-users\fP (kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP (kea\-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at \fI\%https://lists.isc.org\fP\&. The community provides best\-effort support on both of those lists. .sp ISC provides professional support for Kea services. See \fI\%https://www.isc.org/kea/\fP for details. .SH HISTORY .sp The \fBb10\-dhcp4\fP daemon was first coded in November 2011 by Tomek Mrugalski. .sp In mid\-2014, Kea was decoupled from the BIND 10 framework and became a standalone DHCP server. The DHCPv4 server binary was renamed to kea\-dhcp4. Kea 1.0.0 was released in December 2015. .SH SEE ALSO .sp \fBkea\-dhcp6(8)\fP, \fBkea\-dhcp\-ddns(8)\fP, \fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkeactrl(8)\fP, \fBperfdhcp(8)\fP, \fBkea\-netconf(8)\fP, \fBkea\-lfc(8)\fP, Kea Administrator Reference Manual. .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT 2019-2020, Internet Systems Consortium .\" Generated by docutils manpage writer. . kea-2.0.2/doc/sphinx/_build/man/kea-dhcp6.80000644000175000017500000000756114206773520015135 00000000000000.\" Man page generated from reStructuredText. . .TH "KEA-DHCP6" "8" "Feb 27, 2022" "2.0.2" "Kea" .SH NAME kea-dhcp6 \- DHCPv6 server in Kea . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp \fBkea\-dhcp6\fP [\fB\-v\fP] [\fB\-V\fP] [\fB\-W\fP] [\fB\-d\fP] [\fB\-c\fP config\-file] [\fB\-t\fP config\-file] [\fB\-p\fP server\-port\-number] [\fB\-P\fP client\-port\-number] .SH DESCRIPTION .sp The \fBkea\-dhcp6\fP daemon provides the DHCPv6 server implementation. .SH ARGUMENTS .sp The arguments are as follows: .INDENT 0.0 .TP .B \fB\-v\fP Displays the version. .TP .B \fB\-V\fP Displays the extended version. .TP .B \fB\-W\fP Displays the configuration report. .TP .B \fB\-d\fP Enables the debug mode with extra verbosity. .TP .B \fB\-c config\-file\fP Specifies the configuration file with the configuration for the DHCPv6 server. It may also contain configuration entries for other Kea services. .TP .B \fB\-t config\-file\fP Checks the configuration file and reports the first error, if any. Note that not all parameters are completely checked; in particular, service and control channel sockets are not opened, and hook libraries are not loaded. .TP .B \fB\-p server\-port\-number\fP Specifies the server port number (1\-65535) on which the server listens. This is useful for testing purposes only. .TP .B \fB\-P client\-port\-number\fP Specifies the client port number (1\-65535) to which the server responds. This is useful for testing purposes only. .UNINDENT .SH DOCUMENTATION .sp Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software \- compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at \fI\%https://kea.readthedocs.io\fP\&. .sp Kea source code is documented in the Kea Developer\(aqs Guide. Its online version is available at \fI\%https://reports.kea.isc.org/dev_guide/\fP\&. .sp The Kea project website is available at \fI\%https://kea.isc.org\fP\&. .SH MAILING LISTS AND SUPPORT .sp There are two public mailing lists available for the Kea project. \fBkea\-users\fP (kea\-users at lists.isc.org) is intended for Kea users, while \fBkea\-dev\fP (kea\-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at \fI\%https://lists.isc.org\fP\&. The community provides best\-effort support on both of those lists. .sp ISC provides professional support for Kea services. See \fI\%https://www.isc.org/kea/\fP for details. .SH HISTORY .sp The \fBb10\-dhcp6\fP daemon was first coded in June 2011 by Tomek Mrugalski. .sp Kea became a standalone server and the BIND 10 framework was removed. The DHCPv6 server binary was renamed to kea\-dhcp6 in July 2014. .SH SEE ALSO .sp \fBkea\-dhcp4(8)\fP, \fBkea\-dhcp\-ddns(8)\fP, \fBkea\-ctrl\-agent(8)\fP, \fBkea\-admin(8)\fP, \fBkeactrl(8)\fP, \fBperfdhcp(8)\fP, \fBkea\-netconf(8)\fP, \fBkea\-lfc(8)\fP, Kea Administrator Reference Manual. .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT 2019-2020, Internet Systems Consortium .\" Generated by docutils manpage writer. . kea-2.0.2/doc/sphinx/index.rst0000644000175000017500000000237714206773363013137 00000000000000.. only:: not latex .. image:: static/kea-logo-200.png :align: right .. _introduction: ################################## Kea Administrator Reference Manual ################################## Kea is an open source implementation of the Dynamic Host Configuration Protocol (DHCP) servers, developed and maintained by Internet Systems Consortium (ISC). This is the reference guide for Kea version |release|. Links to the most up-to-date version of this document (in PDF, HTML, and plain text formats) can be found on `Read the Docs `_. Other useful Kea information can be found in our `Knowledgebase `_. .. toctree:: :numbered: :maxdepth: 5 arm/intro arm/quickstart arm/install arm/admin arm/config arm/keactrl arm/agent arm/dhcp4-srv arm/dhcp6-srv arm/database-connectivity arm/lease-expiration arm/congestion-handling arm/ddns arm/lfc arm/classify arm/hooks arm/stats arm/ctrl-channel arm/logging arm/shell arm/integrations arm/stork arm/security .. toctree:: :caption: Appendices :name: appendices :maxdepth: 1 api manpages kea-messages arm/config-templates.rst umls grammar/grammar arm/acknowledgments kea-2.0.2/doc/sphinx/static/0000755000175000017500000000000014206773520012627 500000000000000kea-2.0.2/doc/sphinx/static/kea-logo-200.png0000644000175000017500000004455314206773363015272 00000000000000PNG  IHDR9:tEXtSoftwareAdobe ImageReadyqe<iTXtXML:com.adobe.xmp kea-logo-cmyk E IDATx]$U>yr͉ٝeaHRr%Ss@QP1@1!f"PQ( ,.\Ugk:TU:wg+ܺu{=G T6jᶐ7r{t : v;mf/s;HV>)}ǝU#=.@jzHj-Ahcc =jלmzH ; ΅V# r- Gx ?BTѤWO\bwaEIPùWB7EynDm҃]T}'BVn=FaJyF0J*{ =LsqHe,Q\S {nɱA${Pi,D}}™dȄ ($ ,"}c~-]]p 9e%n=x eI[$t^V^zܮ$n_*z$z^nZ_ #9SwL\JHwh(q/[ ǁ#i{G}_t7T]&Z`.b! ,RS%U51 pA]n/pHNsn8u/I~a̭Yq 8UjvD:E)|]1(yܞ"="`u3{ B?H`ЄpD=}kl&}xl`H"FԒโtG2S􀈈̉@~M;"f3> M"m&ZW*<="ݛ\ -ϋ"l`7d+mHdvsdE_& `!~ S _-_6cd0qsE,ZuO:&]TXQâ/_2@7Z<ҶʤVMÄY.cHsknϗD|B yT}qduAG'Q9Z&ޱY~#M$|I@aw!rۛ,2d~\nA`nq,> [ڦ*jgkx 'F ¯QA'E#fADHH^vE0.)W/φ$)pJzCq;[GAfA+̼%1A)1mjM@J{S i(fop~c- ߎ!#178\f>.)2;²ăȊw F탗n^Zy6xyEɲ&'HѸB1*vCk7{Jӳ3`cYeopv l~ġz olp׋`d9V8 m1Yk+M<s;S I}k9$-zLJ΍J#Dk"*gPR!%2ʢkD[): `-F!)A B%8KVC*"1|Ej@472[澬Ui^ܑO3{"cC" UtOQ*ax&"ԉǵAD $8"%fY%EqE9qr?;fTE֥fV:"b'슶0)X:uf}1P@Y,IK'sI_i"BXd&<<9&E!%=Ԭ׈x,U%pl_5NZ 5Ǽb(lWbR1pVbB; F$I#"01TJc)rgYRh7?2$.H+"9egҔ%ѱVL=txA9$ YȜN38 V<fp,epI88(tj'3V[X캋[]ThqLa?8,mIPĄݠm<:]4-@ȥP'"^f#!/=F}B@v@CȪ42ݘ/*Jo"'ar~td+E⺕@75@@1 &f߀q7IhRo t*V?5Q>n5s1j 4Ӏv,hTOytԖ[Eyˆ%X$bWu8qG3cN@: M-m'ìW+S!^69=s,CXަF$O>VTF}~)v^(jfڹm6Ҭ61Gi yuA#QBmQ:P`Yybs]R=,+3ݔ˃+pǦcY/b(Kdf_@jb>*"ةώZbVE i-,w>Bx1N,{2S[֦{-Sq<'hVu0537b@1h4F[[WfZ f{FWV|3$UiK 'DfOʄ,.D|8.Q[;?Q¼SaS4o(mmΞ+0,'P.po"}.Sgb y/jib0Zq0"qUEF5!ׯ2KI$>GcXAi#GY!ul]^!&H E㴾ݦ{"޺E~iz4g.t^nA wƯ4w4}>W)/\b ñOޱY)ʜL/"J6ޒnMG[HCObTdD UMюT!OH7137\ 0nk20" fY"T`76bs*p!0=Π,U&K&:$)KWE)7s\$po"qD*rɹUYsa&r s &]#=@z (*RH; Z`53b*>@'=oP?K.m  }"4v-S Eu&x&/O~lr.`\tY0J+X(g Ə=e "cPn;#Kߎ R/y7^Q1lbLzT?Xڂ=3(gI U)4Yr/(ڛxo@; Ѵqq%h1^ok/;o )gJ\r[k FQ2eLD =Ū +i)*K{ɑm -VXVi ^#}x{.":HOE"mR18~Ӟ=x KE4.ֶ#,qQ1{vi7WYbz@Ab#i͸4  }u FzɤH5G(B{*4CeZH0 6!>6 <JDoP7II a?'UKA@TYijlKFlS@:@zE-, DR8h&r`R|[ %i ϑOtQp 5MBoSa/*(4U]jK|>ו_#]Б,1@d_ snjc5uÌł}Qj SQtP5٩҆'`m08j0p+SR9oB!4*0o"v͘,RQS2AMJ ЍIi5҈J}-ziƂ>Zj&Չh"40AôMDdjFIǤ,.JQvng'8:x5U$e>/+| 17=LN,C; (:QPT#"UB֦};/@^TfCA `2I6e6"dx.Kݡ+c#Y^5[8f/jŠ$+a <,C)upVO'#84d++fQo q.yEFgtSf>LN~˦;; lcWǣ*nouy;'(1'QtO&VM8 i>K&Hp5H`(sF EI( M/0֪] -ֹ h% G_ LOvBNk\c?iFoi߆>N)N'cjeP`dR& Ѣ{-KHjo- @p`VY%m&V]k[dB+o^5Е#0RtMe n6GⴧSPJj8 ׾Eh'5đE"}sm}n 挥vcl-l!b='pP"L>r Vj E[q8^gߝ\e+٠ѫ%YW)j GeXy(Jh7rւX6L{,S zŒɟȘqS_EH崴H\ k@}t,LJ;`yQƦ=gt>  gJ=4<(sGtDÄ?Y?CS"" PfY* VJ[%&8ƲVTmoI%)myGst^̕XLjy&Kq,;^M㺗-;a"_ e Hx\˛e XRME"y+[W I+ UgHz(A>⽱ə@~^48ijjFO?8A{k o9e+w?&^PbI k+jUldsLê0ݗI$r{4dIDvs\jB{IRgL¤ԝ9E-8vBo)t\Vtj ;+bE=P&=Ea/Z[rAI8>ɽ)K5ҲGC2V[ )pZ<[c{keSa(\љ"5ÍU wEe+-|jihf*5+늹>bW"*NM1c6p O0ژ_1@LxR*Qmi , Y1 m&S$,z(ך{dcbdL$ۡ^s483/02p=g5+#I'4M~/Gqw$,C%,dž_1 5* ,;M[zxݙլ't=κ(YYԝ`¬Ӥ,D³;R " L@eKĨ!1]bcսB$M=Jm$E;"jn5vLYM@M&= {j>M4|ݢ8 S{_q=H 9U`E-h ȣ\̣7J^=HhMD/<1!?6rT@b'O`Fd[ͤ~r%FфS\IhQv{|`[1#s&KH!ln%m5Fmj5!!#ɣڴ;ШX?LrwI_&"^\޲8Ϳ䥵iyUe.qWhzӲ`ŸD&ʊ[fK$~Fn=z6Y5kYZ>+Is:406|ؼ55ZBҼRA߿R( dtttM !f;c*mW$?nOO2O=zo{ڀ!=Xk.}E+9#dU[;$)_QWq屚 !U sZPbz.*5^ff +iJ 74ūhڄ>̤osiG,p<%]œJ2;%X^LMq.A6Zjd*9!{G7ilpȐ\ `nf1ElJeo}N$PMvp r{Yw $A,H*5)ߦDg?-2}mN=O7{Po6^ {w~W]18IBSxӦ7ړ>F8E҆$2+53WDr "`{;'M{n8md/c0W&N*pܣ)'Yͥ*M5ix٘+hj!f[O@ϕep ő K1$W8?LOOҬ(j$*!eN!j-Vl7N@@9i3m1%## C%H[q,g Md?yqm]ҜD t%:{X})+ D5moL㳈Ct.XN=ʔ 2|XW8AJfyɿ/C^^^AU@EWC٭J6մS,3=؂Ebi?!0/˽OGg2,'"~HS+谛g.XbXyIR N] hԫ@Y +%781vzW0p =M`NQ]ܜZSo]- * JNd!e@&>Oz8\EHkDO^ϝ߃lwbdR AOpd+ըP@c1Ŕ漖+#8joNSQ( RIjK߮)]d I ڋE4˗ǁ %5E&/3t#nZimrE*~HH"* DU:qJ)p \٠uyQi 9(.)9? L;"poX3H6 T*\ TH $Xv J]L+V*EzނH":oWZy4V,nX;*U;iQ䧨B+و*wQRŹ%T5;wy)SY/e1`! ȣ ů>dLNh%%<lmR+D p@UL坪<;&dկ|l|U"̰xt+ @GX*2nr p+҆4w %sOW IWa?o>>(^uzfEto$mhPM6d9wOˊCؗnN+ mBH*qltr?³m]T㊦Vɦ 3]8B ل'IO}MPtwsZGWi_j^\#np ?QPYsSiSCgL/OЛ[IcqXX4~!}ѽY/nu`E䋫Jk39^ "=TCT~uφQ=遢T<߳ S:j2Vծt8w+I- 'I]ũ \#E{taXO5F_?Yjm™)ZT j3hMy&xhC[+!eʺ@WII*;@pLjJkdeV3|й>LHc2A fb:tv$Z uVĻvi<] ~a9DRM`&jmPO U$E@|<5.pL>MdpL3<ԙ$ciGN/qB\6x:z }nj֬ 9R9JL 䩶U'A5Hg2kHA]nmo) -r qKolt%!5'gT\+'(w&-jI;{;Jl{_/:--";9ՌN)4HbYin:ԅd- j$e$= 5==M<) } amtT(b<'tnMHڢʞ'=xN3%"ͱA$Sq9c{p,+B"M,?gg9o<=!ڸW5GDrLum[SVShׄJϰz`ZXj!Z$u4OGh}1Ye 2m(35q?2H'䡈 SU|g!~ͣJ`wBQuJqd$~$waR, PJWabv}SdVPgvwha=I*z4̥:jH'C[- aɬiN|d(̔JSъR飍O@=V[IR\>8Hi9IQ8]꼬Ũ$AA}-)ҳw.QoSWT ~@4ဗxe`΂DZcBq8 Lm$['oΙV}BDL2rf+7e{yEٳ^|`/mXAǜ9 K 93KMspsF; @jpxjy?mKOQ%-",,!5s:h%@!v6YZqjb(za?ViV.CrU0N0F*4zAU9I@Ga$YFzDE_43pvpUk$qD7?56^Up eDu2 qxX d{tʬ|,ϚHq*Ŷ~2 )mM_e}q _~,H~DVږsU-8Ro1< fL 7́^`I0裶(Ev|Tq':9Ig}qhL=}~;]\ۤ|W~fH 2@FqI*`Bm~QjHH[ $mhJl4JREÄ #[ / 4>;ij*V FN2NUO9C T5VJ<d++pgwS+! JrN$;kӼ&硽g$i4♬>bf@m5_qa0 4)a"I79[=^EnqG؋hs\*!X;D 6PŮ-jUMfʻA?|aE{s_SV^Pq7Ģ4U;c,JZ9O ,FRe2Qse}sE;UU!S?&05(\63SY\Z3/ͬlGy1F<[,Ĵ ; -hq_\A$& ` L~t`V|V9JG3vD:Mc4]Z<`{ZxMlBj (HHee,ҥ0Eo@O X#% c]4jרP\.'8?qPx#H٠hE/'NW:?$v85@P5a-#=k*@9YQ(}nwNINbsk A2fsNuV`f;>[FՒէ+x)IN:U44j[I&jqhaQ{pz83 @}#|` "@rbPٳAϵqjЙGlC Z\?3JM ŏj u$hIQk`IN1s<]_ wfrZFY3 Zhd"⾨/[ZWkbK+[Zí?I/ řSE<&'8)IUDgе5GZU>foc$W$b)@@.}kyx^$^Lc^jiH0wHRKS'M65y у[}񸪳%^>ꡣ=tIj p Ԃ40jLh~Ƙ&iek c 9Kk-m+sX]bUyὖKmjSŲyHD/h!s;g(DB`"⭚mNGx=7^f(MK^u[U .itm>0> &4wFh34eǒ]4ro)@@ S`)@M3P,p3Q@Ӵ:2Ί7^wqヤG @-]^d__VEiJEWpׇ(r43;轼S`vz4$D<+3QIkD> ^02 unT`%+3a,PA ՏSo5Tp\^}(uKi;TWA2Kl(w"+AgRNR,8hǸ=o&:!8̫=q&,2>gazҽ$v%kȾE &wXJn mDnkdbx 0f_-:dϚtkwus9T:_,c@& e!9Ѝ j?p[R[$h7]j=){]SnOlԭe}9eӡؙ|X!b]N6^&"7UNps/ŭ.rp|tC."/x߇dK'Pi>sEwTub=<ҫ_#(,c~GL"]n`,];Y4]9⠢e&161< :LJKN`yU!!دC %g'sL ƫ//ӻ\:X +nx7_;P$Zt 2Fz9cp;NaNv\(޸l.=vΫV8+o@(wJcw('=@Cвjd8l)Tx 2 WE ի$98gyeḭT9ѣ4jUu NcVBTspcK[Q9Y83`? k*O[88oi>jZ#?W'8F y ND܎j"]i8x/p_a󖉽J5d$=wW{[_,7HmU$W:8gJ^ rv!@Xi\OMFp;:J,Av*tsx^=XNR+i8U4Nت jC$JbaoFAOi `Mnp}ER"^J,'y !p1CrȈxr7VJ8R(`1a#>%yaZN@,Y([ _")zq%aj lc&ŪQ_ 3H-XNXwQG N `4sTq9U.д┮v?՟*(n%wR(0M\of`d?'$2~TYOzb#^d XdX4_nsaڎט] ELoqjQCUiA&Y "d1rOwHC֖u dY܊ߩ\C4Yuy@'p$"ӓ$H<=3r\DhE@p'xԃT"yS 28v0L&S^a*UšS ᰿3 Agh^pN}d[NTUz?:ݳn*0+8zf8_%@yg7w-$3|w 8rYqx:nOqx7% 28-*H8I9hCE}e!.@ȹbڗGztp>u&Aheu sb 񏀮'gGнi)@Fpxf r:"vu F\8B#ksbP@PV gTe?W9Ntb T{(QP罭 w1y.@ B#)V}NjD:k8M _I⸚^U~뤚짫hl =uv>r2k G)UW%vh^li'8ZHJT*?r>HO^a o;8Vp3@@GHRX>I^TCFBx8< %"sI*f~B `R}#g%=Z9}YV-^V=_ D(/\ !P,r>Kwj\C'g~3mvsafϋȂ=B2?S5`}:E6ѵ%gѕ`XṰhH""@RJn!YDٿybG?o,]bB/!p93a TZ@?q?Dz"+UI ePQB;ҍXXRx F^X 8a(sM(Eoq<חa+Ff=O)zNsg02'^FzbCoC[ V+zqHz?`) 0:Dmxot`)oT>G`6xr)YY7!"wU1 \ rU,[Mpowv,,BL,C  8vOh GBNrZпF? (DGp>$[It'畣ͰLDuEEx7^[P?GjnEcE]s7x ]"%"2lYrg*Q8L/r0&(6zpM#Ҫy#"l*<,#1#vEb (?BzQ[} i^%s?U2*T \NKZ<;>/+=ydA>F *Zl VI9DIENDB`kea-2.0.2/doc/sphinx/static/kea.css0000644000175000017500000000054114206773363014026 00000000000000/* give more screen width to the content as by default it is too narrow and many tables and boxes are squeezed */ .wy-nav-content { max-width: 1100px; } /* by default table content is not wrapped and then the tables are pretty wide so removing that */ .wy-table-responsive table td, .wy-table-responsive table th { white-space: normal; } kea-2.0.2/doc/sphinx/static/kea-imageonly-100bw.png0000644000175000017500000002253014206773363016635 00000000000000PNG  IHDRdh U/zTXtRaw profile type exifxQ+ EYE$$r,?cU|Pnh \ ` ԳJ*\*WK1*6a'=® x?NBt^Eo;-$|i'TsnHFS!G܅$'_QH$QC_$R>/̷a_'{_E&t+{iI_rwO=Lj\.>ojt^m))k:>ZPs!x)D* =D;;Zeٲ8>4+ > ,XN -e ύ0 b`\ëIh͉by B|b9Օ_ W_+ + $ޒY M:/m Ea{Qtf'B3TDΒҠ%'78|q8VJ -c ՠIUM]&LmSœg/^5[sɵp\c/R+TU1p!G:Ï|>g:|6npf[nN[ݺK{mHC yQh'jmj@qMuq"I')%&XXB:4@0ubtg Gn+raDgn_Pku]S? rҷ݉o[-z M/~$wiCCPICC profilex}=H@_S"v鐡v *U(BP+`r4iHR\ׂUg]\AIEJ_Zhq?{ܽFiV8鶙N&lnU "CDf1'I)x{zYjbO$eioOo}0+*9I$~7E63y0XbYԈSmyV= y}e4#HbK BA eT`#NN4'<#_"B29PnD+)z_cͺ|;N?Wz_m3;ZmM.w'C6eW ) k8}2U88bE^xw_woif|rVo uiTXtXML:com.adobe.xmp kea1 bKGDY  pHYs.#.#x?vtIME&; HIDATxwǿ ,,R) (i؂&vbE {JKb`@] MCWEP.?ymf޾]|>Ǜ3=sOs+@S h Z;z@mPl??k`PJ*0z@o' bk%4m` dcpYV6Gyd\&r`*ӱh1 jxFQ;02tl.#^6oE #ž/T%Tmc)Ĉ߷AFǟfFjdV1=_8E2뀕rӼ^ qe`f`ކ>{ )K>1>Я >[,13hX~m cJ* V/&D= و #V_-q?boW|&2{)gȤZ /)*'TQR&F}m?_)#nOkB]6f HNYLPQX)&ƈ\_6*jzJ(A,r._#7fQ4Oqs^JY, ppI8*ZǢTS]}"/b_*k`e=1qV*,_jР^C"׋(CBr=e"g2_?PsBl0'i+蠈(]d ΋<;]a{x2XR7 {6Sdֲbv3qOY\ȹow*&XPF0bLg4*y`92r.nvcC`6\7r0YUjY]q>|[r=j]m( c-%7@ bMNwLaKBõKY(lTYC;|j᥾%"F\K$(@&op2,^94*~:6v "B]v|0aiB|>azf>o8&1L~6vI$K&AEn&cxxR(b6C\-6wDT Ua>g5#.$<} ۧ9/>S{)GYO2{mYKy,Uh#2q=ɢjw~/!b^*?seOPp5y<;A]" LlNȄ7D6db˯s4MQ\__=+;(W.t {ĀCf04 t:<:Uv}*J&& ;A:cXne'yo.x:h|]@g/(}rp!ZFdLKu?hY@GxW@q˷#W6T&ыחMlNo"m\t2[ +Z1!u)Gg::>}1PUq f;oHl%/gu|ډtS^pE3ku 6#N*ֽ$=gB66-(k ef}UBYG7&`iE4(`UC m-p^w\;$!ӊl.̨cYܛZ?! =!Zsm&C$v3CJqc͘l,])w#ǽBOYJ~*+αZjYσ.R+k #ȼ3(<cOҮ@r_)+[>#fBg?q\; ް,]-4<_kTj#J~UI쑡-R1G‚cˌF_N0ZȒНbI&^!\<,&cQ霝:>2+Icl^dJ\ii[0ԡ)VE!*j *OxqXHQqC`//x:m͌oOKt\*uYRf&Ŕlh 6!c4c}>X#RfPIʐlyMN5}ߑF$ϙQeyOz#Mg9-OdRM*n', ?J&eDyCTQVa0+6T3G\1'̐mɦ[/,6K58ȭ}b)6]SK%CjȖ2)CIiAl@!7Qtr^4OM`@IJPIp!Jx(8[ }3d{c )EULbaL|q*MIGČxG^p&bjyfK*ޠR?\ *D5(`:ʂK-̭e'>dEJi*ͨ'"%MVbHzF]$8 R!?*^yojm_Үw1%d_yϕTTcWb9v <71Ɵ ()Ŀ M^2=:b|!WV>`K+d+veI6lfM,1HٷR, M-8eCM۳vQ#өiuLkWs潞1HޛQEKن*O |TLt溽NfbY4l6JQFD ]L=bS$Cfb1KX-hS6A4dOGmg"AۦB5ѵ S*H7RjĐTDcr,'b}Z~mI+_h?R1A%7)Cۤ(Qdt7x}Jӣ$QXo fTۧ1wTjLk嗚#RTQCqIENDB`kea-2.0.2/doc/sphinx/static/kea-logo-100x70.png0000644000175000017500000002337214206773363015624 00000000000000PNG  IHDRdF:1iiCCPICC Profile8UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic.3e&DCBBUS[yP V l_v]^UPAժ,jkVk[ $PPpaK2If2s=ν3 z 3{sG??U%g!!)JȾR^J"TR6 )FZ):k}yћfķ99yGR'efGY0FF*%K_"dJ/1JJ rPU %ָ[<'Ys>@*{(˽0ɐYHO'㟧?$a)CM)4Gj֙Đ&AA:dTheA BN!@)JEKG!.rZ7Cx?_X[ݶ/-%W:muxr4E,oTS0Tڅ hͯ4 /y5DݘKtvf;كG0.߁ yLQS$']GGt?d)#l wZ^V+Q̅]x:Zg<:ruhz/zeHCrh!-\@@I`TkL5:RRG%TkzF6yAzT'㲏.Wa&amb$&Zү+ӼKP)+*%0ԆuW!ѶG.0nЪLJǰ Ky)~';CMtDlR\^Cbo&j;89~UiMp*C=W~9q]gR^Ijzeq~xܳ-YaZ̋&=(#ժա\ёA\!]i8?>l> z*5] 0HS~ Dhbύc"7{M)'v 0Z&jK} uyA,j^p& ~Wa)Ġ͜6ڼd9gYnq+uA&vMJ@Tv5H9X$\9n'XvJz^E[Oo6HNBdtK=K\;m0f{ )%"5!_g10:LC^ҽqaM.fꄋD(Y(?Sŗ -Vypv³ n*<[ 7oP;{ 5 R+tx&}p2i7'Ss Ka$Zd / M+컄m~DDPGL$ 6i-NW9*zNPCy}>*G.D\rz ڙ8F8 ݤvuf/M.[nD 7a->CD׮2 K( cqЖ _j\T;4 1w"gmc`_1xgE B$\Jybf[kT0T23bvޏQsvͫrGTP9Io2tq|!x4נs9Ұ18\~ AxDX՛<El((\w `NM,Jz1]UxP\w0Xis^ &/)畦-Yz˜7t0Di[1R;p_%;?Q.M.Z%a)1 +{z[=,|=ʾ y&6,/\G]UFy~~Cei!u雌ढ़heH\(Cʸmqn샃mΔºGD9gD0ࢢrhB *x`ZsA83BVQXV gBCbZB -f/'ȷAݝùs"5ʲQ$Ҵheh L(BՀ} /lC_!Fgf QMB3AQ՘y_# 3Tx.TJ0\x3jD)HHcSyjkccf_VWy-"1B/),`>X7.8y<]Mʸb}>'%}m<ҀU ^{%Niߋ{}g$r@g%@>e4 l*19&LbN-74G ~>3pH@e-0l362:@p!?lzMX6& W0f'X`1JmlegBt~]a݋cb:W5[GZȟ=ğkv1I\1^Mmec*2G6 \l8-`\$[K)L*CMyN^Z}nD3j[ +y[vX(d%ʟAF"E/8GuΓ'A# 6A92WRWnj*owB!e© ^Q#|^SV*`Tԋ܅k3Mɤ".P+w'ěo/&PdUQn,@U'z 2yQh "ܦt AהވS%홵mP;b}ۇc(xsoIG+pHNBZ"靁2k0ڪv'].{Ӑu4"yX\$΃Z`@E]ߥ=e>?jjnJ+ DVP(X|UwC{`TuJB!܃a+NkDUyHTǀWZo|e_Ҿ @-^HaQը!;V0O;!=uR0 Vψd~`:@ 8@ #Dk$kRs';?_IQKa*nPm^3NN8[8f9Asw^=k ^*m3}{&֟f%>v+65P\b?o+u/ kUU"² Z[\ɰ2jM: xcp& ~J8?UV 2>Ɓm&ڞ!E1g-&lZe#M""M,w* "ƥ^Y4@zhA si9Igt^5Z:_x_A  [46\d-u+ya\}zvi:YI37R9?-+2]F֢)b㝟PH=Zjs@n'!GbA)!H{s\\IJڎM'Sm87˿ 4@lx R5~b_ ;HSR U-lqBprSnޡtȁwU^e2 ͩ= U"ؒѨ2`X—2uxֲ)I (}x rxƶȡD^qJqu[nON_nS+!S{:Aԇ+8)mPvxuA26e H/&cNc^>Ji^[: NP!)\OPpZ+f7Tf2Z™٤xgxp41-)?3T6p/>A{8Ѥ{ łApQ凾Q_F}r7WO7s5AT1 (h2`)|̒g#b0d)xu02D74n*G5 Rpw_!8z~M'2} "O6<RKo9FBg١HƅŒ`l0⅕?yxRl$/.  #w޸f;~**>:L/4cAkNJ 00%Nb08tƤX<߂,i03C6ZIM"'JG p wpEPƸ<L69oҵ44{`X̬N⏸cƝ68 nZxaLQXϝ m*?$c4cfbP -%4rO=bO)t.{QK} 9/xŽ7ѾN9[1R j҉O>ᆯˠǻοmyM8Wt2TmH] MAz~icc_; As#/d ޛ Jz;z,e,XGRj1Z6iNZDJH!Hx|ˡP>tk ?LSx%Ct>GξuZb kq_%[ꘆ!>׏(CNRӪW< :?tK`؉ LQWxCyr,ԝx2M`pG#}5{v`ID6.Ły#!S!m@`>lnƥtdxx/|.y5< ǩZ:őP*㾉`/nzƵN:Zpo[|,*v1}b2'H "2; \g}l%w#E'`DyF0? ԗ|xz,K/OSzcJ:ǃ7|/0ci3n& <Ήsn}>E, 2**&S0ԁp ;ck-&)~ m~j.OF:1 |)_;.^b H0/z8!@QlS4SBTW\X>#AmSINxw,]iL]ʝ+ɪ=+D([ڀg/ vTld6(\P< :OH/ `Ue ynfvH/,jӶ|Rb拊}%^n' /p5a,uuA^~u@B7@;>r2?]@Jc7T&j0m8vAc?!<ο#i MjҫSn.Z%s2ϓ!u#ByX.2+խNhX 2Ww*L$JMs5RJtJ퀝1C!l,#AځWB )s{ iH;*ǑA։mFސ%b1뒆Ε!pr㶾3=掻ƌk;S!fnaFn!O0rc[eqoG4p.w_Wo~}>2Hݎ!݀|:'*F<o^ O8gWPЯR=UkF빊mkƊ99W1($kJF5Sm9j7vYӍxzʮknmZ}.(Wk؎/a] 1O:t qq2A9pT\/Ӎ۞h7pC{ rŶ/ 4Y'v"vGz[2 u+C_8UX~IENDB`kea-2.0.2/doc/sphinx/static/static_sources.mk0000644000175000017500000000025114206773363016135 00000000000000static_sources += static/kea-imageonly-100bw.png static_sources += static/kea-logo-100x70.png static_sources += static/kea-logo-200.png static_sources += static/kea.css kea-2.0.2/doc/sphinx/api-files.txt0000644000175000017500000001547114206773511013702 00000000000000src/share/api/build-report.json src/share/api/cache-clear.json src/share/api/cache-flush.json src/share/api/cache-get-by-id.json src/share/api/cache-get.json src/share/api/cache-insert.json src/share/api/cache-load.json src/share/api/cache-remove.json src/share/api/cache-size.json src/share/api/cache-write.json src/share/api/class-add.json src/share/api/class-del.json src/share/api/class-get.json src/share/api/class-list.json src/share/api/class-update.json src/share/api/config-backend-pull.json src/share/api/config-get.json src/share/api/config-reload.json src/share/api/config-set.json src/share/api/config-test.json src/share/api/config-write.json src/share/api/dhcp-disable.json src/share/api/dhcp-enable.json src/share/api/gss-tsig-get-all.json src/share/api/gss-tsig-get.json src/share/api/gss-tsig-key-del.json src/share/api/gss-tsig-key-expire.json src/share/api/gss-tsig-key-get.json src/share/api/gss-tsig-list.json src/share/api/gss-tsig-purge-all.json src/share/api/gss-tsig-purge.json src/share/api/ha-continue.json src/share/api/ha-heartbeat.json src/share/api/ha-maintenance-cancel.json src/share/api/ha-maintenance-notify.json src/share/api/ha-maintenance-start.json src/share/api/ha-reset.json src/share/api/ha-scopes.json src/share/api/ha-sync.json src/share/api/ha-sync-complete-notify.json src/share/api/lease4-add.json src/share/api/lease4-del.json src/share/api/lease4-get-all.json src/share/api/lease4-get-by-client-id.json src/share/api/lease4-get-by-hostname.json src/share/api/lease4-get-by-hw-address.json src/share/api/lease4-get-page.json src/share/api/lease4-get.json src/share/api/lease4-resend-ddns.json src/share/api/lease4-update.json src/share/api/lease4-wipe.json src/share/api/lease6-add.json src/share/api/lease6-bulk-apply.json src/share/api/lease6-del.json src/share/api/lease6-get-all.json src/share/api/lease6-get-by-duid.json src/share/api/lease6-get-by-hostname.json src/share/api/lease6-get-page.json src/share/api/lease6-get.json src/share/api/lease6-resend-ddns.json src/share/api/lease6-update.json src/share/api/lease6-wipe.json src/share/api/leases-reclaim.json src/share/api/libreload.json src/share/api/list-commands.json src/share/api/network4-add.json src/share/api/network4-del.json src/share/api/network4-get.json src/share/api/network4-list.json src/share/api/network4-subnet-add.json src/share/api/network4-subnet-del.json src/share/api/network6-add.json src/share/api/network6-del.json src/share/api/network6-get.json src/share/api/network6-list.json src/share/api/network6-subnet-add.json src/share/api/network6-subnet-del.json src/share/api/remote-class4-del.json src/share/api/remote-class4-get-all.json src/share/api/remote-class4-get.json src/share/api/remote-class4-set.json src/share/api/remote-class6-del.json src/share/api/remote-class6-get-all.json src/share/api/remote-class6-get.json src/share/api/remote-class6-set.json src/share/api/remote-global-parameter4-del.json src/share/api/remote-global-parameter4-get-all.json src/share/api/remote-global-parameter4-get.json src/share/api/remote-global-parameter4-set.json src/share/api/remote-global-parameter6-del.json src/share/api/remote-global-parameter6-get-all.json src/share/api/remote-global-parameter6-get.json src/share/api/remote-global-parameter6-set.json src/share/api/remote-network4-del.json src/share/api/remote-network4-get.json src/share/api/remote-network4-list.json src/share/api/remote-network4-set.json src/share/api/remote-network6-del.json src/share/api/remote-network6-get.json src/share/api/remote-network6-list.json src/share/api/remote-network6-set.json src/share/api/remote-option-def4-del.json src/share/api/remote-option-def4-get-all.json src/share/api/remote-option-def4-get.json src/share/api/remote-option-def4-set.json src/share/api/remote-option-def6-del.json src/share/api/remote-option-def6-get-all.json src/share/api/remote-option-def6-get.json src/share/api/remote-option-def6-set.json src/share/api/remote-option4-global-del.json src/share/api/remote-option4-global-get-all.json src/share/api/remote-option4-global-get.json src/share/api/remote-option4-global-set.json src/share/api/remote-option4-network-del.json src/share/api/remote-option4-network-set.json src/share/api/remote-option4-pool-del.json src/share/api/remote-option4-pool-set.json src/share/api/remote-option4-subnet-del.json src/share/api/remote-option4-subnet-set.json src/share/api/remote-option6-global-del.json src/share/api/remote-option6-global-get-all.json src/share/api/remote-option6-global-get.json src/share/api/remote-option6-global-set.json src/share/api/remote-option6-network-del.json src/share/api/remote-option6-network-set.json src/share/api/remote-option6-pd-pool-del.json src/share/api/remote-option6-pd-pool-set.json src/share/api/remote-option6-pool-del.json src/share/api/remote-option6-pool-set.json src/share/api/remote-option6-subnet-del.json src/share/api/remote-option6-subnet-set.json src/share/api/remote-server4-del.json src/share/api/remote-server4-get-all.json src/share/api/remote-server4-get.json src/share/api/remote-server4-set.json src/share/api/remote-server6-del.json src/share/api/remote-server6-get-all.json src/share/api/remote-server6-get.json src/share/api/remote-server6-set.json src/share/api/remote-subnet4-del-by-id.json src/share/api/remote-subnet4-del-by-prefix.json src/share/api/remote-subnet4-get-by-id.json src/share/api/remote-subnet4-get-by-prefix.json src/share/api/remote-subnet4-list.json src/share/api/remote-subnet4-set.json src/share/api/remote-subnet6-del-by-id.json src/share/api/remote-subnet6-del-by-prefix.json src/share/api/remote-subnet6-get-by-id.json src/share/api/remote-subnet6-get-by-prefix.json src/share/api/remote-subnet6-list.json src/share/api/remote-subnet6-set.json src/share/api/reservation-add.json src/share/api/reservation-del.json src/share/api/reservation-get-all.json src/share/api/reservation-get-by-hostname.json src/share/api/reservation-get-by-id.json src/share/api/reservation-get-page.json src/share/api/reservation-get.json src/share/api/server-tag-get.json src/share/api/shutdown.json src/share/api/stat-lease4-get.json src/share/api/stat-lease6-get.json src/share/api/statistic-get-all.json src/share/api/statistic-get.json src/share/api/statistic-remove-all.json src/share/api/statistic-remove.json src/share/api/statistic-reset-all.json src/share/api/statistic-reset.json src/share/api/statistic-sample-age-set-all.json src/share/api/statistic-sample-age-set.json src/share/api/statistic-sample-count-set-all.json src/share/api/statistic-sample-count-set.json src/share/api/status-get.json src/share/api/subnet4-add.json src/share/api/subnet4-del.json src/share/api/subnet4-get.json src/share/api/subnet4-list.json src/share/api/subnet4-update.json src/share/api/subnet6-add.json src/share/api/subnet6-del.json src/share/api/subnet6-get.json src/share/api/subnet6-list.json src/share/api/subnet6-update.json src/share/api/version-get.json kea-2.0.2/doc/sphinx/man/0000755000175000017500000000000014206773520012113 500000000000000kea-2.0.2/doc/sphinx/man/perfdhcp.8.rst0000644000175000017500000005350014206773363014536 00000000000000.. Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC") This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. perfdhcp - DHCP benchmarking tool --------------------------------- Synopsis ~~~~~~~~ :program:`perfdhcp` [**-1**] [**-4** | **-6**] [**-A** encapsulation-level] [**-b** base] [**-B**] [**-c**] [**-C** separator] [**-d** drop-time] [**-D** max-drop] [-e lease-type] [**-E** time-offset] [**-f** renew-rate] [**-F** release-rate] [**-g** thread-mode] [**-h**] [**-i**] [**-I** ip-offset] [**-J** remote-address-list-file] [**-l** local-address|interface] [**-L** local-port] [**-M** mac-list-file] [**-n** num-request] [**-N** remote-port] [**-O** random-offset] [**-o** code,hexstring] [**-p** test-period] [**-P** preload] [**-r** rate] [**-R** num-clients] [**-s** seed] [**-S** srvid-offset] [**--scenario** name] [**-t** report] [**-T** template-file] [**-u**] [**-v**] [**-W** exit-wait-time] [**-w** script_name] [**-x** diagnostic-selector] [**-X** xid-offset] [server] Description ~~~~~~~~~~~ ``perfdhcp`` is a DHCP benchmarking tool. It provides a way of measuring the performance of DHCP servers by generating large amounts of traffic from simulated multiple clients. It is able to test both IPv4 and IPv6 servers, and provides statistics concerning response times and the number of requests that are dropped. The tool supports scenarios, which offer certain behaviours. By default (basic scenario) tests are run using the full four-packet exchange sequence (DORA for DHCPv4, SARR for DHCPv6). An option is provided to run tests using the initial two-packet exchange (DO and SA) instead. It is also possible to configure ``perfdhcp`` to send DHCPv6 RENEW and RELEASE messages at a specified rate in parallel with the DHCPv6 four-way exchanges. By default, if there is no response received with 1 second, a response is considered lost and perfdhcp continues with other transactions. Second scenario is called avalanche, which is selected by ``--scenario avalanche``. It first sends as many Discovery or Solicit messages as request in -R option then a retransmission (with exponential back off mechanism) is used for each simulated client until all requests are answered. It will generate report when all clients get their addresses or when it will be manually stopped. This scenario attempts to replicate a case where the server is not able to handle the traffic swiftly enough. Real clients will assume the packet or the response was lost and will retransmit, further increasing DHCP traffic. This is sometimes called avalanche effect, thus the scenario name. Option ``-p`` is ignored in avalanche scenario. When running a performance test, ``perfdhcp`` will exchange packets with the server under test as fast as possible unless the ``-r`` parameter is used to limit the request rate. The length of the test can be limited by setting a threshold on any or all of the number of requests made by ``perfdhcp``, the elapsed time, or the number of requests dropped by the server. Templates ~~~~~~~~~ To allow the contents of packets sent to the server to be customized, ``perfdhcp`` allows the specification of template files that determine the contents of the packets. For example, the customized packet may contain a DHCPv6 ORO to request a set of options to be returned by the server, or it may contain the Client FQDN option to request that the server perform DNS updates. This may be used to discover performance bottlenecks for different server configurations (e.g. DDNS enabled or disabled). Up to two template files can be specified on the command line, each file representing the contents of a particular type of packet, the type being determined by the test being carried out. For example, if testing DHCPv6: - With no template files specified on the command line, ``perfdhcp`` will generate both SOLICIT and REQUEST packets. - With one template file specified, that file will be used as the pattern for SOLICIT packets: ``perfdhcp`` will generate the REQUEST packets. - With two template files given on the command line, the first will be used as the pattern for SOLICIT packets, the second as the pattern for REQUEST packets. (Similar determination applies to DHCPv4's DISCOVER and REQUEST packets.) The template file holds the DHCP packet represented as a stream of ASCII hexadecimal digits and it excludes any IP/UDP stack headers. The template file must not contain any characters other than hexadecimal digits and spaces. Spaces are discarded when the template file is parsed; in the file, '12B4' is the same as '12 B4' which is the same as '1 2 B 4'. The template files should be used in conjunction with the command-line parameters which specify offsets of the data fields being modified in outbound packets. For example, the ``-E time-offset`` switch specifies the offset of the DHCPv6 Elapsed Time option in the packet template. If the offset is specified, ``perfdhcp`` will inject the current elapsed-time value into this field before sending the packet to the server. In many scenarios, ``perfdhcp`` needs to simulate multiple clients, each having a unique client identifier. Since packets for each client are generated from the same template file, it is necessary to randomize the client identifier (or HW address in DHCPv4) in the packet created from it. The ``-O random-offset`` option allows specification of the offset in the template where randomization should be performed. It is important to note that this offset points to the end (not the beginning) of the client identifier (or HW address field). The number of bytes being randomized depends on the number of simulated clients. If the number of simulated clients is between 1 and 255, only one byte (to which the randomization offset points) will be randomized. If the number of simulated clients is between 256 and 65535, two bytes will be randomized. Note that the last two bytes of the client identifier will be randomized in this case: the byte which the randomization offset parameter points to, and the one which precedes it (random-offset - 1). If the number of simulated clients exceeds 65535, three bytes will be randomized, and so on. Perfdhcp can now simulate traffic from multiple subnets by enabling option -J and passing path to file that contains v4 or v6 addresses that will be used as relay in generated messages. That enable testing of vast numbers of Kea shared networks. While testing Kea v4 it should be started with KEA_TEST_SEND_RESPONSES_TO_SOURCE environment variable to force Kea to send generated messages to source address of incoming packet. Templates may currently be used to generate packets being sent to the server in 4-way exchanges, i.e. SOLICIT, REQUEST (DHCPv6) and DISCOVER, REQUEST (DHCPv4). They cannot be used when RENEW or RELEASE packets are being sent. Options ~~~~~~~ ``-1`` Takes the server-ID option from the first received message. ``-4`` Establishes DHCPv4 operation; this is the default. It is incompatible with the ``-6`` option. ``-6`` Establishes DHCPv6 operation. This is incompatible with the ``-4`` option. ``-b basetype=value`` Indicates the base MAC or DUID used to simulate different clients. The basetype may be "mac" or "duid". (The keyword "ether" may alternatively used for MAC.) The ``-b`` option can be specified multiple times. The MAC address must consist of six octets separated by single (:) or double (::) colons, for example: mac=00:0c:01:02:03:04. The DUID value is a hexadecimal string; it must be at least six octets long and not longer than 64 bytes, and the length must be less than 128 hexadecimal digits, for example: duid=0101010101010101010110111F14. ``-d drop-time`` Specifies the time after which a request is treated as having been lost. The value is given in seconds and may contain a fractional component. The default is 1 second. ``-e lease-type`` Specifies the type of lease being requested from the server. It may be one of the following: **address-only** Only regular addresses (v4 or v6) will be requested. **prefix-only** Only IPv6 prefixes will be requested. **address-and-prefix** Both IPv6 addresses and prefixes will be requested. The ``-e prefix-only`` and ``-e address-and-prefix`` forms may not be used with the ``-4`` option. ``-F release-rate`` Specifies the rate at which RELEASE requests are sent to a server. This value is only valid when used in conjunction with the exchange rate (given by ``-r rate``). Furthermore, the sum of this value and the renew-rate (given by ``-f rate``) must be equal to or less than the exchange rate value. ``-f renew-rate`` Specifies the rate at which DHCPv4 or DHCPv6 renew requests are sent to a server. This value is only valid when used in conjunction with the exchange rate (given by ``-r rate``). Furthermore, the sum of this value and the release-rate (given by ``-F rate``) must be equal to or less than the exchange rate. ``-g thread-mode`` Allows selection of thread-mode, which can be either 'single' or 'multi'. In multi-thread mode packets are received in a separate thread, which allows better utilisation of CPUs. In a single-CPU system it is better to run in one thread to avoid threads blocking each other. If more than one CPU is present in the system, multi-thread mode is the default; otherwise single-thread is the default. ``-h`` Prints help and exits. ``-i`` Performs only the initial part of the exchange: DISCOVER-OFFER if ``-4`` is selected, SOLICIT-ADVERTISE if ``-6`` is chosen. ``-i`` is incompatible with the following options: ``-1``, ``-d``, ``-D``, ``-E``, ``-S``, ``-I`` and ``-F``. In addition, it cannot be used with multiple instances of ``-O``, ``-T`` and ``-X``. ``-J remote-address-list-file`` Text file that include multiple addresses. If provided perfdhcp will choose randomly one of addresses for each exchange. This is used to generate traffic from multiple subnets. Designed to test shared-networks. While testing kea v4 it should be started with KEA_TEST_SEND_RESPONSES_TO_SOURCE=ENABLE env variable otherwise perfdhcp will not be able to receive responses. ``-l local-addr|interface`` For DHCPv4 operation, specifies the local hostname/address to use when communicating with the server. By default, the interface address through which traffic would normally be routed to the server is used. For DHCPv6 operation, specifies the name of the network interface through which exchanges are initiated. ``-L local-port`` Specifies the local port to use. This must be zero or a positive integer up to 65535. A value of 0 (the default) allows ``perfdhcp`` to choose its own port. ``-M mac-list-file`` Specifies a text file containing a list of MAC addresses, one per line. If provided, a MAC address will be chosen randomly from this list for every new exchange. In DHCPv6, MAC addresses are used to generate DUID-LLs. This parameter must not be used in conjunction with the -b parameter. ``-N remote-port`` Specifies the remote port to use. This must be zero or a positive integer up to 65535. A value of 0 (the default) allows ``perfdhcp`` to choose the standard service port. ``-o code,hexstring`` Forces ``perfdhcp`` to insert the specified extra option (or options if used several times) into packets being transmitted. The code specifies the option code and the hexstring is a hexadecimal string that defines the content of the option. Care should be taken as ``perfdhcp`` does not offer any kind of logic behind those options; they are simply inserted into packets and sent as is. Be careful not to duplicate options that are already inserted. For example, to insert client class identifier (option code 60) with a string 'docsis', use -o 60,646f63736973. The ``-o`` may be used multiple times. It is necessary to specify the protocol family (either ``-4`` or ``-6``) before using ``-o``. ``-P preload`` Initiates preload exchanges back-to-back at startup. Must be 0 (the default) or a positive integer. ``-r rate`` Initiates the rate of DORA/SARR (or if ``-i`` is given, DO/SA) exchanges per second. A periodic report is generated showing the number of exchanges which were not completed, as well as the average response latency. The program continues until interrupted, at which point a final report is generated. ``-R num-clients`` Specifies how many different clients are used. With a value of 1 (the default), all requests seem to come from the same client. Must be a positive number. ``-s seed`` Specifies the seed for randomization, making runs of ``perfdhcp`` repeatable. This must be 0 or a positive integer. The value 0 means that a seed is not used; this is the default. ``--scenario name`` Specifies type of the scenario, can be **basic** (default) or **avalanche**. ``-T template-file`` Specifies a file containing the template to use as a stream of hexadecimal digits. This may be specified up to two times and controls the contents of the packets sent (see the "Templates" section above). ``-u`` Enable checking address uniqueness. Lease valid lifetime should not be shorter than test duration and clients should not request address more than once without releasing it first. ``-v`` Prints the version of this program. ``-W exit-wait-time`` Specifies the exit-wait-time parameter, which causes ``perfdhcp`` to wait for exit-wait-time after an exit condition has been met, to receive all packets without sending any new packets. Expressed in microseconds. If not specified, 0 is used (i.e. exit immediately after exit conditions are met). ``-w script_name`` Specifies the name of the script to be run before/after ``perfdhcp``. When called, the script is passed a single parameter, either "start" or "stop", indicating whether it is being called before or after ``perfdhcp``. ``-x diagnostic-selector`` Includes extended diagnostics in the output. This is a string of single keywords specifying the operations for which verbose output is desired. The selector key letters are: **a** Prints the decoded command line arguments. **e** Prints the exit reason. **i** Prints the rate processing details. **l** Prints the received leases. **s** Prints the first server-ID. **t** When finished, prints timers of all successful exchanges. **T** When finished, prints templates. ``-y seconds`` Time in seconds after which perfdhcp will start simulating the client waiting longer for server responses. This increase the secs field in DHCPv4 and sends increased values in Elapsed option in DHCPv6. Must be used with '-Y'. ``-Y seconds`` Period of time in seconds in which perfdhcp will be simulating the client waiting longer for server responses. This increase the secs field in DHCPv4 and sends increased values in Elapsed option in DHCPv6. Must be used with '-y'. DHCPv4-Only Options ~~~~~~~~~~~~~~~~~~~ The following options only apply for DHCPv4 (i.e. when ``-4`` is given). ``-B`` Forces broadcast handling. DHCPv6-Only Options ~~~~~~~~~~~~~~~~~~~ The following options only apply for DHCPv6 (i.e. when ``-6`` is given). ``-c`` Adds a rapid-commit option (exchanges will be SOLICIT-ADVERTISE). ``-A encapsulation-level`` Specifies that relayed traffic must be generated. The argument specifies the level of encapsulation, i.e. how many relay agents are simulated. Currently the only supported encapsulation-level value is 1, which means that the generated traffic is equivalent to the amount of traffic passing through a single relay agent. Template-Related Options ~~~~~~~~~~~~~~~~~~~~~~~~ The following options may only be used in conjunction with ``-T`` and control how ``perfdhcp`` modifies the template. The options may be specified multiple times on the command line; each occurrence affects the corresponding template file (see "Templates" above). ``-E time-offset`` Specifies the offset of the secs field (DHCPv4) or elapsed-time option (DHCPv6) in the second (i.e. REQUEST) template; must be 0 or a positive integer. A value of 0 disables this. ``-I ip-offset`` Specifies the offset of the IP address (DHCPv4) in the requested-IP option or IA_NA option (DHCPv6) in the second (REQUEST) template. ``-O random-offset`` Specifies the offset of the last octet to randomize in the template. This must be an integer greater than 3. The ``-T`` switch must be given to use this option. ``-S srvid-offset`` Specifies the offset of the server-ID option in the second (REQUEST) template. This must be a positive integer, and the switch can only be used when the template option (``-T``) is also given. ``-X xid-offset`` Specifies the offset of the transaction ID (xid) in the template. This must be a positive integer, and the switch can only be used when the template option (``-T``) is also given. Options Controlling a Test ~~~~~~~~~~~~~~~~~~~~~~~~~~ ``-D max-drop`` Aborts the test immediately if **max-drop** requests have been dropped. Use ``-D 0`` to abort if even a single request has been dropped. **max-drop** must be a positive integer. If **max-drop** includes the suffix '%', it specifies a maximum percentage of requests that may be dropped before abort. In this case, testing of the threshold begins after 10 requests have been expected to be received. ``-n num-requests`` Initiates **num-request** transactions. No report is generated until all transactions have been initiated/waited-for, after which a report is generated and the program terminates. ``-p test-period`` Sends requests for **test-period**, which is specified in the same manner as ``-d``. This can be used as an alternative to ``-n`` or both options can be given, in which case the testing is completed when either limit is reached. ``-t interval`` Sets the delay (in seconds) between two successive reports. ``-C separator`` Output reduced, an argument is a separator for periodic (-t) reports generated in easy parsable mode. Data output won't be changed, remain identical as in -t option. Arguments ~~~~~~~~~ server Indicates the server to test, specified as an IP address. In the DHCPv6 case, the special name 'all' can be used to refer to All_DHCP_Relay_Agents_and_Servers (the multicast address FF02::1:2), or the special name 'servers' to refer to All_DHCP_Servers (the multicast address FF05::1:3). The server is mandatory except where the ``-l`` option is given to specify an interface, in which case it defaults to 'all'. Errors ~~~~~~ ``perfdhcp`` can report the following errors in the packet exchange: tooshort A message was received that was too short. orphans A message was received which does not match one sent to the server (i.e. it is a duplicate message, a message that has arrived after an excessive delay, or one that is just not recognized). locallimit Local system limits have been reached when sending a message. Exit Status ~~~~~~~~~~~ ``perfdhcp`` can exit with one of the following status codes: 0 Success. 1 General error. 2 Error in command-line arguments. 3 No general failures in operation, but one or more exchanges were unsuccessful. Usage Examples ~~~~~~~~~~~~~~ Simulate regular DHCPv4 traffic: 100 DHCPv4 devices (-R 100), 10 packets per second (-r 10), show the query/response rate details (-xi), the report should be shown every 2 seconds (-t 2), send the packets to the IP 192.0.2.1: sudo perfdhcp -xi -t 2 -r 10 -R 100 192.0.2.1 Here's a similar case, but for DHCPv6. Note that DHCPv6 protocol uses link-local addresses, so you need to specify the interface (eth0 in this example) to send the traffic. 'all' is a convenience alias for All_DHCP_Relay_Agents_and_Servers (the multicast address FF02::1:2). Alternatively, you can use 'servers' alias to refer to All_DHCP_Servers (the multicast address FF05::1:3), or skip it all together and the default value (all) will be used. sudo perfdhcp -6 -xi -t 1 -r 1 -R 10 -l eth0 all The following examples simulate normal DHCPv4 and DHCPv6 traffic that after 3 seconds starts pretending to not receive any responses from the server for 10 seconds. DHCPv4 protocol signals this by increased secs field and DHCPv6 uses elapsed option for that. In real networks this indicates that the clients are not getting responses in a timely matter. This can be used to simulate some HA scenarios, as Kea uses secs field and elapsed option value as one of the indicators that the HA partner is not responding. When enabled with -y and -Y, the secs and elapsed time value increased steadily. sudo perfdhcp -xi -t 1 -r 1 -y 10 -Y 3 192.0.2.1 sudo perfdhcp -6 -xi -t 1 -r 1 -y 10 -Y 3 2001:db8::1 Mailing Lists and Support ~~~~~~~~~~~~~~~~~~~~~~~~~ There are two public mailing lists available for the Kea project. **kea-users** (kea-users at lists.isc.org) is intended for Kea users, while **kea-dev** (kea-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at https://lists.isc.org. The community provides best-effort support on both of those lists. ISC provides professional support for Kea services. See https://www.isc.org/kea/ for details. History ~~~~~~~ The ``perfdhcp`` tool was initially coded in October 2011 by John DuBois, Francis Dupont, and Marcin Siodelski of ISC. Kea 1.0.0, which included ``perfdhcp``, was released in December 2015. See Also ~~~~~~~~ :manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`, :manpage:`kea-dhcp-ddns(8)`, :manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`kea-netconf(8)`, :manpage:`keactrl(8)`, :manpage:`kea-lfc(8)`, Kea Administrator Reference Manual. kea-2.0.2/doc/sphinx/man/kea-dhcp4.8.rst0000644000175000017500000000662114206773363014505 00000000000000.. Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC") This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. kea-dhcp4 - DHCPv4 server in Kea -------------------------------- Synopsis ~~~~~~~~ :program:`kea-dhcp4` [**-v**] [**-V**] [**-W**] [**-d**] [**-c** config-file] [**-t** config-file] [**-p** server-port-number] [**-P** client-port-number] Description ~~~~~~~~~~~ The ``kea-dhcp4`` daemon provides the DHCPv4 server implementation. Arguments ~~~~~~~~~ The arguments are as follows: ``-v`` Displays the version. ``-V`` Displays the extended version. ``-W`` Displays the configuration report. ``-d`` Enables the debug mode with extra verbosity. ``-c config-file`` Specifies the configuration file with the configuration for the DHCPv4 server. It may also contain configuration entries for other Kea services. ``-t config-file`` Checks the configuration file and reports the first error, if any. Note that not all parameters are completely checked; in particular, service and control channel sockets are not opened, and hook libraries are not loaded. ``-p server-port-number`` Specifies the server port number (1-65535) on which the server listens. This is useful for testing purposes only. ``-P client-port-number`` Specifies the client port number (1-65535) to which the server responds. This is useful for testing purposes only. Documentation ~~~~~~~~~~~~~ Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software - compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at https://kea.readthedocs.io. Kea source code is documented in the Kea Developer's Guide. Its online version is available at https://reports.kea.isc.org/dev_guide/. The Kea project website is available at https://kea.isc.org. Mailing Lists and Support ~~~~~~~~~~~~~~~~~~~~~~~~~ There are two public mailing lists available for the Kea project. **kea-users** (kea-users at lists.isc.org) is intended for Kea users, while **kea-dev** (kea-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at https://lists.isc.org. The community provides best-effort support on both of those lists. ISC provides professional support for Kea services. See https://www.isc.org/kea/ for details. History ~~~~~~~ The ``b10-dhcp4`` daemon was first coded in November 2011 by Tomek Mrugalski. In mid-2014, Kea was decoupled from the BIND 10 framework and became a standalone DHCP server. The DHCPv4 server binary was renamed to kea-dhcp4. Kea 1.0.0 was released in December 2015. See Also ~~~~~~~~ :manpage:`kea-dhcp6(8)`, :manpage:`kea-dhcp-ddns(8)`, :manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`keactrl(8)`, :manpage:`perfdhcp(8)`, :manpage:`kea-netconf(8)`, :manpage:`kea-lfc(8)`, Kea Administrator Reference Manual. kea-2.0.2/doc/sphinx/man/rst_man_sources.mk0000644000175000017500000000061414206773363015600 00000000000000rst_man_sources += man/kea-admin.8.rst rst_man_sources += man/kea-ctrl-agent.8.rst rst_man_sources += man/kea-dhcp4.8.rst rst_man_sources += man/kea-dhcp6.8.rst rst_man_sources += man/kea-dhcp-ddns.8.rst rst_man_sources += man/kea-lfc.8.rst rst_man_sources += man/kea-netconf.8.rst rst_man_sources += man/kea-shell.8.rst rst_man_sources += man/keactrl.8.rst rst_man_sources += man/perfdhcp.8.rst kea-2.0.2/doc/sphinx/man/man8s.mk0000644000175000017500000000066414206773363013425 00000000000000man8s += $(sphinxbuilddir)/man/kea-admin.8 man8s += $(sphinxbuilddir)/man/kea-ctrl-agent.8 man8s += $(sphinxbuilddir)/man/kea-dhcp4.8 man8s += $(sphinxbuilddir)/man/kea-dhcp6.8 man8s += $(sphinxbuilddir)/man/kea-dhcp-ddns.8 man8s += $(sphinxbuilddir)/man/kea-lfc.8 man8s += $(sphinxbuilddir)/man/kea-netconf.8 man8s += $(sphinxbuilddir)/man/kea-shell.8 man8s += $(sphinxbuilddir)/man/keactrl.8 man8s += $(sphinxbuilddir)/man/perfdhcp.8 kea-2.0.2/doc/sphinx/man/keactrl.8.rst0000644000175000017500000001005614206773363014367 00000000000000.. Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC") This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. keactrl - Shell script for managing Kea --------------------------------------- Synopsis ~~~~~~~~ :program:`keactrl` [**command**] [**-c** keactrl-config-file] [**-s** server[,server,...]] [**-v**] Description ~~~~~~~~~~~ ``keactrl`` is a shell script which controls the startup, shutdown, and reconfiguration of the Kea servers (``kea-dhcp4``, ``kea-dhcp6``, ``kea-dhcp-ddns``, ``kea-ctrl-agent``, and ``kea-netconf``). It also provides the means for checking the current status of the servers and determining the configuration files in use. Configuration File ~~~~~~~~~~~~~~~~~~ Depending on the user's requirements, not all of the available servers need be run. The ``keactrl`` configuration file specifies which servers are enabled and which are disabled. By default the configuration file is ``[kea-install-dir]/etc/kea/keactrl.conf``. See the Kea Administrator Reference Manual for documentation of the parameters in the ``keactrl`` configuration file. Options ~~~~~~~ ``command`` Specifies the command to be issued to the servers. It can be one of the following: **start** Starts the servers. **stop** Stops the servers. **reload** Instructs the servers to re-read the Kea configuration file. This command is not supported by the Netconf agent. **status** Prints the status of the servers. ``-c|--ctrl-config keactrl-config-file`` Specifies the ``keactrl`` configuration file. Without this switch, ``keactrl`` attempts to use the file ``[kea-install-dir]/etc/kea/keactrl.conf``. ``-s|--server server[,server,...]`` Specifies a subset of the enabled servers to which the command should be issued. The list of servers should be separated by commas with no intervening spaces. Acceptable values are: **dhcp4** DHCPv4 server (``kea-dhcp4``). **dhcp6** DHCPv6 server (``kea-dhcp6``). **dhcp_ddns** DHCP DDNS server (``kea-dhcp-ddns``). **ctrl_agent** Control Agent (``kea-ctrl-agent``). **netconf** Netconf agent (``kea-netconf``). **all** All servers, including Netconf if it was configured to be built. This is the default. ``-v|--version`` Prints the ``keactrl`` version and quits. Documentation ~~~~~~~~~~~~~ Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software - compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at https://kea.readthedocs.io. Kea source code is documented in the Kea Developer's Guide. Its online version is available at https://reports.kea.isc.org/dev_guide/. The Kea project website is available at https://kea.isc.org. Mailing Lists and Support ~~~~~~~~~~~~~~~~~~~~~~~~~ There are two public mailing lists available for the Kea project. **kea-users** (kea-users at lists.isc.org) is intended for Kea users, while **kea-dev** (kea-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at https://lists.isc.org. The community provides best-effort support on both of those lists. ISC provides professional support for Kea services. See https://www.isc.org/kea/ for details. See Also ~~~~~~~~ :manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`, :manpage:`kea-dhcp-ddns(8)`, :manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`kea-netconf(8)`, :manpage:`perfdhcp(8)`, :manpage:`kea-lfc(8)`, Kea Administrator Reference Manual. kea-2.0.2/doc/sphinx/man/kea-dhcp-ddns.8.rst0000644000175000017500000000635014206773363015346 00000000000000.. Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC") This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. kea-dhcp-ddns - DHCP-DDNS process in Kea ---------------------------------------- Synopsis ~~~~~~~~ :program:`kea-dhcp-ddns` [**-v**] [**-V**] [**-W**] [**-d**] [**-c** config-file] [**-t** config-file] Description ~~~~~~~~~~~ The ``kea-dhcp-ddns`` service process requests an update of DNS mapping based on DHCP lease change events. It runs as a separate process that expects to receive Name Change Requests from Kea DHCP servers. Arguments ~~~~~~~~~ The arguments are as follows: ``-v`` Displays the version. ``-V`` Displays the extended version. ``-W`` Displays the configuration report. ``-d`` Sets the logging level to debug with extra verbosity. This is primarily for development purposes in stand-alone mode. ``-c config-file`` Specifies the configuration file with the configuration for the DHCP-DDNS server. It may also contain configuration entries for other Kea services. ``-t config-file`` Checks the syntax of the configuration file and reports the first error if any. Note that not all parameters are completely checked, in particular, service socket is not opened. Documentation ~~~~~~~~~~~~~ Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software - compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at https://kea.readthedocs.io. Kea source code is documented in the Kea Developer's Guide. Its online version is available at https://reports.kea.isc.org/dev_guide/. The Kea project website is available at https://kea.isc.org. Mailing Lists and Support ~~~~~~~~~~~~~~~~~~~~~~~~~ There are two public mailing lists available for the Kea project. **kea-users** (kea-users at lists.isc.org) is intended for Kea users, while **kea-dev** (kea-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at https://lists.isc.org. The community provides best-effort support on both of those lists. ISC provides professional support for Kea services. See https://www.isc.org/kea/ for details. History ~~~~~~~ The ``b10-dhcp-ddns`` process was first coded in May 2013 by Thomas Markwalder. Kea became a standalone server and the BIND 10 framework was removed. The DHCP-DDNS server binary was renamed to kea-dhcp-ddns in July 2014. Kea 1.0.0 was released in December 2015. See Also ~~~~~~~~ :manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`, :manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`keactrl(8)`, :manpage:`perfdhcp(8)`, :manpage:`kea-netconf(8)`, :manpage:`kea-lfc(8)`, Kea Administrator Reference Manual. kea-2.0.2/doc/sphinx/man/kea-lfc.8.rst0000644000175000017500000001165314206773363014250 00000000000000.. Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC") This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. kea-lfc - Lease File Cleanup process in Kea ------------------------------------------- Synopsis ~~~~~~~~ :program:`kea-lfc` [**-4**|**-6**] [**-c** config-file] [**-p** pid-file] [**-x** previous-file] [**-i** copy-file] [**-o** output-file] [**-f** finish-file] [**-v**] [**-V**] [**-W**] [**-d**] [**-h**] Description ~~~~~~~~~~~ The ``kea-lfc`` service process removes redundant information from the files used to provide persistent storage for the memfile database backend. The service is written to run as a stand-alone process. While it can be started externally, there is usually no need to do this. It is run periodically by the Kea DHCP servers. Arguments ~~~~~~~~~ The arguments are as follows: ``-4 | -6`` Indicates the protocol version of the lease files; must be either 4 or 6. ``-c config-file`` Specifies the file with the configuration for the ``kea-lfc`` process. It may also contain configuration entries for other Kea services. Currently ``kea-lfc`` gets all of its arguments from the command line; in the future it will be extended to obtain some arguments from the configuration file. ``-p pid-file`` Specifies the PID file. When the ``kea-lfc`` process starts, it attempts to determine if another instance of the process is already running by examining the PID file. If one is already running, the new process is terminated. If one is not running, Kea writes its PID into the PID file. ``-x previous-file`` Specifies the previous or ex-lease file. When ``kea-lfc`` starts, this is the result of any previous run of ``kea-lfc``; when ``kea-lfc`` finishes, it is the result of this run. If ``kea-lfc`` is interrupted before completing, this file may not exist. ``-i copy-file`` Specifies the input or copy of lease file. Before the DHCP server invokes ``kea-lfc``, it will move the current lease file here and then call ``kea-lfc`` with this file. ``-o output-file`` Specifies the output lease file, which is the temporary file ``kea-lfc`` should use to write the leases. Once this file is finished writing, it is moved to the finish file (see below). ``-f finish-file`` Specifies the finish or completion file, another temporary file ``kea-lfc`` uses for bookkeeping. When ``kea-lfc`` finishes writing the output file, it moves it to this file name. After ``kea-lfc`` finishes deleting the other files (previous and input), it moves this file to the previous lease file. By moving the files in this fashion, the ``kea-lfc`` and the DHCP server processes can determine the correct file to use even if one of the processes was interrupted before completing its task. ``-v`` Causes the version stamp to be printed. ``-V`` Causes a longer form of the version stamp to be printed. ``-W`` Displays the configuration report. ``-d`` Sets the logging level to debug with extra verbosity. This is primarily for development purposes in stand-alone mode. ``-h`` Causes the usage string to be printed. Documentation ~~~~~~~~~~~~~ Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software - compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at https://kea.readthedocs.io. Kea source code is documented in the Kea Developer's Guide. Its online version is available at https://reports.kea.isc.org/dev_guide/. The Kea project website is available at https://kea.isc.org. Mailing Lists and Support ~~~~~~~~~~~~~~~~~~~~~~~~~ There are two public mailing lists available for the Kea project. **kea-users** (kea-users at lists.isc.org) is intended for Kea users, while **kea-dev** (kea-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at https://lists.isc.org. The community provides best-effort support on both of those lists. ISC provides professional support for Kea services. See https://www.isc.org/kea/ for details. History ~~~~~~~ The ``kea-lfc`` process was first coded in January 2015 by the ISC Kea/DHCP team. See Also ~~~~~~~~ :manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`, :manpage:`kea-dhcp-ddns(8)`, :manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`keactrl(8)`, :manpage:`perfdhcp(8)`, :manpage:`kea-netconf(8)`, Kea Administrator Reference Manual. kea-2.0.2/doc/sphinx/man/kea-shell.8.rst0000644000175000017500000001065714206773363014616 00000000000000.. Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC") This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. kea-shell - Text client for Control Agent process ------------------------------------------------- Synopsis ~~~~~~~~ :program:`kea-shell` [**-h**] [**-v**] [**--host**] [**--port**] [**--path**] [**--ca**] [**--cert**] [**--key**] [**--auth-user**] [**--auth-password**] [**--timeout**] [**--service**] [command] Description ~~~~~~~~~~~ The ``kea-shell`` provides a REST client for the Kea Control Agent (CA). It takes command as a command-line parameter that is being sent to CA with proper JSON encapsulation. Optional arguments may be specified on the standard input. The request is sent via HTTP and a response is retrieved, displayed on the standard output. Basic HTTP authentication and HTTPS i.e. TLS transport are supported. Arguments ~~~~~~~~~ The arguments are as follows: ``-h`` Displays help regarding command-line parameters. ``-v`` Displays the version. ``--host`` Specifies the host to connect to. Control Agent must be running at the specified host. If not specified, 127.0.0.1 is used. ``--port`` Specifies the TCP port to connect to. Control Agent must be listening at the specified port. If not specified, 8000 is used. ``--path`` Specifies the path in the URL to connect to. If not specified, an empty path is used. As Control Agent listens at the empty path, this parameter is useful only with a reverse proxy. ``--ca`` Specifies the file or directory name of the Certification Authority. If not specified HTTPS is not used. ``--cert`` Specifies the file name of the user end-entity public key certificate. If specified the file name of the user key must be specified too. ``--key`` Specifies the file name of the user key file. If specified the file name of the user certificate must be specified too. Note that encrypted key files are not supported. ``--auth-user`` Specifies the user id for basic HTTP authentication. If not specified or specified as the empty string authentication is not used. ``--auth-password`` Specifies the password for basic HTTP authentication. If not specified but the user id is specified an empty password is used. ``--timeout`` Specifies the connection timeout in seconds. If not specified, 10 (seconds) is used. ``--service`` Specifies the service that is the target of a command. If not specified, Control Agent will be targeted. May be used more than once to specify multiple targets. ``command`` Specifies the command to be sent to CA. If not specified, "list-commands" is used. Documentation ~~~~~~~~~~~~~ Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software - compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at https://kea.readthedocs.io. Kea source code is documented in the Kea Developer's Guide. Its online version is available at https://reports.kea.isc.org/dev_guide/. The Kea project website is available at https://kea.isc.org. Mailing Lists and Support ~~~~~~~~~~~~~~~~~~~~~~~~~ There are two public mailing lists available for the Kea project. **kea-users** (kea-users at lists.isc.org) is intended for Kea users, while **kea-dev** (kea-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at https://lists.isc.org. The community provides best-effort support on both of those lists. ISC provides professional support for Kea services. See https://www.isc.org/kea/ for details. History ~~~~~~~ The ``kea-shell`` was first coded in March 2017 by Tomek Mrugalski. See Also ~~~~~~~~ :manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`, :manpage:`kea-dhcp-ddns(8)`, :manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`keactrl(8)`, :manpage:`perfdhcp(8)`, :manpage:`kea-lfc(8)`, Kea Administrator Reference Manual. kea-2.0.2/doc/sphinx/man/kea-admin.8.rst0000644000175000017500000001211714206773363014570 00000000000000.. Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC") This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. kea-admin - Shell script for managing Kea databases --------------------------------------------------- Synopsis ~~~~~~~~ :program:`kea-admin` [command] [backend] [**-h** database_host] [**-P** database_port] [**-u** database_username] [**-p** [database_password]] [**-n** database_name] [**-d** script_directory] [**-v**] [**-4** | **-6**] [**-o** output_file] Description ~~~~~~~~~~~ ``kea-admin`` is a shell script which offers database maintenance. In particular, it features database initialization, database version checking, and database schema upgrade. Arguments ~~~~~~~~~ ``command`` Specifies the command to be issued to the servers. It can be one of the following: **db-init** Initializes a new database schema. This is useful during a new Kea installation. The database is initialized to the latest version supported by the version of the software being installed. **db-version** Reports the database backend version number. This is not necessarily equal to the Kea version number as each backend has its own versioning scheme. **db-upgrade** Conducts a database schema upgrade. This is useful when upgrading Kea. **lease-dump** Dumps the contents of the lease database (for MySQL, PostgreSQL, or CQL backends) to a CSV (comma-separated values) text file. The first line of the file contains the column names. This is meant to be used as a diagnostic tool, so it provides a portable, human-readable form of the lease data. **stats-recount** Recounts lease statistics for MySQL or PostgreSQL database. ``backend`` Specifies the backend type. Currently allowed backends are: memfile, mysql, pgsql, and cql. ``-h|--host hostname`` Specifies the hostname when connecting to a database. If not specified, the default value of **localhost** is used. ``-P|--port port`` Specifies the port when connecting to a database. If not specified, the default value chosen by the database client is used. ``-u|--user username`` Specifies the username when connecting to a database. If not specified, the default value of **keatest** is used. ``-p|--password password`` Specifies the password when connecting to a database. If only ``-p`` or ``--password`` is given, the user is prompted for a password. If not specified at all, the **KEA_ADMIN_DB_PASSWORD** environment variable is checked for a value and used if it exists. Otherwise the default value of **keatest** is used. ``-n|--name database-name`` Specifies the name of the database to connect to. If not specified, the default value of **keatest** is used. ``-d|--directory script-directory`` Specifies the override scripts directory. That script is used during upgrades, database initialization, and possibly other operations. If not specified, the default value of ``(prefix)/share/kea/scripts/`` is used. ``-o|--output output_file`` Specifies the file to which the lease data will be dumped. Required for lease-dump. ``-v|--version`` Prints the ``kea-admin`` version and quits. ``-4`` Directs ``kea-admin`` to lease-dump the DHCPv4 leases. Incompatible with the -6 option. ``-6`` Directs ``kea-admin`` to lease-dump the DHCPv6 leases. Incompatible with the -4 option. Documentation ~~~~~~~~~~~~~ Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software - compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at https://kea.readthedocs.io. Kea source code is documented in the Kea Developer's Guide. Its online version is available at https://reports.kea.isc.org/dev_guide/. The Kea project website is available at https://kea.isc.org. Mailing Lists and Support ~~~~~~~~~~~~~~~~~~~~~~~~~ There are two public mailing lists available for the Kea project. **kea-users** (kea-users at lists.isc.org) is intended for Kea users, while **kea-dev** (kea-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at https://lists.isc.org. The community provides best-effort support on both of those lists. ISC provides professional support for Kea services. See https://www.isc.org/kea/ for details. See Also ~~~~~~~~ :manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`, :manpage:`kea-dhcp-ddns(8)`, :manpage:`kea-ctrl-agent(8)`, :manpage:`keactrl(8)`, :manpage:`perfdhcp(8)`, :manpage:`kea-netconf(8)`, Kea Administrator Reference Manual. kea-2.0.2/doc/sphinx/man/kea-netconf.8.rst0000644000175000017500000000575514206773363015146 00000000000000.. Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC") This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. kea-netconf - NETCONF agent for configuring Kea ----------------------------------------------- Synopsis ~~~~~~~~ :program:`kea-netconf` [**-v**] [**-V**] [**-W**] [**-d**] [**-c** config-file] [**-t** config-file] Description ~~~~~~~~~~~ The ``kea-netconf`` agent provides a YANG/NETCONF interface for the Kea environment. Arguments ~~~~~~~~~ The arguments are as follows: ``-v`` Displays the version. ``-V`` Displays the extended version. ``-W`` Displays the configuration report. ``-d`` Enables the debug mode with extra verbosity. ``-c config-file`` Specifies the file with the configuration for the NETCONF agent. ``-t config-file`` Checks the syntax of the configuration file and reports the first error, if any. Note that not all parameters are completely checked; in particular, service and client sockets are not opened, and hook libraries are not loaded. Documentation ~~~~~~~~~~~~~ Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software - compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at https://kea.readthedocs.io. Kea source code is documented in the Kea Developer's Guide. Its online version is available at https://reports.kea.isc.org/dev_guide/. The Kea project website is available at https://kea.isc.org. Mailing Lists and Support ~~~~~~~~~~~~~~~~~~~~~~~~~ There are two public mailing lists available for the Kea project. **kea-users** (kea-users at lists.isc.org) is intended for Kea users, while **kea-dev** (kea-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at https://lists.isc.org. The community provides best-effort support on both of those lists. ISC provides professional support for Kea services. See https://www.isc.org/kea/ for details. History ~~~~~~~ Early prototypes of ``kea-netconf`` implementation were written during IETF Hackathons in Berlin, London, and Montreal. An actual production-ready implementation was started in August 2018 by Tomek Mrugalski and Francis Dupont. See Also ~~~~~~~~ :manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`, :manpage:`kea-dhcp-ddns(8)`, :manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`keactrl(8)`, :manpage:`perfdhcp(8)`, :manpage:`kea-lfc(8)`, Kea Administrator Reference Manual. kea-2.0.2/doc/sphinx/man/kea-ctrl-agent.8.rst0000644000175000017500000000637414206773363015550 00000000000000.. Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC") This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. kea-ctrl-agent - Control Agent process in Kea --------------------------------------------- Synopsis ~~~~~~~~ :program:`kea-ctrl-agent` [**-v**] [**-V**] [**-W**] [**-d**] [**-c** config-file] [**-t** config-file] Description ~~~~~~~~~~~ The ``kea-ctrl-agent`` provides a REST service for controlling Kea services. The received HTTP requests are decapsulated and forwarded to the respective Kea services in JSON format. Received JSON responses are encapsulated within HTTP responses and returned to the controlling entity. Some commands may be handled by the Control Agent directly, and not forwarded to any Kea service. Arguments ~~~~~~~~~ The arguments are as follows: ``-v`` Displays the version. ``-V`` Displays the extended version. ``-W`` Displays the configuration report. ``-d`` Sets the logging level to debug with extra verbosity. This is primarily for development purposes in stand-alone mode. ``-c config-file`` Specifies the file with the configuration for the Control Agent server. It may also contain configuration entries for other Kea services. ``-t config-file`` Checks the syntax of the configuration file and reports the first error, if any. Note that not all parameters are completely checked; in particular, service and client sockets are not opened, and hook libraries are not loaded. Documentation ~~~~~~~~~~~~~ Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software - compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at https://kea.readthedocs.io. Kea source code is documented in the Kea Developer's Guide. Its online version is available at https://reports.kea.isc.org/dev_guide/. The Kea project website is available at https://kea.isc.org. Mailing Lists and Support ~~~~~~~~~~~~~~~~~~~~~~~~~ There are two public mailing lists available for the Kea project. **kea-users** (kea-users at lists.isc.org) is intended for Kea users, while **kea-dev** (kea-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at https://lists.isc.org. The community provides best-effort support on both of those lists. ISC provides professional support for Kea services. See https://www.isc.org/kea/ for details. History ~~~~~~~ The ``kea-ctrl-agent`` was first coded in December 2016 by Marcin Siodelski. See Also ~~~~~~~~ :manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp6(8)`, :manpage:`kea-dhcp-ddns(8)`, :manpage:`kea-admin(8)`, :manpage:`keactrl(8)`, :manpage:`perfdhcp(8)`, :manpage:`kea-lfc(8)`, Kea Administrator Reference Manual. kea-2.0.2/doc/sphinx/man/kea-dhcp6.8.rst0000644000175000017500000000653014206773363014506 00000000000000.. Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC") This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. kea-dhcp6 - DHCPv6 server in Kea -------------------------------- Synopsis ~~~~~~~~ :program:`kea-dhcp6` [**-v**] [**-V**] [**-W**] [**-d**] [**-c** config-file] [**-t** config-file] [**-p** server-port-number] [**-P** client-port-number] Description ~~~~~~~~~~~ The ``kea-dhcp6`` daemon provides the DHCPv6 server implementation. Arguments ~~~~~~~~~ The arguments are as follows: ``-v`` Displays the version. ``-V`` Displays the extended version. ``-W`` Displays the configuration report. ``-d`` Enables the debug mode with extra verbosity. ``-c config-file`` Specifies the configuration file with the configuration for the DHCPv6 server. It may also contain configuration entries for other Kea services. ``-t config-file`` Checks the configuration file and reports the first error, if any. Note that not all parameters are completely checked; in particular, service and control channel sockets are not opened, and hook libraries are not loaded. ``-p server-port-number`` Specifies the server port number (1-65535) on which the server listens. This is useful for testing purposes only. ``-P client-port-number`` Specifies the client port number (1-65535) to which the server responds. This is useful for testing purposes only. Documentation ~~~~~~~~~~~~~ Kea comes with an extensive Kea Administrator Reference Manual that covers all aspects of running the Kea software - compilation, installation, configuration, configuration examples, and much more. Kea also features a Kea Messages Manual, which lists all possible messages Kea can print with a brief description for each of them. Both documents are available in various formats (.txt, .html, .pdf) with the Kea distribution. The Kea documentation is available at https://kea.readthedocs.io. Kea source code is documented in the Kea Developer's Guide. Its online version is available at https://reports.kea.isc.org/dev_guide/. The Kea project website is available at https://kea.isc.org. Mailing Lists and Support ~~~~~~~~~~~~~~~~~~~~~~~~~ There are two public mailing lists available for the Kea project. **kea-users** (kea-users at lists.isc.org) is intended for Kea users, while **kea-dev** (kea-dev at lists.isc.org) is intended for Kea developers, prospective contributors, and other advanced users. Both lists are available at https://lists.isc.org. The community provides best-effort support on both of those lists. ISC provides professional support for Kea services. See https://www.isc.org/kea/ for details. History ~~~~~~~ The ``b10-dhcp6`` daemon was first coded in June 2011 by Tomek Mrugalski. Kea became a standalone server and the BIND 10 framework was removed. The DHCPv6 server binary was renamed to kea-dhcp6 in July 2014. See Also ~~~~~~~~ :manpage:`kea-dhcp4(8)`, :manpage:`kea-dhcp-ddns(8)`, :manpage:`kea-ctrl-agent(8)`, :manpage:`kea-admin(8)`, :manpage:`keactrl(8)`, :manpage:`perfdhcp(8)`, :manpage:`kea-netconf(8)`, :manpage:`kea-lfc(8)`, Kea Administrator Reference Manual. kea-2.0.2/doc/sphinx/api2doc.py0000755000175000017500000001522014206773363013163 00000000000000#!/usr/bin/env python3 # Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC") # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http:#mozilla.org/MPL/2.0/. # Produce API Reference # - reads *.json files (each file describes a single command) # - produces .rst file suitable for Sphinx as output import os import json import argparse import collections def parse_args(): parser = argparse.ArgumentParser(description='Convert set of *.json files to .rst documentation format') parser.add_argument('-o', '--output', help='Output file name (default to stdout).') parser.add_argument('files', help='Input API .json files.', nargs='+') args = parser.parse_args() return args def read_input_files(files): apis = {} for f in files: name = os.path.basename(f)[:-5] # Skip special names starting with _ (such as _template.json) if name.startswith('_'): print("Skipping %s (starts with underscore)" % f) continue with open(f) as fp: print("Processing %s" % f) # use OrderedDict to preserve order of fields in cmd-syntax try: descr = json.load(fp, object_pairs_hook=collections.OrderedDict) except: print('\nError while processing %s\n\n' % f) raise assert name == descr['name'] apis[name] = descr return apis def generate_rst(apis): rst = '''.. _api: API Reference ============= ''' daemons = {} hooks = {} for func in apis.values(): for dm in func['support']: if dm not in daemons: daemons[dm] = [] daemons[dm].append(func) if 'hook' in func: if func['hook'] not in hooks: hooks[func['hook']] = [] hooks[func['hook']].append(func) rst += 'Kea currently supports %d commands in %s daemons and %s hook libraries.\n\n' % ( len(apis), ", ".join([':ref:`%s `' % (m, m) for m in sorted(daemons.keys())]), ", ".join([':ref:`%s `' % (m, m) for m in sorted(hooks.keys())])) for dm, funcs in sorted(daemons.items()): rst += '.. _commands-%s:\n\n' % dm rst += 'Commands supported by `%s` daemon: ' % dm funcs = sorted([ ':ref:`%s `' % (f['name'], f['name']) for f in funcs]) rst += ', '.join(funcs) rst += '.\n\n' for h, funcs in sorted(hooks.items()): rst += '.. _commands-%s:\n\n' % h rst += 'Commands supported by `%s` hook library: ' % h funcs = sorted([ ':ref:`%s `' % (f['name'], f['name']) for f in funcs]) rst += ', '.join(funcs) rst += '.\n\n' for func in sorted(apis.values(), key=lambda f: f['name']): # "name" is visible in the ARM. "real_name" is used to provide links # to commands. Keep both even if they're the same for when you want to # make changes to "name" to change the way it's seen in the ARM. name = func['name'] real_name = func['name'] rst += '.. _ref-%s:\n\n' % real_name rst += name + '\n' rst += '-' * len(name) + '\n\n' # command overview for brief_line in func['brief']: rst += '%s\n' % brief_line rst += '\n' # command can be issued to the following daemons rst += 'Supported by: ' rst += ', '.join(sorted([':ref:`%s `' % (dm, dm) for dm in func['support']])) rst += '\n\n' # availability rst += 'Availability: %s ' % func['avail'] rst += '(:ref:`%s ` hook library)' % (func['hook'], func['hook']) if 'hook' in func else '(built-in)' rst += '\n\n' # access try: access = func['access'] except: print('\naccess missing in %s\n\n' % name) raise if not access in ['read', 'write']: print('\nUnknown access %s in %s\n\n' % (access, name)) raise ValueError('access must be read or write') rst += 'Access: %s *(parameter ignored in this Kea version)* \n\n' % access # description and examples rst += 'Description and examples: see :ref:`%s command `\n\n' % (name, real_name) # command syntax rst += 'Command syntax:\n\n' rst += '::\n\n' if 'cmd-syntax' in func: cmd_syntaxes = [func['cmd-syntax']] if isinstance(cmd_syntaxes, dict): cmd_syntaxes = [cmd_syntax] for cmd_syntax in cmd_syntaxes: if 'comment' in cmd_syntax: rst += cmd_syntax['comment'] rst += '\n\n' del cmd_syntax['comment'] for line in cmd_syntax: rst += ' %s\n' % line else: rst += ' {\n' rst += ' "command": \"%s\"\n' % name rst += ' }' rst += '\n\n' if 'cmd-comment' in func: for l in func['cmd-comment']: rst += "%s\n" % l rst += '\n' # response syntax rst += 'Response syntax:\n\n' rst += '::\n\n' if 'resp-syntax' in func: resp_syntaxes = [func['resp-syntax']] if isinstance(resp_syntaxes, dict): resp_syntaxes = [resp_syntax] for resp_syntax in resp_syntaxes: for line in resp_syntax: rst += ' %s\n' % line else: rst += ' {\n' rst += ' "result": ,\n' rst += ' "text": ""\n' rst += ' }' rst += '\n\n' if 'resp-comment' in func: for resp_comment_line in func['resp-comment']: rst += "%s\n" % resp_comment_line rst += '\n\n' else: rst += 'Result is an integer representation of the status. Currently supported statuses are:\n\n' rst += '- 0 - success\n' rst += '- 1 - error\n' rst += '- 2 - unsupported\n' rst += '- 3 - empty (command was completed successfully, but no data was affected or returned)\n\n' return rst def generate(in_files, out_file): apis = read_input_files(in_files) rst = generate_rst(apis) if out_file: with open(out_file, 'w') as f: f.write(rst) print('Wrote generated RST content to: %s' % out_file) else: print(rst) def main(): args = parse_args() generate(args.files, args.output) if __name__ == '__main__': main() kea-2.0.2/doc/sphinx/mes_files.mk0000644000175000017500000000341414206773363013566 00000000000000mes_files += $(top_srcdir)/src/hooks/dhcp/flex_option/flex_option_messages.mes mes_files += $(top_srcdir)/src/hooks/dhcp/bootp/bootp_messages.mes mes_files += $(top_srcdir)/src/hooks/dhcp/mysql_cb/mysql_cb_messages.mes mes_files += $(top_srcdir)/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes mes_files += $(top_srcdir)/src/hooks/dhcp/high_availability/ha_messages.mes mes_files += $(top_srcdir)/src/hooks/dhcp/stat_cmds/stat_cmds_messages.mes mes_files += $(top_srcdir)/src/hooks/dhcp/user_chk/user_chk_messages.mes mes_files += $(top_srcdir)/src/lib/config/config_messages.mes mes_files += $(top_srcdir)/src/lib/hooks/hooks_messages.mes mes_files += $(top_srcdir)/src/lib/dhcpsrv/dhcpsrv_messages.mes mes_files += $(top_srcdir)/src/lib/dhcpsrv/alloc_engine_messages.mes mes_files += $(top_srcdir)/src/lib/dhcpsrv/hosts_messages.mes mes_files += $(top_srcdir)/src/lib/http/auth_messages.mes mes_files += $(top_srcdir)/src/lib/http/http_messages.mes mes_files += $(top_srcdir)/src/lib/dhcp_ddns/dhcp_ddns_messages.mes mes_files += $(top_srcdir)/src/lib/database/db_messages.mes mes_files += $(top_srcdir)/src/lib/log/log_messages.mes mes_files += $(top_srcdir)/src/lib/log/logimpl_messages.mes mes_files += $(top_srcdir)/src/lib/log/tests/log_test_messages.mes mes_files += $(top_srcdir)/src/lib/process/process_messages.mes mes_files += $(top_srcdir)/src/lib/asiodns/asiodns_messages.mes mes_files += $(top_srcdir)/src/lib/eval/eval_messages.mes mes_files += $(top_srcdir)/src/lib/d2srv/d2_messages.mes mes_files += $(top_srcdir)/src/bin/dhcp4/dhcp4_messages.mes mes_files += $(top_srcdir)/src/bin/agent/ca_messages.mes mes_files += $(top_srcdir)/src/bin/dhcp6/dhcp6_messages.mes mes_files += $(top_srcdir)/src/bin/lfc/lfc_messages.mes mes_files += $(top_srcdir)/src/bin/netconf/netconf_messages.mes kea-2.0.2/doc/sphinx/mes2doc.py0000755000175000017500000000725214206773363013204 00000000000000#!/usr/bin/env python3 # Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC") # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http:#mozilla.org/MPL/2.0/. # Produce System Messages Manual # # This tool reads all the message files given on the command line. # It pulls all the messages and description out, sorts them by # message ID, and writes them out as a single (formatted) file. # # Invocation: # The code is invoked using the command line: # # system_messages.py [-o ] # # If no output file is specified, output is written to stdout. # The produced format is ReStructuredText. import re import argparse def parse_args(): parser = argparse.ArgumentParser(description='Convert set of *.mes files to .rst documentation format') parser.add_argument('-o', '--output', help='Output file name (default to stdout).') parser.add_argument('files', help='Input .mes files.', nargs='+') args = parser.parse_args() return args def read_input_files(files): messages = {} for f in files: with open(f) as fp: print("Processing %s" % f) namespace = None msg_descr = None msg_id = None msg_text = None for line in fp.readlines(): line = line.strip() if not line or line.startswith('#'): pass elif line.startswith('$'): pass elif line.startswith('%'): # end previous message if msg_id is not None: section = msg_id.split('_')[0] messages[msg_id] = (section, msg_id, msg_text, msg_descr) # start next message m = re.search('^%\s?([A-Z0-9_]+)\s+(.*)', line); msg_id, msg_text = m.groups() msg_descr = [] else: msg_descr.append(line) return messages def generate_rst(messages): rst = '''.. _kea-messages: ################### Kea Messages Manual ################### Kea is an open source implementation of the Dynamic Host Configuration Protocol (DHCP) servers, developed and maintained by Internet Systems Consortium (ISC). This is the reference guide for Kea version |release|. Links to the most up-to-date version of this document (in PDF, HTML, and plain text formats), along with other useful information about Kea, can be found in ISC's `Knowledgebase `_. Please note that in the messages below, the percent sign ("%") followed by a number is used to indicate a placeholder for data that is provided by the Kea code during its operation. .. toctree:: :numbered: :maxdepth: 5 ''' prev_section = None for _, msg in sorted(messages.items()): section, msg_id, msg_text, msg_descr = msg if section != prev_section: prev_section = section rst += section + '\n' rst += '~' * len(section) + '\n\n' rst += '**' + msg_id + '**\n\n' rst += msg_text + '\n\n' rst += ''.join([' ' + l + '\n' for l in msg_descr]) rst += '\n' return rst def generate(in_files, out_file): messages = read_input_files(in_files) rst = generate_rst(messages) if out_file: with open(out_file, 'w') as f: f.write(rst) print('Wrote generated RST content to: %s' % out_file) else: print(rst) def main(): args = parse_args() generate(args.files, args.output) if __name__ == '__main__': main() kea-2.0.2/doc/sphinx/grammar/0000755000175000017500000000000014206773520012766 500000000000000kea-2.0.2/doc/sphinx/grammar/grammar-netconf-parser.rst0000644000175000017500000001272414206773363020025 00000000000000 Grammar generated on 2021-12-14 13:12. See Chapter :ref:`netconf` for an explanation. .. code-block:: BNF :linenos: Grammar $accept ::= start EOF start ::= START_JSON json start ::= START_NETCONF netconf_syntax_map start ::= START_SUB_NETCONF sub_netconf sub_netconf ::= "{" global_params "}" json ::= value value ::= INTEGER | FLOAT | BOOLEAN | STRING | NULL | map | list_generic map ::= "{" map_content "}" map_value ::= map map_content ::= | not_empty_map not_empty_map ::= STRING ":" value | not_empty_map "," STRING ":" value list_generic ::= "[" list_content "]" list_content ::= | not_empty_list not_empty_list ::= value | not_empty_list "," value unknown_map_entry ::= STRING ":" netconf_syntax_map ::= "{" global_object "}" global_object ::= "Netconf" ":" "{" global_params "}" global_params ::= | not_empty_global_params not_empty_global_params ::= global_param | not_empty_global_params "," global_param global_param ::= boot_update | subscribe_changes | validate_changes | managed_servers | hooks_libraries | loggers | user_context | comment | unknown_map_entry boot_update ::= "boot-update" ":" BOOLEAN subscribe_changes ::= "subscribe-changes" ":" BOOLEAN validate_changes ::= "validate-changes" ":" BOOLEAN user_context ::= "user-context" ":" map_value comment ::= "comment" ":" STRING hooks_libraries ::= "hooks-libraries" ":" "[" hooks_libraries_list "]" hooks_libraries_list ::= | not_empty_hooks_libraries_list not_empty_hooks_libraries_list ::= hooks_library | not_empty_hooks_libraries_list "," hooks_library hooks_library ::= "{" hooks_params "}" hooks_params ::= hooks_param | hooks_params "," hooks_param | unknown_map_entry hooks_param ::= library | parameters library ::= "library" ":" STRING parameters ::= "parameters" ":" map_value managed_servers ::= "managed-servers" ":" "{" servers_entries "}" servers_entries ::= | not_empty_servers_entries not_empty_servers_entries ::= server_entry | not_empty_servers_entries "," server_entry server_entry ::= dhcp4_server | dhcp6_server | d2_server | ca_server | unknown_map_entry dhcp4_server ::= "dhcp4" ":" "{" managed_server_params "}" dhcp6_server ::= "dhcp6" ":" "{" managed_server_params "}" d2_server ::= "d2" ":" "{" managed_server_params "}" ca_server ::= "ca" ":" "{" managed_server_params "}" managed_server_params ::= managed_server_param | managed_server_params "," managed_server_param managed_server_param ::= model | boot_update | subscribe_changes | validate_changes | control_socket | user_context | comment | unknown_map_entry model ::= "model" ":" STRING control_socket ::= "control-socket" ":" "{" control_socket_params "}" control_socket_params ::= control_socket_param | control_socket_params "," control_socket_param control_socket_param ::= socket_type | socket_name | socket_url | user_context | comment | unknown_map_entry socket_type ::= "socket-type" ":" socket_type_value socket_type_value ::= "unix" | "http" | "stdout" socket_name ::= "socket-name" ":" STRING socket_url ::= "socket-url" ":" STRING loggers ::= "loggers" ":" "[" loggers_entries "]" loggers_entries ::= logger_entry | loggers_entries "," logger_entry logger_entry ::= "{" logger_params "}" logger_params ::= logger_param | logger_params "," logger_param logger_param ::= name | output_options_list | debuglevel | severity | user_context | comment | unknown_map_entry name ::= "name" ":" STRING debuglevel ::= "debuglevel" ":" INTEGER severity ::= "severity" ":" STRING output_options_list ::= "output_options" ":" "[" output_options_list_content "]" output_options_list_content ::= output_entry | output_options_list_content "," output_entry output_entry ::= "{" output_params_list "}" output_params_list ::= output_params | output_params_list "," output_params output_params ::= output | flush | maxsize | maxver | pattern output ::= "output" ":" STRING flush ::= "flush" ":" BOOLEAN maxsize ::= "maxsize" ":" INTEGER maxver ::= "maxver" ":" INTEGER pattern ::= "pattern" ":" STRING kea-2.0.2/doc/sphinx/grammar/grammar-d2-parser.rst0000644000175000017500000001734614206773363016703 00000000000000 Grammar generated on 2021-12-14 13:12. See Chapter :ref:`dhcp-ddns-server` for an explanation. .. code-block:: BNF :linenos: Grammar $accept ::= start EOF start ::= TOPLEVEL_JSON sub_json start ::= TOPLEVEL_DHCPDDNS syntax_map start ::= SUB_DHCPDDNS sub_dhcpddns start ::= SUB_TSIG_KEY sub_tsig_key start ::= SUB_TSIG_KEYS sub_tsig_keys start ::= SUB_DDNS_DOMAIN sub_ddns_domain start ::= SUB_DDNS_DOMAINS sub_ddns_domains start ::= SUB_DNS_SERVER sub_dns_server start ::= SUB_DNS_SERVERS sub_dns_servers start ::= SUB_HOOKS_LIBRARY sub_hooks_library value ::= INTEGER | FLOAT | BOOLEAN | STRING | NULL | map2 | list_generic sub_json ::= value map2 ::= "{" map_content "}" map_value ::= map2 map_content ::= | not_empty_map not_empty_map ::= STRING ":" value | not_empty_map "," STRING ":" value list_generic ::= "[" list_content "]" list_content ::= | not_empty_list not_empty_list ::= value | not_empty_list "," value unknown_map_entry ::= STRING ":" syntax_map ::= "{" global_object "}" global_object ::= "DhcpDdns" ":" "{" dhcpddns_params "}" sub_dhcpddns ::= "{" dhcpddns_params "}" dhcpddns_params ::= dhcpddns_param | dhcpddns_params "," dhcpddns_param dhcpddns_param ::= ip_address | port | dns_server_timeout | ncr_protocol | ncr_format | forward_ddns | reverse_ddns | tsig_keys | control_socket | hooks_libraries | loggers | user_context | comment | unknown_map_entry ip_address ::= "ip-address" ":" STRING port ::= "port" ":" INTEGER dns_server_timeout ::= "dns-server-timeout" ":" INTEGER ncr_protocol ::= "ncr-protocol" ":" ncr_protocol_value ncr_protocol_value ::= "UDP" | "TCP" ncr_format ::= "ncr-format" ":" "JSON" user_context ::= "user-context" ":" map_value comment ::= "comment" ":" STRING forward_ddns ::= "forward-ddns" ":" "{" ddns_mgr_params "}" reverse_ddns ::= "reverse-ddns" ":" "{" ddns_mgr_params "}" ddns_mgr_params ::= | not_empty_ddns_mgr_params not_empty_ddns_mgr_params ::= ddns_mgr_param | ddns_mgr_params "," ddns_mgr_param ddns_mgr_param ::= ddns_domains | unknown_map_entry ddns_domains ::= "ddns-domains" ":" "[" ddns_domain_list "]" sub_ddns_domains ::= "[" ddns_domain_list "]" ddns_domain_list ::= | not_empty_ddns_domain_list not_empty_ddns_domain_list ::= ddns_domain | not_empty_ddns_domain_list "," ddns_domain ddns_domain ::= "{" ddns_domain_params "}" sub_ddns_domain ::= "{" ddns_domain_params "}" ddns_domain_params ::= ddns_domain_param | ddns_domain_params "," ddns_domain_param ddns_domain_param ::= ddns_domain_name | ddns_key_name | dns_servers | user_context | comment | unknown_map_entry ddns_domain_name ::= "name" ":" STRING ddns_key_name ::= "key-name" ":" STRING dns_servers ::= "dns-servers" ":" "[" dns_server_list "]" sub_dns_servers ::= "[" dns_server_list "]" dns_server_list ::= dns_server | dns_server_list "," dns_server dns_server ::= "{" dns_server_params "}" sub_dns_server ::= "{" dns_server_params "}" dns_server_params ::= dns_server_param | dns_server_params "," dns_server_param dns_server_param ::= dns_server_hostname | dns_server_ip_address | dns_server_port | ddns_key_name | user_context | comment | unknown_map_entry dns_server_hostname ::= "hostname" ":" STRING dns_server_ip_address ::= "ip-address" ":" STRING dns_server_port ::= "port" ":" INTEGER tsig_keys ::= "tsig-keys" ":" "[" tsig_keys_list "]" sub_tsig_keys ::= "[" tsig_keys_list "]" tsig_keys_list ::= | not_empty_tsig_keys_list not_empty_tsig_keys_list ::= tsig_key | not_empty_tsig_keys_list "," tsig_key tsig_key ::= "{" tsig_key_params "}" sub_tsig_key ::= "{" tsig_key_params "}" tsig_key_params ::= tsig_key_param | tsig_key_params "," tsig_key_param tsig_key_param ::= tsig_key_name | tsig_key_algorithm | tsig_key_digest_bits | tsig_key_secret | user_context | comment | unknown_map_entry tsig_key_name ::= "name" ":" STRING tsig_key_algorithm ::= "algorithm" ":" STRING tsig_key_digest_bits ::= "digest-bits" ":" INTEGER tsig_key_secret ::= "secret" ":" STRING control_socket ::= "control-socket" ":" "{" control_socket_params "}" control_socket_params ::= control_socket_param | control_socket_params "," control_socket_param control_socket_param ::= control_socket_type | control_socket_name | user_context | comment | unknown_map_entry control_socket_type ::= "socket-type" ":" STRING control_socket_name ::= "socket-name" ":" STRING hooks_libraries ::= "hooks-libraries" ":" "[" hooks_libraries_list "]" hooks_libraries_list ::= | not_empty_hooks_libraries_list not_empty_hooks_libraries_list ::= hooks_library | not_empty_hooks_libraries_list "," hooks_library hooks_library ::= "{" hooks_params "}" sub_hooks_library ::= "{" hooks_params "}" hooks_params ::= hooks_param | hooks_params "," hooks_param | unknown_map_entry hooks_param ::= library | parameters library ::= "library" ":" STRING parameters ::= "parameters" ":" map_value loggers ::= "loggers" ":" "[" loggers_entries "]" loggers_entries ::= logger_entry | loggers_entries "," logger_entry logger_entry ::= "{" logger_params "}" logger_params ::= logger_param | logger_params "," logger_param logger_param ::= name | output_options_list | debuglevel | severity | user_context | comment | unknown_map_entry name ::= "name" ":" STRING debuglevel ::= "debuglevel" ":" INTEGER severity ::= "severity" ":" STRING output_options_list ::= "output_options" ":" "[" output_options_list_content "]" output_options_list_content ::= output_entry | output_options_list_content "," output_entry output_entry ::= "{" output_params_list "}" output_params_list ::= output_params | output_params_list "," output_params output_params ::= output | flush | maxsize | maxver | pattern output ::= "output" ":" STRING flush ::= "flush" ":" BOOLEAN maxsize ::= "maxsize" ":" INTEGER maxver ::= "maxver" ":" INTEGER pattern ::= "pattern" ":" STRING kea-2.0.2/doc/sphinx/grammar/grammar-dhcp4-parser.rst0000644000175000017500000007247714206773363017406 00000000000000 Grammar generated on 2021-12-14 13:12. See Chapter :ref:`dhcp4` for an explanation. .. code-block:: BNF :linenos: Grammar $accept ::= start EOF start ::= TOPLEVEL_JSON sub_json start ::= TOPLEVEL_DHCP4 syntax_map start ::= SUB_DHCP4 sub_dhcp4 start ::= SUB_INTERFACES4 sub_interfaces4 start ::= SUB_SUBNET4 sub_subnet4 start ::= SUB_POOL4 sub_pool4 start ::= SUB_RESERVATION sub_reservation start ::= SUB_OPTION_DEFS sub_option_def_list start ::= SUB_OPTION_DEF sub_option_def start ::= SUB_OPTION_DATA sub_option_data start ::= SUB_HOOKS_LIBRARY sub_hooks_library start ::= SUB_DHCP_DDNS sub_dhcp_ddns start ::= SUB_CONFIG_CONTROL sub_config_control value ::= INTEGER | FLOAT | BOOLEAN | STRING | NULL | map2 | list_generic sub_json ::= value map2 ::= "{" map_content "}" map_value ::= map2 map_content ::= | not_empty_map not_empty_map ::= STRING ":" value | not_empty_map "," STRING ":" value list_generic ::= "[" list_content "]" list_content ::= | not_empty_list not_empty_list ::= value | not_empty_list "," value list_strings ::= "[" list_strings_content "]" list_strings_content ::= | not_empty_list_strings not_empty_list_strings ::= STRING | not_empty_list_strings "," STRING unknown_map_entry ::= STRING ":" syntax_map ::= "{" global_object "}" global_object ::= "Dhcp4" ":" "{" global_params "}" sub_dhcp4 ::= "{" global_params "}" global_params ::= global_param | global_params "," global_param global_param ::= valid_lifetime | min_valid_lifetime | max_valid_lifetime | renew_timer | rebind_timer | decline_probation_period | subnet4_list | shared_networks | interfaces_config | lease_database | hosts_database | hosts_databases | host_reservation_identifiers | client_classes | option_def_list | option_data_list | hooks_libraries | expired_leases_processing | dhcp4o6_port | control_socket | dhcp_queue_control | dhcp_ddns | echo_client_id | match_client_id | authoritative | next_server | server_hostname | boot_file_name | user_context | comment | sanity_checks | reservations | config_control | server_tag | reservation_mode | reservations_global | reservations_in_subnet | reservations_out_of_pool | calculate_tee_times | t1_percent | t2_percent | cache_threshold | cache_max_age | loggers | hostname_char_set | hostname_char_replacement | ddns_send_updates | ddns_override_no_update | ddns_override_client_update | ddns_replace_client_name | ddns_generated_prefix | ddns_qualifying_suffix | ddns_update_on_renew | ddns_use_conflict_resolution | store_extended_info | statistic_default_sample_count | statistic_default_sample_age | dhcp_multi_threading | ip_reservations_unique | compatibility | parked_packet_limit | unknown_map_entry valid_lifetime ::= "valid-lifetime" ":" INTEGER min_valid_lifetime ::= "min-valid-lifetime" ":" INTEGER max_valid_lifetime ::= "max-valid-lifetime" ":" INTEGER renew_timer ::= "renew-timer" ":" INTEGER rebind_timer ::= "rebind-timer" ":" INTEGER calculate_tee_times ::= "calculate-tee-times" ":" BOOLEAN t1_percent ::= "t1-percent" ":" FLOAT t2_percent ::= "t2-percent" ":" FLOAT cache_threshold ::= "cache-threshold" ":" FLOAT cache_max_age ::= "cache-max-age" ":" INTEGER decline_probation_period ::= "decline-probation-period" ":" INTEGER server_tag ::= "server-tag" ":" STRING parked_packet_limit ::= "parked-packet-limit" ":" INTEGER echo_client_id ::= "echo-client-id" ":" BOOLEAN match_client_id ::= "match-client-id" ":" BOOLEAN authoritative ::= "authoritative" ":" BOOLEAN ddns_send_updates ::= "ddns-send-updates" ":" BOOLEAN ddns_override_no_update ::= "ddns-override-no-update" ":" BOOLEAN ddns_override_client_update ::= "ddns-override-client-update" ":" BOOLEAN ddns_replace_client_name ::= "ddns-replace-client-name" ":" ddns_replace_client_name_value ddns_replace_client_name_value ::= "when-present" | "never" | "always" | "when-not-present" | BOOLEAN ddns_generated_prefix ::= "ddns-generated-prefix" ":" STRING ddns_qualifying_suffix ::= "ddns-qualifying-suffix" ":" STRING ddns_update_on_renew ::= "ddns-update-on-renew" ":" BOOLEAN ddns_use_conflict_resolution ::= "ddns-use-conflict-resolution" ":" BOOLEAN hostname_char_set ::= "hostname-char-set" ":" STRING hostname_char_replacement ::= "hostname-char-replacement" ":" STRING store_extended_info ::= "store-extended-info" ":" BOOLEAN statistic_default_sample_count ::= "statistic-default-sample-count" ":" INTEGER statistic_default_sample_age ::= "statistic-default-sample-age" ":" INTEGER ip_reservations_unique ::= "ip-reservations-unique" ":" BOOLEAN interfaces_config ::= "interfaces-config" ":" "{" interfaces_config_params "}" interfaces_config_params ::= interfaces_config_param | interfaces_config_params "," interfaces_config_param interfaces_config_param ::= interfaces_list | dhcp_socket_type | outbound_interface | re_detect | user_context | comment | unknown_map_entry sub_interfaces4 ::= "{" interfaces_config_params "}" interfaces_list ::= "interfaces" ":" list_strings dhcp_socket_type ::= "dhcp-socket-type" ":" socket_type socket_type ::= "raw" | "udp" outbound_interface ::= "outbound-interface" ":" outbound_interface_value outbound_interface_value ::= "same-as-inbound" | "use-routing" re_detect ::= "re-detect" ":" BOOLEAN lease_database ::= "lease-database" ":" "{" database_map_params "}" sanity_checks ::= "sanity-checks" ":" "{" sanity_checks_params "}" sanity_checks_params ::= sanity_checks_param | sanity_checks_params "," sanity_checks_param sanity_checks_param ::= lease_checks lease_checks ::= "lease-checks" ":" STRING hosts_database ::= "hosts-database" ":" "{" database_map_params "}" hosts_databases ::= "hosts-databases" ":" "[" database_list "]" database_list ::= | not_empty_database_list not_empty_database_list ::= database | not_empty_database_list "," database database ::= "{" database_map_params "}" database_map_params ::= database_map_param | database_map_params "," database_map_param database_map_param ::= database_type | user | password | host | port | name | persist | lfc_interval | readonly | connect_timeout | contact_points | max_reconnect_tries | reconnect_wait_time | on_fail | request_timeout | tcp_keepalive | tcp_nodelay | keyspace | consistency | serial_consistency | max_row_errors | unknown_map_entry database_type ::= "type" ":" db_type db_type ::= "memfile" | "mysql" | "postgresql" | "cql" user ::= "user" ":" STRING password ::= "password" ":" STRING host ::= "host" ":" STRING port ::= "port" ":" INTEGER name ::= "name" ":" STRING persist ::= "persist" ":" BOOLEAN lfc_interval ::= "lfc-interval" ":" INTEGER readonly ::= "readonly" ":" BOOLEAN connect_timeout ::= "connect-timeout" ":" INTEGER request_timeout ::= "request-timeout" ":" INTEGER tcp_keepalive ::= "tcp-keepalive" ":" INTEGER tcp_nodelay ::= "tcp-nodelay" ":" BOOLEAN contact_points ::= "contact-points" ":" STRING keyspace ::= "keyspace" ":" STRING consistency ::= "consistency" ":" STRING serial_consistency ::= "serial-consistency" ":" STRING max_reconnect_tries ::= "max-reconnect-tries" ":" INTEGER reconnect_wait_time ::= "reconnect-wait-time" ":" INTEGER on_fail ::= "on-fail" ":" on_fail_mode on_fail_mode ::= "stop-retry-exit" | "serve-retry-exit" | "serve-retry-continue" max_row_errors ::= "max-row-errors" ":" INTEGER host_reservation_identifiers ::= "host-reservation-identifiers" ":" "[" host_reservation_identifiers_list "]" host_reservation_identifiers_list ::= host_reservation_identifier | host_reservation_identifiers_list "," host_reservation_identifier host_reservation_identifier ::= duid_id | hw_address_id | circuit_id | client_id | flex_id duid_id ::= "duid" hw_address_id ::= "hw-address" circuit_id ::= "circuit-id" client_id ::= "client-id" flex_id ::= "flex-id" dhcp_multi_threading ::= "multi-threading" ":" "{" multi_threading_params "}" multi_threading_params ::= multi_threading_param | multi_threading_params "," multi_threading_param multi_threading_param ::= enable_multi_threading | thread_pool_size | packet_queue_size | user_context | comment | unknown_map_entry enable_multi_threading ::= "enable-multi-threading" ":" BOOLEAN thread_pool_size ::= "thread-pool-size" ":" INTEGER packet_queue_size ::= "packet-queue-size" ":" INTEGER hooks_libraries ::= "hooks-libraries" ":" "[" hooks_libraries_list "]" hooks_libraries_list ::= | not_empty_hooks_libraries_list not_empty_hooks_libraries_list ::= hooks_library | not_empty_hooks_libraries_list "," hooks_library hooks_library ::= "{" hooks_params "}" sub_hooks_library ::= "{" hooks_params "}" hooks_params ::= hooks_param | hooks_params "," hooks_param | unknown_map_entry hooks_param ::= library | parameters library ::= "library" ":" STRING parameters ::= "parameters" ":" map_value expired_leases_processing ::= "expired-leases-processing" ":" "{" expired_leases_params "}" expired_leases_params ::= expired_leases_param | expired_leases_params "," expired_leases_param expired_leases_param ::= reclaim_timer_wait_time | flush_reclaimed_timer_wait_time | hold_reclaimed_time | max_reclaim_leases | max_reclaim_time | unwarned_reclaim_cycles reclaim_timer_wait_time ::= "reclaim-timer-wait-time" ":" INTEGER flush_reclaimed_timer_wait_time ::= "flush-reclaimed-timer-wait-time" ":" INTEGER hold_reclaimed_time ::= "hold-reclaimed-time" ":" INTEGER max_reclaim_leases ::= "max-reclaim-leases" ":" INTEGER max_reclaim_time ::= "max-reclaim-time" ":" INTEGER unwarned_reclaim_cycles ::= "unwarned-reclaim-cycles" ":" INTEGER subnet4_list ::= "subnet4" ":" "[" subnet4_list_content "]" subnet4_list_content ::= | not_empty_subnet4_list not_empty_subnet4_list ::= subnet4 | not_empty_subnet4_list "," subnet4 subnet4 ::= "{" subnet4_params "}" sub_subnet4 ::= "{" subnet4_params "}" subnet4_params ::= subnet4_param | subnet4_params "," subnet4_param subnet4_param ::= valid_lifetime | min_valid_lifetime | max_valid_lifetime | renew_timer | rebind_timer | option_data_list | pools_list | subnet | interface | id | client_class | require_client_classes | reservations | reservation_mode | reservations_global | reservations_in_subnet | reservations_out_of_pool | relay | match_client_id | authoritative | next_server | server_hostname | boot_file_name | subnet_4o6_interface | subnet_4o6_interface_id | subnet_4o6_subnet | user_context | comment | calculate_tee_times | t1_percent | t2_percent | cache_threshold | cache_max_age | ddns_send_updates | ddns_override_no_update | ddns_override_client_update | ddns_replace_client_name | ddns_generated_prefix | ddns_qualifying_suffix | ddns_update_on_renew | ddns_use_conflict_resolution | hostname_char_set | hostname_char_replacement | store_extended_info | unknown_map_entry subnet ::= "subnet" ":" STRING subnet_4o6_interface ::= "4o6-interface" ":" STRING subnet_4o6_interface_id ::= "4o6-interface-id" ":" STRING subnet_4o6_subnet ::= "4o6-subnet" ":" STRING interface ::= "interface" ":" STRING client_class ::= "client-class" ":" STRING require_client_classes ::= "require-client-classes" ":" list_strings reservations_global ::= "reservations-global" ":" BOOLEAN reservations_in_subnet ::= "reservations-in-subnet" ":" BOOLEAN reservations_out_of_pool ::= "reservations-out-of-pool" ":" BOOLEAN reservation_mode ::= "reservation-mode" ":" hr_mode hr_mode ::= "disabled" | "out-of-pool" | "global" | "all" id ::= "id" ":" INTEGER shared_networks ::= "shared-networks" ":" "[" shared_networks_content "]" shared_networks_content ::= | shared_networks_list shared_networks_list ::= shared_network | shared_networks_list "," shared_network shared_network ::= "{" shared_network_params "}" shared_network_params ::= shared_network_param | shared_network_params "," shared_network_param shared_network_param ::= name | subnet4_list | interface | renew_timer | rebind_timer | option_data_list | match_client_id | authoritative | next_server | server_hostname | boot_file_name | relay | reservation_mode | reservations_global | reservations_in_subnet | reservations_out_of_pool | client_class | require_client_classes | valid_lifetime | min_valid_lifetime | max_valid_lifetime | user_context | comment | calculate_tee_times | t1_percent | t2_percent | cache_threshold | cache_max_age | ddns_send_updates | ddns_override_no_update | ddns_override_client_update | ddns_replace_client_name | ddns_generated_prefix | ddns_qualifying_suffix | ddns_update_on_renew | ddns_use_conflict_resolution | hostname_char_set | hostname_char_replacement | store_extended_info | unknown_map_entry option_def_list ::= "option-def" ":" "[" option_def_list_content "]" sub_option_def_list ::= "{" option_def_list "}" option_def_list_content ::= | not_empty_option_def_list not_empty_option_def_list ::= option_def_entry | not_empty_option_def_list "," option_def_entry option_def_entry ::= "{" option_def_params "}" sub_option_def ::= "{" option_def_params "}" option_def_params ::= | not_empty_option_def_params not_empty_option_def_params ::= option_def_param | not_empty_option_def_params "," option_def_param option_def_param ::= option_def_name | option_def_code | option_def_type | option_def_record_types | option_def_space | option_def_encapsulate | option_def_array | user_context | comment | unknown_map_entry option_def_name ::= name code ::= "code" ":" INTEGER option_def_code ::= code option_def_type ::= "type" ":" STRING option_def_record_types ::= "record-types" ":" STRING space ::= "space" ":" STRING option_def_space ::= space option_def_encapsulate ::= "encapsulate" ":" STRING option_def_array ::= "array" ":" BOOLEAN option_data_list ::= "option-data" ":" "[" option_data_list_content "]" option_data_list_content ::= | not_empty_option_data_list not_empty_option_data_list ::= option_data_entry | not_empty_option_data_list "," option_data_entry option_data_entry ::= "{" option_data_params "}" sub_option_data ::= "{" option_data_params "}" option_data_params ::= | not_empty_option_data_params not_empty_option_data_params ::= option_data_param | not_empty_option_data_params "," option_data_param option_data_param ::= option_data_name | option_data_data | option_data_code | option_data_space | option_data_csv_format | option_data_always_send | user_context | comment | unknown_map_entry option_data_name ::= name option_data_data ::= "data" ":" STRING option_data_code ::= code option_data_space ::= space option_data_csv_format ::= "csv-format" ":" BOOLEAN option_data_always_send ::= "always-send" ":" BOOLEAN pools_list ::= "pools" ":" "[" pools_list_content "]" pools_list_content ::= | not_empty_pools_list not_empty_pools_list ::= pool_list_entry | not_empty_pools_list "," pool_list_entry pool_list_entry ::= "{" pool_params "}" sub_pool4 ::= "{" pool_params "}" pool_params ::= pool_param | pool_params "," pool_param pool_param ::= pool_entry | option_data_list | client_class | require_client_classes | user_context | comment | unknown_map_entry pool_entry ::= "pool" ":" STRING user_context ::= "user-context" ":" map_value comment ::= "comment" ":" STRING reservations ::= "reservations" ":" "[" reservations_list "]" reservations_list ::= | not_empty_reservations_list not_empty_reservations_list ::= reservation | not_empty_reservations_list "," reservation reservation ::= "{" reservation_params "}" sub_reservation ::= "{" reservation_params "}" reservation_params ::= | not_empty_reservation_params not_empty_reservation_params ::= reservation_param | not_empty_reservation_params "," reservation_param reservation_param ::= duid | reservation_client_classes | client_id_value | circuit_id_value | flex_id_value | ip_address | hw_address | hostname | option_data_list | next_server | server_hostname | boot_file_name | user_context | comment | unknown_map_entry next_server ::= "next-server" ":" STRING server_hostname ::= "server-hostname" ":" STRING boot_file_name ::= "boot-file-name" ":" STRING ip_address ::= "ip-address" ":" STRING ip_addresses ::= "ip-addresses" ":" list_strings duid ::= "duid" ":" STRING hw_address ::= "hw-address" ":" STRING client_id_value ::= "client-id" ":" STRING circuit_id_value ::= "circuit-id" ":" STRING flex_id_value ::= "flex-id" ":" STRING hostname ::= "hostname" ":" STRING reservation_client_classes ::= "client-classes" ":" list_strings relay ::= "relay" ":" "{" relay_map "}" relay_map ::= ip_address | ip_addresses client_classes ::= "client-classes" ":" "[" client_classes_list "]" client_classes_list ::= client_class_entry | client_classes_list "," client_class_entry client_class_entry ::= "{" client_class_params "}" client_class_params ::= | not_empty_client_class_params not_empty_client_class_params ::= client_class_param | not_empty_client_class_params "," client_class_param client_class_param ::= client_class_name | client_class_test | only_if_required | option_def_list | option_data_list | next_server | server_hostname | boot_file_name | user_context | comment | unknown_map_entry | valid_lifetime | min_valid_lifetime | max_valid_lifetime client_class_name ::= name client_class_test ::= "test" ":" STRING only_if_required ::= "only-if-required" ":" BOOLEAN dhcp4o6_port ::= "dhcp4o6-port" ":" INTEGER control_socket ::= "control-socket" ":" "{" control_socket_params "}" control_socket_params ::= control_socket_param | control_socket_params "," control_socket_param control_socket_param ::= control_socket_type | control_socket_name | user_context | comment | unknown_map_entry control_socket_type ::= "socket-type" ":" STRING control_socket_name ::= "socket-name" ":" STRING dhcp_queue_control ::= "dhcp-queue-control" ":" "{" queue_control_params "}" queue_control_params ::= queue_control_param | queue_control_params "," queue_control_param queue_control_param ::= enable_queue | queue_type | capacity | user_context | comment | arbitrary_map_entry enable_queue ::= "enable-queue" ":" BOOLEAN queue_type ::= "queue-type" ":" STRING capacity ::= "capacity" ":" INTEGER arbitrary_map_entry ::= STRING ":" value dhcp_ddns ::= "dhcp-ddns" ":" "{" dhcp_ddns_params "}" sub_dhcp_ddns ::= "{" dhcp_ddns_params "}" dhcp_ddns_params ::= dhcp_ddns_param | dhcp_ddns_params "," dhcp_ddns_param dhcp_ddns_param ::= enable_updates | server_ip | server_port | sender_ip | sender_port | max_queue_size | ncr_protocol | ncr_format | dep_override_no_update | dep_override_client_update | dep_replace_client_name | dep_generated_prefix | dep_qualifying_suffix | dep_hostname_char_set | dep_hostname_char_replacement | user_context | comment | unknown_map_entry enable_updates ::= "enable-updates" ":" BOOLEAN server_ip ::= "server-ip" ":" STRING server_port ::= "server-port" ":" INTEGER sender_ip ::= "sender-ip" ":" STRING sender_port ::= "sender-port" ":" INTEGER max_queue_size ::= "max-queue-size" ":" INTEGER ncr_protocol ::= "ncr-protocol" ":" ncr_protocol_value ncr_protocol_value ::= "udp" | "tcp" ncr_format ::= "ncr-format" ":" "JSON" dep_qualifying_suffix ::= "qualifying-suffix" ":" STRING dep_override_no_update ::= "override-no-update" ":" BOOLEAN dep_override_client_update ::= "override-client-update" ":" BOOLEAN dep_replace_client_name ::= "replace-client-name" ":" ddns_replace_client_name_value dep_generated_prefix ::= "generated-prefix" ":" STRING dep_hostname_char_set ::= "hostname-char-set" ":" STRING dep_hostname_char_replacement ::= "hostname-char-replacement" ":" STRING config_control ::= "config-control" ":" "{" config_control_params "}" sub_config_control ::= "{" config_control_params "}" config_control_params ::= config_control_param | config_control_params "," config_control_param config_control_param ::= config_databases | config_fetch_wait_time config_databases ::= "config-databases" ":" "[" database_list "]" config_fetch_wait_time ::= "config-fetch-wait-time" ":" INTEGER loggers ::= "loggers" ":" "[" loggers_entries "]" loggers_entries ::= logger_entry | loggers_entries "," logger_entry logger_entry ::= "{" logger_params "}" logger_params ::= logger_param | logger_params "," logger_param logger_param ::= name | output_options_list | debuglevel | severity | user_context | comment | unknown_map_entry debuglevel ::= "debuglevel" ":" INTEGER severity ::= "severity" ":" STRING output_options_list ::= "output_options" ":" "[" output_options_list_content "]" output_options_list_content ::= output_entry | output_options_list_content "," output_entry output_entry ::= "{" output_params_list "}" output_params_list ::= output_params | output_params_list "," output_params output_params ::= output | flush | maxsize | maxver | pattern output ::= "output" ":" STRING flush ::= "flush" ":" BOOLEAN maxsize ::= "maxsize" ":" INTEGER maxver ::= "maxver" ":" INTEGER pattern ::= "pattern" ":" STRING compatibility ::= "compatibility" ":" "{" compatibility_params "}" compatibility_params ::= compatibility_param | compatibility_params "," compatibility_param compatibility_param ::= lenient_option_parsing | unknown_map_entry lenient_option_parsing ::= "lenient-option-parsing" ":" BOOLEAN kea-2.0.2/doc/sphinx/grammar/grammar-ca-parser.rst0000644000175000017500000001342514206773363016753 00000000000000 Grammar generated on 2021-12-14 13:12. See Chapter :ref:`kea-ctrl-agent` for an explanation. .. code-block:: BNF :linenos: Grammar $accept ::= start EOF start ::= START_JSON json start ::= START_AGENT agent_syntax_map start ::= START_SUB_AGENT sub_agent sub_agent ::= "{" global_params "}" json ::= value value ::= INTEGER | FLOAT | BOOLEAN | STRING | NULL | map | list_generic map ::= "{" map_content "}" map_value ::= map map_content ::= | not_empty_map not_empty_map ::= STRING ":" value | not_empty_map "," STRING ":" value list_generic ::= "[" list_content "]" list_content ::= | not_empty_list not_empty_list ::= value | not_empty_list "," value unknown_map_entry ::= STRING ":" agent_syntax_map ::= "{" global_object "}" global_object ::= "Control-agent" ":" "{" global_params "}" global_params ::= global_param | global_params "," global_param global_param ::= http_host | http_port | trust_anchor | cert_file | key_file | cert_required | authentication | control_sockets | hooks_libraries | loggers | user_context | comment | unknown_map_entry http_host ::= "http-host" ":" STRING http_port ::= "http-port" ":" INTEGER trust_anchor ::= "trust-anchor" ":" STRING cert_file ::= "cert-file" ":" STRING key_file ::= "key-file" ":" STRING cert_required ::= "cert-required" ":" BOOLEAN user_context ::= "user-context" ":" map_value comment ::= "comment" ":" STRING hooks_libraries ::= "hooks-libraries" ":" "[" hooks_libraries_list "]" hooks_libraries_list ::= | not_empty_hooks_libraries_list not_empty_hooks_libraries_list ::= hooks_library | not_empty_hooks_libraries_list "," hooks_library hooks_library ::= "{" hooks_params "}" hooks_params ::= hooks_param | hooks_params "," hooks_param | unknown_map_entry hooks_param ::= library | parameters library ::= "library" ":" STRING parameters ::= "parameters" ":" map_value control_sockets ::= "control-sockets" ":" "{" control_sockets_params "}" control_sockets_params ::= control_socket | control_sockets_params "," control_socket control_socket ::= dhcp4_server_socket | dhcp6_server_socket | d2_server_socket | unknown_map_entry dhcp4_server_socket ::= "dhcp4" ":" "{" control_socket_params "}" dhcp6_server_socket ::= "dhcp6" ":" "{" control_socket_params "}" d2_server_socket ::= "d2" ":" "{" control_socket_params "}" control_socket_params ::= control_socket_param | control_socket_params "," control_socket_param control_socket_param ::= socket_name | socket_type | user_context | comment | unknown_map_entry socket_name ::= "socket-name" ":" STRING socket_type ::= "socket-type" ":" socket_type_value socket_type_value ::= "unix" authentication ::= "authentication" ":" "{" auth_params "}" auth_params ::= auth_param | auth_params "," auth_param auth_param ::= auth_type | realm | clients | comment | user_context | unknown_map_entry auth_type ::= "type" ":" auth_type_value auth_type_value ::= "basic" realm ::= "realm" ":" STRING clients ::= "clients" ":" "[" clients_list "]" clients_list ::= | not_empty_clients_list not_empty_clients_list ::= basic_auth | not_empty_clients_list "," basic_auth basic_auth ::= "{" clients_params "}" clients_params ::= clients_param | clients_params "," clients_param clients_param ::= user | password | user_context | comment | unknown_map_entry user ::= "user" ":" STRING password ::= "password" ":" STRING loggers ::= "loggers" ":" "[" loggers_entries "]" loggers_entries ::= logger_entry | loggers_entries "," logger_entry logger_entry ::= "{" logger_params "}" logger_params ::= logger_param | logger_params "," logger_param logger_param ::= name | output_options_list | debuglevel | severity | user_context | comment | unknown_map_entry name ::= "name" ":" STRING debuglevel ::= "debuglevel" ":" INTEGER severity ::= "severity" ":" STRING output_options_list ::= "output_options" ":" "[" output_options_list_content "]" output_options_list_content ::= output_entry | output_options_list_content "," output_entry output_entry ::= "{" output_params_list "}" output_params_list ::= output_params | output_params_list "," output_params output_params ::= output | flush | maxsize | maxver | pattern output ::= "output" ":" STRING flush ::= "flush" ":" BOOLEAN maxsize ::= "maxsize" ":" INTEGER maxver ::= "maxver" ":" INTEGER pattern ::= "pattern" ":" STRING kea-2.0.2/doc/sphinx/grammar/grammar.rst0000644000175000017500000000232714206773363015077 00000000000000.. Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. .. _bnf-grammar: Kea Configuration File Syntax (BNF) =================================== Kea consists of several daemons, each with its own configuration syntax. The following sections provide a complete syntax of all possible parameters, written as Backus-Naur Form (BNF). See `wikipedia article on BNF `_ for background and explanation. BNF Grammar for DHCPv4 ---------------------- .. include:: grammar-dhcp4-parser.rst BNF Grammar for DHCPv6 ---------------------- .. include:: grammar-dhcp6-parser.rst BNF Grammar for Control Agent ----------------------------- .. include:: grammar-ca-parser.rst BNF Grammar for DHCP-DDNS ------------------------- .. include:: grammar-d2-parser.rst BNF Grammar for Kea-netconf --------------------------- .. include:: grammar-netconf-parser.rst kea-2.0.2/doc/sphinx/grammar/grammar-dhcp6-parser.rst0000644000175000017500000007513614206773363017403 00000000000000 Grammar generated on 2021-12-14 13:12. See Chapter :ref:`dhcp6` for an explanation. .. code-block:: BNF :linenos: Grammar $accept ::= start EOF start ::= TOPLEVEL_JSON sub_json start ::= TOPLEVEL_DHCP6 syntax_map start ::= SUB_DHCP6 sub_dhcp6 start ::= SUB_INTERFACES6 sub_interfaces6 start ::= SUB_SUBNET6 sub_subnet6 start ::= SUB_POOL6 sub_pool6 start ::= SUB_PD_POOL sub_pd_pool start ::= SUB_RESERVATION sub_reservation start ::= SUB_OPTION_DEFS sub_option_def_list start ::= SUB_OPTION_DEF sub_option_def start ::= SUB_OPTION_DATA sub_option_data start ::= SUB_HOOKS_LIBRARY sub_hooks_library start ::= SUB_DHCP_DDNS sub_dhcp_ddns start ::= SUB_CONFIG_CONTROL sub_config_control value ::= INTEGER | FLOAT | BOOLEAN | STRING | NULL | map2 | list_generic sub_json ::= value map2 ::= "{" map_content "}" map_value ::= map2 map_content ::= | not_empty_map not_empty_map ::= STRING ":" value | not_empty_map "," STRING ":" value list_generic ::= "[" list_content "]" list_content ::= | not_empty_list not_empty_list ::= value | not_empty_list "," value list_strings ::= "[" list_strings_content "]" list_strings_content ::= | not_empty_list_strings not_empty_list_strings ::= STRING | not_empty_list_strings "," STRING unknown_map_entry ::= STRING ":" syntax_map ::= "{" global_object "}" global_object ::= "Dhcp6" ":" "{" global_params "}" sub_dhcp6 ::= "{" global_params "}" global_params ::= global_param | global_params "," global_param global_param ::= data_directory | preferred_lifetime | min_preferred_lifetime | max_preferred_lifetime | valid_lifetime | min_valid_lifetime | max_valid_lifetime | renew_timer | rebind_timer | decline_probation_period | subnet6_list | shared_networks | interfaces_config | lease_database | hosts_database | hosts_databases | mac_sources | relay_supplied_options | host_reservation_identifiers | client_classes | option_def_list | option_data_list | hooks_libraries | expired_leases_processing | server_id | dhcp4o6_port | control_socket | dhcp_queue_control | dhcp_ddns | user_context | comment | sanity_checks | reservations | config_control | server_tag | reservation_mode | reservations_global | reservations_in_subnet | reservations_out_of_pool | calculate_tee_times | t1_percent | t2_percent | cache_threshold | cache_max_age | loggers | hostname_char_set | hostname_char_replacement | ddns_send_updates | ddns_override_no_update | ddns_override_client_update | ddns_replace_client_name | ddns_generated_prefix | ddns_qualifying_suffix | ddns_update_on_renew | ddns_use_conflict_resolution | store_extended_info | statistic_default_sample_count | statistic_default_sample_age | dhcp_multi_threading | ip_reservations_unique | compatibility | parked_packet_limit | unknown_map_entry data_directory ::= "data-directory" ":" STRING preferred_lifetime ::= "preferred-lifetime" ":" INTEGER min_preferred_lifetime ::= "min-preferred-lifetime" ":" INTEGER max_preferred_lifetime ::= "max-preferred-lifetime" ":" INTEGER valid_lifetime ::= "valid-lifetime" ":" INTEGER min_valid_lifetime ::= "min-valid-lifetime" ":" INTEGER max_valid_lifetime ::= "max-valid-lifetime" ":" INTEGER renew_timer ::= "renew-timer" ":" INTEGER rebind_timer ::= "rebind-timer" ":" INTEGER calculate_tee_times ::= "calculate-tee-times" ":" BOOLEAN t1_percent ::= "t1-percent" ":" FLOAT t2_percent ::= "t2-percent" ":" FLOAT cache_threshold ::= "cache-threshold" ":" FLOAT cache_max_age ::= "cache-max-age" ":" INTEGER decline_probation_period ::= "decline-probation-period" ":" INTEGER ddns_send_updates ::= "ddns-send-updates" ":" BOOLEAN ddns_override_no_update ::= "ddns-override-no-update" ":" BOOLEAN ddns_override_client_update ::= "ddns-override-client-update" ":" BOOLEAN ddns_replace_client_name ::= "ddns-replace-client-name" ":" ddns_replace_client_name_value ddns_replace_client_name_value ::= "when-present" | "never" | "always" | "when-not-present" | BOOLEAN ddns_generated_prefix ::= "ddns-generated-prefix" ":" STRING ddns_qualifying_suffix ::= "ddns-qualifying-suffix" ":" STRING ddns_update_on_renew ::= "ddns-update-on-renew" ":" BOOLEAN ddns_use_conflict_resolution ::= "ddns-use-conflict-resolution" ":" BOOLEAN hostname_char_set ::= "hostname-char-set" ":" STRING hostname_char_replacement ::= "hostname-char-replacement" ":" STRING store_extended_info ::= "store-extended-info" ":" BOOLEAN statistic_default_sample_count ::= "statistic-default-sample-count" ":" INTEGER statistic_default_sample_age ::= "statistic-default-sample-age" ":" INTEGER server_tag ::= "server-tag" ":" STRING parked_packet_limit ::= "parked-packet-limit" ":" INTEGER ip_reservations_unique ::= "ip-reservations-unique" ":" BOOLEAN interfaces_config ::= "interfaces-config" ":" "{" interfaces_config_params "}" sub_interfaces6 ::= "{" interfaces_config_params "}" interfaces_config_params ::= interfaces_config_param | interfaces_config_params "," interfaces_config_param interfaces_config_param ::= interfaces_list | re_detect | user_context | comment | unknown_map_entry interfaces_list ::= "interfaces" ":" list_strings re_detect ::= "re-detect" ":" BOOLEAN lease_database ::= "lease-database" ":" "{" database_map_params "}" hosts_database ::= "hosts-database" ":" "{" database_map_params "}" hosts_databases ::= "hosts-databases" ":" "[" database_list "]" database_list ::= | not_empty_database_list not_empty_database_list ::= database | not_empty_database_list "," database database ::= "{" database_map_params "}" database_map_params ::= database_map_param | database_map_params "," database_map_param database_map_param ::= database_type | user | password | host | port | name | persist | lfc_interval | readonly | connect_timeout | contact_points | max_reconnect_tries | reconnect_wait_time | on_fail | request_timeout | tcp_keepalive | tcp_nodelay | keyspace | consistency | serial_consistency | max_row_errors | unknown_map_entry database_type ::= "type" ":" db_type db_type ::= "memfile" | "mysql" | "postgresql" | "cql" user ::= "user" ":" STRING password ::= "password" ":" STRING host ::= "host" ":" STRING port ::= "port" ":" INTEGER name ::= "name" ":" STRING persist ::= "persist" ":" BOOLEAN lfc_interval ::= "lfc-interval" ":" INTEGER readonly ::= "readonly" ":" BOOLEAN connect_timeout ::= "connect-timeout" ":" INTEGER reconnect_wait_time ::= "reconnect-wait-time" ":" INTEGER on_fail ::= "on-fail" ":" on_fail_mode on_fail_mode ::= "stop-retry-exit" | "serve-retry-exit" | "serve-retry-continue" max_row_errors ::= "max-row-errors" ":" INTEGER request_timeout ::= "request-timeout" ":" INTEGER tcp_keepalive ::= "tcp-keepalive" ":" INTEGER tcp_nodelay ::= "tcp-nodelay" ":" BOOLEAN contact_points ::= "contact-points" ":" STRING max_reconnect_tries ::= "max-reconnect-tries" ":" INTEGER keyspace ::= "keyspace" ":" STRING consistency ::= "consistency" ":" STRING serial_consistency ::= "serial-consistency" ":" STRING sanity_checks ::= "sanity-checks" ":" "{" sanity_checks_params "}" sanity_checks_params ::= sanity_checks_param | sanity_checks_params "," sanity_checks_param sanity_checks_param ::= lease_checks lease_checks ::= "lease-checks" ":" STRING mac_sources ::= "mac-sources" ":" "[" mac_sources_list "]" mac_sources_list ::= mac_sources_value | mac_sources_list "," mac_sources_value mac_sources_value ::= duid_id | string_id duid_id ::= "duid" string_id ::= STRING host_reservation_identifiers ::= "host-reservation-identifiers" ":" "[" host_reservation_identifiers_list "]" host_reservation_identifiers_list ::= host_reservation_identifier | host_reservation_identifiers_list "," host_reservation_identifier host_reservation_identifier ::= duid_id | hw_address_id | flex_id hw_address_id ::= "hw-address" flex_id ::= "flex-id" relay_supplied_options ::= "relay-supplied-options" ":" "[" list_content "]" dhcp_multi_threading ::= "multi-threading" ":" "{" multi_threading_params "}" multi_threading_params ::= multi_threading_param | multi_threading_params "," multi_threading_param multi_threading_param ::= enable_multi_threading | thread_pool_size | packet_queue_size | user_context | comment | unknown_map_entry enable_multi_threading ::= "enable-multi-threading" ":" BOOLEAN thread_pool_size ::= "thread-pool-size" ":" INTEGER packet_queue_size ::= "packet-queue-size" ":" INTEGER hooks_libraries ::= "hooks-libraries" ":" "[" hooks_libraries_list "]" hooks_libraries_list ::= | not_empty_hooks_libraries_list not_empty_hooks_libraries_list ::= hooks_library | not_empty_hooks_libraries_list "," hooks_library hooks_library ::= "{" hooks_params "}" sub_hooks_library ::= "{" hooks_params "}" hooks_params ::= hooks_param | hooks_params "," hooks_param | unknown_map_entry hooks_param ::= library | parameters library ::= "library" ":" STRING parameters ::= "parameters" ":" map_value expired_leases_processing ::= "expired-leases-processing" ":" "{" expired_leases_params "}" expired_leases_params ::= expired_leases_param | expired_leases_params "," expired_leases_param expired_leases_param ::= reclaim_timer_wait_time | flush_reclaimed_timer_wait_time | hold_reclaimed_time | max_reclaim_leases | max_reclaim_time | unwarned_reclaim_cycles reclaim_timer_wait_time ::= "reclaim-timer-wait-time" ":" INTEGER flush_reclaimed_timer_wait_time ::= "flush-reclaimed-timer-wait-time" ":" INTEGER hold_reclaimed_time ::= "hold-reclaimed-time" ":" INTEGER max_reclaim_leases ::= "max-reclaim-leases" ":" INTEGER max_reclaim_time ::= "max-reclaim-time" ":" INTEGER unwarned_reclaim_cycles ::= "unwarned-reclaim-cycles" ":" INTEGER subnet6_list ::= "subnet6" ":" "[" subnet6_list_content "]" subnet6_list_content ::= | not_empty_subnet6_list not_empty_subnet6_list ::= subnet6 | not_empty_subnet6_list "," subnet6 subnet6 ::= "{" subnet6_params "}" sub_subnet6 ::= "{" subnet6_params "}" subnet6_params ::= subnet6_param | subnet6_params "," subnet6_param subnet6_param ::= preferred_lifetime | min_preferred_lifetime | max_preferred_lifetime | valid_lifetime | min_valid_lifetime | max_valid_lifetime | renew_timer | rebind_timer | option_data_list | pools_list | pd_pools_list | subnet | interface | interface_id | id | rapid_commit | client_class | require_client_classes | reservations | reservation_mode | reservations_global | reservations_in_subnet | reservations_out_of_pool | relay | user_context | comment | calculate_tee_times | t1_percent | t2_percent | cache_threshold | cache_max_age | hostname_char_set | hostname_char_replacement | ddns_send_updates | ddns_override_no_update | ddns_override_client_update | ddns_replace_client_name | ddns_generated_prefix | ddns_qualifying_suffix | ddns_update_on_renew | ddns_use_conflict_resolution | store_extended_info | unknown_map_entry subnet ::= "subnet" ":" STRING interface ::= "interface" ":" STRING interface_id ::= "interface-id" ":" STRING client_class ::= "client-class" ":" STRING require_client_classes ::= "require-client-classes" ":" list_strings reservations_global ::= "reservations-global" ":" BOOLEAN reservations_in_subnet ::= "reservations-in-subnet" ":" BOOLEAN reservations_out_of_pool ::= "reservations-out-of-pool" ":" BOOLEAN reservation_mode ::= "reservation-mode" ":" hr_mode hr_mode ::= "disabled" | "out-of-pool" | "global" | "all" id ::= "id" ":" INTEGER rapid_commit ::= "rapid-commit" ":" BOOLEAN shared_networks ::= "shared-networks" ":" "[" shared_networks_content "]" shared_networks_content ::= | shared_networks_list shared_networks_list ::= shared_network | shared_networks_list "," shared_network shared_network ::= "{" shared_network_params "}" shared_network_params ::= shared_network_param | shared_network_params "," shared_network_param shared_network_param ::= name | subnet6_list | interface | interface_id | renew_timer | rebind_timer | option_data_list | relay | reservation_mode | reservations_global | reservations_in_subnet | reservations_out_of_pool | client_class | require_client_classes | preferred_lifetime | min_preferred_lifetime | max_preferred_lifetime | rapid_commit | valid_lifetime | min_valid_lifetime | max_valid_lifetime | user_context | comment | calculate_tee_times | t1_percent | t2_percent | cache_threshold | cache_max_age | hostname_char_set | hostname_char_replacement | ddns_send_updates | ddns_override_no_update | ddns_override_client_update | ddns_replace_client_name | ddns_generated_prefix | ddns_qualifying_suffix | ddns_update_on_renew | ddns_use_conflict_resolution | store_extended_info | unknown_map_entry option_def_list ::= "option-def" ":" "[" option_def_list_content "]" sub_option_def_list ::= "{" option_def_list "}" option_def_list_content ::= | not_empty_option_def_list not_empty_option_def_list ::= option_def_entry | not_empty_option_def_list "," option_def_entry option_def_entry ::= "{" option_def_params "}" sub_option_def ::= "{" option_def_params "}" option_def_params ::= | not_empty_option_def_params not_empty_option_def_params ::= option_def_param | not_empty_option_def_params "," option_def_param option_def_param ::= option_def_name | option_def_code | option_def_type | option_def_record_types | option_def_space | option_def_encapsulate | option_def_array | user_context | comment | unknown_map_entry option_def_name ::= name code ::= "code" ":" INTEGER option_def_code ::= code option_def_type ::= "type" ":" STRING option_def_record_types ::= "record-types" ":" STRING space ::= "space" ":" STRING option_def_space ::= space option_def_encapsulate ::= "encapsulate" ":" STRING option_def_array ::= "array" ":" BOOLEAN option_data_list ::= "option-data" ":" "[" option_data_list_content "]" option_data_list_content ::= | not_empty_option_data_list not_empty_option_data_list ::= option_data_entry | not_empty_option_data_list "," option_data_entry option_data_entry ::= "{" option_data_params "}" sub_option_data ::= "{" option_data_params "}" option_data_params ::= | not_empty_option_data_params not_empty_option_data_params ::= option_data_param | not_empty_option_data_params "," option_data_param option_data_param ::= option_data_name | option_data_data | option_data_code | option_data_space | option_data_csv_format | option_data_always_send | user_context | comment | unknown_map_entry option_data_name ::= name option_data_data ::= "data" ":" STRING option_data_code ::= code option_data_space ::= space option_data_csv_format ::= "csv-format" ":" BOOLEAN option_data_always_send ::= "always-send" ":" BOOLEAN pools_list ::= "pools" ":" "[" pools_list_content "]" pools_list_content ::= | not_empty_pools_list not_empty_pools_list ::= pool_list_entry | not_empty_pools_list "," pool_list_entry pool_list_entry ::= "{" pool_params "}" sub_pool6 ::= "{" pool_params "}" pool_params ::= pool_param | pool_params "," pool_param pool_param ::= pool_entry | option_data_list | client_class | require_client_classes | user_context | comment | unknown_map_entry pool_entry ::= "pool" ":" STRING user_context ::= "user-context" ":" map_value comment ::= "comment" ":" STRING pd_pools_list ::= "pd-pools" ":" "[" pd_pools_list_content "]" pd_pools_list_content ::= | not_empty_pd_pools_list not_empty_pd_pools_list ::= pd_pool_entry | not_empty_pd_pools_list "," pd_pool_entry pd_pool_entry ::= "{" pd_pool_params "}" sub_pd_pool ::= "{" pd_pool_params "}" pd_pool_params ::= pd_pool_param | pd_pool_params "," pd_pool_param pd_pool_param ::= pd_prefix | pd_prefix_len | pd_delegated_len | option_data_list | client_class | require_client_classes | excluded_prefix | excluded_prefix_len | user_context | comment | unknown_map_entry pd_prefix ::= "prefix" ":" STRING pd_prefix_len ::= "prefix-len" ":" INTEGER excluded_prefix ::= "excluded-prefix" ":" STRING excluded_prefix_len ::= "excluded-prefix-len" ":" INTEGER pd_delegated_len ::= "delegated-len" ":" INTEGER reservations ::= "reservations" ":" "[" reservations_list "]" reservations_list ::= | not_empty_reservations_list not_empty_reservations_list ::= reservation | not_empty_reservations_list "," reservation reservation ::= "{" reservation_params "}" sub_reservation ::= "{" reservation_params "}" reservation_params ::= | not_empty_reservation_params not_empty_reservation_params ::= reservation_param | not_empty_reservation_params "," reservation_param reservation_param ::= duid | reservation_client_classes | ip_addresses | prefixes | hw_address | hostname | flex_id_value | option_data_list | user_context | comment | unknown_map_entry ip_addresses ::= "ip-addresses" ":" list_strings prefixes ::= "prefixes" ":" list_strings duid ::= "duid" ":" STRING hw_address ::= "hw-address" ":" STRING hostname ::= "hostname" ":" STRING flex_id_value ::= "flex-id" ":" STRING reservation_client_classes ::= "client-classes" ":" list_strings relay ::= "relay" ":" "{" relay_map "}" relay_map ::= ip_address | ip_addresses ip_address ::= "ip-address" ":" STRING client_classes ::= "client-classes" ":" "[" client_classes_list "]" client_classes_list ::= client_class_entry | client_classes_list "," client_class_entry client_class_entry ::= "{" client_class_params "}" client_class_params ::= | not_empty_client_class_params not_empty_client_class_params ::= client_class_param | not_empty_client_class_params "," client_class_param client_class_param ::= client_class_name | client_class_test | only_if_required | option_data_list | user_context | comment | preferred_lifetime | min_preferred_lifetime | max_preferred_lifetime | valid_lifetime | min_valid_lifetime | max_valid_lifetime | unknown_map_entry client_class_name ::= name client_class_test ::= "test" ":" STRING only_if_required ::= "only-if-required" ":" BOOLEAN server_id ::= "server-id" ":" "{" server_id_params "}" server_id_params ::= server_id_param | server_id_params "," server_id_param server_id_param ::= server_id_type | identifier | time | htype | enterprise_id | persist | user_context | comment | unknown_map_entry server_id_type ::= "type" ":" duid_type duid_type ::= "LLT" | "EN" | "LL" htype ::= "htype" ":" INTEGER identifier ::= "identifier" ":" STRING time ::= "time" ":" INTEGER enterprise_id ::= "enterprise-id" ":" INTEGER dhcp4o6_port ::= "dhcp4o6-port" ":" INTEGER control_socket ::= "control-socket" ":" "{" control_socket_params "}" control_socket_params ::= control_socket_param | control_socket_params "," control_socket_param control_socket_param ::= socket_type | socket_name | user_context | comment | unknown_map_entry socket_type ::= "socket-type" ":" STRING socket_name ::= "socket-name" ":" STRING dhcp_queue_control ::= "dhcp-queue-control" ":" "{" queue_control_params "}" queue_control_params ::= queue_control_param | queue_control_params "," queue_control_param queue_control_param ::= enable_queue | queue_type | capacity | user_context | comment | arbitrary_map_entry enable_queue ::= "enable-queue" ":" BOOLEAN queue_type ::= "queue-type" ":" STRING capacity ::= "capacity" ":" INTEGER arbitrary_map_entry ::= STRING ":" value dhcp_ddns ::= "dhcp-ddns" ":" "{" dhcp_ddns_params "}" sub_dhcp_ddns ::= "{" dhcp_ddns_params "}" dhcp_ddns_params ::= dhcp_ddns_param | dhcp_ddns_params "," dhcp_ddns_param dhcp_ddns_param ::= enable_updates | server_ip | server_port | sender_ip | sender_port | max_queue_size | ncr_protocol | ncr_format | dep_override_no_update | dep_override_client_update | dep_replace_client_name | dep_generated_prefix | dep_qualifying_suffix | dep_hostname_char_set | dep_hostname_char_replacement | user_context | comment | unknown_map_entry enable_updates ::= "enable-updates" ":" BOOLEAN dep_qualifying_suffix ::= "qualifying-suffix" ":" STRING server_ip ::= "server-ip" ":" STRING server_port ::= "server-port" ":" INTEGER sender_ip ::= "sender-ip" ":" STRING sender_port ::= "sender-port" ":" INTEGER max_queue_size ::= "max-queue-size" ":" INTEGER ncr_protocol ::= "ncr-protocol" ":" ncr_protocol_value ncr_protocol_value ::= "UDP" | "TCP" ncr_format ::= "ncr-format" ":" "JSON" dep_override_no_update ::= "override-no-update" ":" BOOLEAN dep_override_client_update ::= "override-client-update" ":" BOOLEAN dep_replace_client_name ::= "replace-client-name" ":" ddns_replace_client_name_value dep_generated_prefix ::= "generated-prefix" ":" STRING dep_hostname_char_set ::= "hostname-char-set" ":" STRING dep_hostname_char_replacement ::= "hostname-char-replacement" ":" STRING config_control ::= "config-control" ":" "{" config_control_params "}" sub_config_control ::= "{" config_control_params "}" config_control_params ::= config_control_param | config_control_params "," config_control_param config_control_param ::= config_databases | config_fetch_wait_time config_databases ::= "config-databases" ":" "[" database_list "]" config_fetch_wait_time ::= "config-fetch-wait-time" ":" INTEGER loggers ::= "loggers" ":" "[" loggers_entries "]" loggers_entries ::= logger_entry | loggers_entries "," logger_entry logger_entry ::= "{" logger_params "}" logger_params ::= logger_param | logger_params "," logger_param logger_param ::= name | output_options_list | debuglevel | severity | user_context | comment | unknown_map_entry debuglevel ::= "debuglevel" ":" INTEGER severity ::= "severity" ":" STRING output_options_list ::= "output_options" ":" "[" output_options_list_content "]" output_options_list_content ::= output_entry | output_options_list_content "," output_entry output_entry ::= "{" output_params_list "}" output_params_list ::= output_params | output_params_list "," output_params output_params ::= output | flush | maxsize | maxver | pattern output ::= "output" ":" STRING flush ::= "flush" ":" BOOLEAN maxsize ::= "maxsize" ":" INTEGER maxver ::= "maxver" ":" INTEGER pattern ::= "pattern" ":" STRING compatibility ::= "compatibility" ":" "{" compatibility_params "}" compatibility_params ::= compatibility_param | compatibility_params "," compatibility_param compatibility_param ::= lenient_option_parsing | unknown_map_entry lenient_option_parsing ::= "lenient-option-parsing" ":" BOOLEAN kea-2.0.2/doc/sphinx/uml/0000755000175000017500000000000014206773520012135 500000000000000kea-2.0.2/doc/sphinx/uml/requestLease4.uml0000644000175000017500000000670614206773363015340 00000000000000@startuml title requestLease4 algorithm (Kea 1.8.0) start :get lease for the client; if (reserved address) then (yes) if (requested address) then (no) :requested address = reserved address; else (yes) if (requested address is reserved for another client) then (yes) :return no lease; stop else (no) endif endif if (lease for requested address) then (yes) if (active and owned by another client) then (yes) :return no lease; stop else (no) endif else (no) endif if (requested address == reserved address) then (no) if (lease for requested address) then (yes) if (active) then (yes) :return no lease; stop else (no) endif else (no) endif if (requested address in allowed pool) then (no) :return no lease; stop else (yes) endif else (yes) endif else (no) if (requested address) then (yes) if (requested address is reserved for another client) then (yes) :return no lease; stop else (no) endif if (lease for requested address) then (yes) if (active and owned by another client) then (yes) :return no lease; stop else (no) endif else (no) endif if (requested address in allowed pool) then (no) :return no lease; stop else (yes) endif else (no) if (client lease and lease address in allowed pool) then (no) while (iterate over pools and subnets) :pick candidate address; if (candidate is reserved for another client) then (no) if (candidate is used by another thread) then (no) if (lease for candidate) then (no) :create and return new lease; stop else (yes) if (expired) then (yes) :reclaim expired lease; :update lease information; :callout lease4_select; if (callout return) then (SKIP) :return no lease; stop else (CONTINUE) :update lease; :return reused lease; stop endif else (no) endif endif else (yes) endif else (yes) endif endwhile :maximum attempts; :return no lease; stop else (yes) endif endif endif ' after check if (client lease) then (yes) if (no requested address or requested address == client lease address) then (yes) if (has reserved address or client lease address in allowed pool) then (yes) :update lease information; if (old lease expired) then (yes) :reclaim expired lease; else (no) endif :callout lease4_renew; if (callout return) then (SKIP) :return old client lease; stop else (CONTINUE) :update lease; :return renewed client lease; stop endif else (no) endif else (no) endif else (no) endif :get lease for requested address; if (requested lease) then (yes) if (expired) then (no) :return no lease; stop else (yes) :reclaim expired lease; :update lease information; :callout lease4_select; if (callout return) then (SKIP) :return no lease; stop else (CONTINUE) :update lease; :return reused lease; stop endif endif else (no) :create and return new lease; stop endif @enduml kea-2.0.2/doc/sphinx/uml/appendRequestedOptions.svg0000644000175000017500000003624714206773363017324 00000000000000Append requested options algorithm (Kea 1.8.0)get configured option listget parameter request list (PRL) from queryget configured options in dhcp4 spacepush back option code to PRLfor each persistent optionfor each item from configured option listget configured options in dhcp4 spaceadd option to responsefirstfoundnot found or already foundfor each item from configured option listyesoption is not set in responsenofor each code in PRLkea-2.0.2/doc/sphinx/uml/buildCfgOptionList.uml0000644000175000017500000000174414206773363016353 00000000000000@startuml title buildCfgOptionList: build configured option list algorithm (Kea 1.8.0) start :Get (empty) configured option list; if (no subnet) then (yes) :return; stop else (no) endif if (current host reservation) then (yes) :push back host configured options; else (no) endif if (assigned address) then (yes) :get pool of assigned address; if (pool) then (yes) :push back pool configured options; else (no) endif else (no) endif :push back subnet configured options; :get shared network from subnet; if (shared network) then (yes) :push back shared network configured options; else (no) endif while (for each query client class) :get client class definition from current configuration; if (found) then (no) if (built-in client class) then (yes) else (no) :log debug "class unconfigured"; endif else (yes) :push back client class definition configured options; endif endwhile :push back global configured options; :return; stop @enduml kea-2.0.2/doc/sphinx/uml/update.png0000644000175000017500000003272514206773363014063 00000000000000PNG  IHDRn00\)tEXtcopyleftGenerated by http://plantuml.com092iTXtplantumlx]OO@&ʡV!ڃ!R$P2[oc$gwphz' B,aV#̾ʽ!Ko| :e.uC #>(`3.sݔoπCtMXqc'2ZCȞ Uĺj \!HU "@:$͒l4Ln"L%B-xBHE[: yQV&~[Uh<;JSi3n[Us6ε߬I4)IDATx^] | W! bo-R[R UgiڪJKPUJjMTj j X}MK%?osܹ7ɽs9ۜ9s+c0 di@!D@@R" `)BJl!%6@H D@@R" `)zgϞ?zH ")) iƫϟKt \~LÂG8PZ .H_xsαT_zuPP###/]x 7n@7oJ=*I3 4n #ʽ[+V^*>||{͛W_-]_~u?DQ5XW^s/_RV܎R3g Z R2w\prr*Yymb~:W7 Et %*W?cԨQ(0zhIzZa^JH¥E urd[?GbaʹK`:ysUJjIvZgϞ>}694p%6mڄNaooշo_777$VT CZnWHIڵqڣGU-[ֶm[7_Gvg,ZH#÷N쉈O?e)Hquu8q"V۷o`6io2QU]B#իW\QqXs;rhJTuqq9y4 X%%>̙3oқ4i#ZΝ;8Ε+ǏYud)ٲe K(M` خŋ!ݻ|?ɣk=],6.knG%.\;_4 dDJ_;d޽V-\_Ex)'ɃS(4ߔ ^zvѪUyV\y„ /^`џ?`k֡CP&%W^XUJ.8mz!'|RfMda`D?@}:x`7ܺu={jTYq@W1cex,Y5VuiÈ}-RBOvcZ߂Y>h ~{ EժUߚQϵDq^J9,NAqX"UΝ%/(lD'ZK igRc 7o 86Gؕ-YeaxHx=!6ZzVҺuk(J "$:u ;wĩ/_⦒Ο? Ԥ94y(@L,H,n^L|z࿐ܝ$=k֬Sl;XD:L@ݗ *@<*%Hj㼔v$ bb&%}i2G0>}YAtȸ@ϐ}e) Ҭݻ\JGe'e^J$OG"z?9rI#l Q R^R%֔@e4:?7a ~𲔨ܝzХY~;X-cVǏS.\vhC|BH??rD:..N;wϞ= cgϲp(/4@@Gxw_ݻsAoLѷL!R%o"ȣIdq0ӧɓnK@v@H D@@R" `)2+%{LX-_ d ٛ`tq[¾c9y7l#%ggM߻FXxZ$ӆ`t7%帰_{hiBL )z#Oo$::ĉϟuVdLHIy:L)%U~{l3wRiڛֶmU+6+KzuTark׮ԥ'nLJ -o7RҬY=?_շoȬY_[PyJ0Ӑ!֯ޭ[ b<J}|ɿ*6=Ko< w;[O{.9+7uشNWDOɩ" iIcRcې&7o.%%J۷wa E5kVœ㏍/KSH+S xO3c*̋ø7iRɓh+*O\ǏSR?.KO\\CB.1mthw:E$y]֮YzbW)J:3\ʕH$y:ErI >ERB'%`ZHIi [tqi)ߏaڶm,)OϽ/cBqvfy<{$2qqq"7VbX.zlݽz7#uɝ;)ii"9UXK_.T(ûʕ˖,YLL?HJRLO0uVcpKC+h'rh(n[mٲ lٲ%v^s޼@ooOĎrk׮9sFBHxCt=Ԗ-sƅ-!sJ1*$BG{tUguTa/%%K3)7zt'$$?4'O4CWJ6ռy}v:w( M.8}v"gVW6H 7$$P,"dHBˀP<]j9>r팈IzAE%`b`)LOIPdrJv 4iR]ۆm kE 4CWJ,ӧG/\؂D=,?!>R|+-յIr8yO'OU'R",E+OdLHI֛6!9ɴ!7m> ZvrrtE%/Qϋ)2p+W`>?|(mZR,\eÆ M=GHW^rȃ(ܲeK4U^ P6!"8zg1 $r)ROPZjIIIX-s?h4?㸸8\nƍ;{Jop0GN-Ç }b%O<*pQhSJoD/QD:u֏`n@O:e4MrźLJ(\x1=D3آ1%-1f1cgϞRri]p!PiݦUtڴiІ_yxU,oF370c1V-R [ׯ_G#(RuYo߾ ^bRCۼy&!@|C~Is/EΝcGVR%e@)ˤΝ;EHJك z;>s77K.f M4->y)Z*Fc tҍ5B4قDM#7gΜ7 }xzzb=߱cӧOqEy$ +vhFILLԅ@<<< DW{+0a#1ź|Ͻ!ڵ+G( ]S; :^0ϝ;w.;R e˖v8E͛>o@@/%k׆f!@laM\-͚51%lSJ)*S3,5C ¥z/jԨ>S| 6݈+W͛!@ w|!6lظq 2g(ѱF3 4nUw.-Hg/ݽRY<ٳXEeZB_xhJJJR:t||<4I؇~I ȢJ˳HG*A$e 0`+U P>X Zn=sHwlk s@[WRel +$bڴi$%ѣm۶F[.uIKd .cpk/ksZsV3HRe?~dd45!66vԬۃGy!ivVks\N EGRϽ&k ֻnO?>&LK"H-zx0px_ d-x⭘jton/tdcf"䵵RK~BJ2 #)s/ɷkԨQ}X@&ķ¤n;ydVWF͛7gϞCڵ7n[`RJ-_ү];wUv҅xĉ^^^nnn5kܵk:˗7 EիW[hO1Eǎ۽{www˗/+>":suu8w[:vXȄΝ;/E|pp#+VVݻwSbd.c ~. .)^ݜۗ3gN߫III {0]ȑ#::I [sqxF}]̍Ǐo6v`$F~3g@6mj4H^6mC1C0bcc@NNN?^H^rÇ(bmڴ Tٹe˖6lW^Æ 5n۸MC';[0r AիW\922@.)^] +~>{U 2Rch%)IHHUK 0 Syݸ8\nƍ !F ѣ?NN΅ Vo<).^*ǵJ(E`6VQ#G _[1 "b`FS SNʹ`䤄ŋS.)^] &MD<2LWBJ,C.%ϛ7/˥/ [?~b]HKWWEE鍙߫!%!wϟϏ岏w뒜LpKH7̐1cqϞ=ӨUׅCBBĴ2:9m4^JX@ <1pQboiZҗ-[S[qn_RTwm:3T0_uI漀7oė'd~ t$%~ܹ3n*U HrܹshѢ Iɞ={daߎ}v%,I&ؖÇ"/%UV҇6lmҥ5jxHM6a"7gΜ^z<0OOO;vx)($|bb"f/z1h VQ[z9sׯ_bEzϢ8oWUd@ &k弼!hڵ+BK( 뒹+zv킗߿O0^HJ,"ޣK#oߑ^!.ϝ;w.;b Nl1yfKIڵY*P[e`9u٢ 7k Qϓ'O*UPL2@|||P m*TNſQN>|X\9⾚7o^U-FFVZDfϞ\nP:b\޽{Z/%%Æ 7n@^uh^7n5* BJR=>X7a߿h[gϞ:)L/x" E#|VRR{#>>$oI ȢJ#aTz $$$yy7x 0͵ _],[߾Nk)A}DǮskds x;#;vڴi$%ѣm۶F[. z l,\N$5 i:xK5NXi`W۷o9rC$.x CREU׵9gCp<Ǯ*0aZ}I sVo9H L;>*';N9~f:y 578dA <. r9t$%*Ͻ(HJ*s HJ,ܯ],BH D@@R" `HJ@vOGRo3~BJOHpx )O?I?pxHJJIyK~X: 'o&Xf]!,&'!ɛ f)9;kz52fggN: '6))DžeڳFOdLHIyz%'N8[ 9ɴ!7tx-[DEEw?YriC0!%Yoѓ"""l,H9ɴ!7I+Vt rt AN2m&$<6h:Ŋ%/.C9ӳX))dy ʴa*,B./_Rv\׮mʕK˕uEiC0JI5}}x/n+/^C%K m~yOEʂc +/>uTarqŏ߾}>R<{Q^Y6lX> 4\]]FPRŋc0yEsdhFʕJJ'NH4TUԯ gii"9UXDR*¾)7)Q1]Jy6BJ>>up_{K7oMzȝۭx৽{͕:vlZNUKzuTariG1)1܍vܼC2k_WP(nޅ>֬Y s^R?6" IOQ"L79>e̘0/J ޤI'OYJ؅%$x,Ie{E4nİ\ݻ{*EU7nlG뢓;w.L1Sy:Er]P~ w+-Y$~Za@֭N޽40CVNP n۲eMٲeK޽yޞ( ֮]s挄$~{-[` -ZCzbUHąLOɩ"^J`KgRo'NIH3h$N i\4lXytQ.]|qE)..άX^m'ڷAnHHY+D-6Y"(xjrP}b1 JVƏRU6!,"•iҤ6 _ S% ;,i\XO^{ Y+rc+C|2OW[zki"T1pҥN\NN],DXV!9ɴ!7m}T: y;̖R" [)BJl!%6y~FIENDB`kea-2.0.2/doc/sphinx/uml/buildCfgOptionList.png0000644000175000017500000025355714206773363016355 00000000000000PNG  IHDRvTH)tEXtcopyleftGenerated by http://plantuml.com09*iTXtplantumlxTKO0[qJTmU7-,RiI&cg(_ijo{c=#[9.Y^˕\pc%gNc*:Ax/P涪!yF d0#ZHnc༄D*0.h`+lx.TuZNûCw,xQNk*i44$:al W =QfPc8J0'(%@p E8< ؓ?Zqѵ=OAh{ط_PjUoæm净4]qI4 +Po < 5!SoK.y\ۃ.q\йPE,:3 n`tݤ7?;=!$yz]QNm3›טKrQVrq{[XCF K%u\\>_OȂJ((O_r]IIDATx^ XTUGEqEw۴Tr+{U7\LKl !"Z2f%na/svwf`y~Ͻ{ssϝ;3B!e!BK! T,!R$PBH@B!EK! T,!R$PBH@B!EK! T,!R$OYYY/^ᅰ J޹sӕ"###11qz޽{ͤyK3Xѯ졡<;IRGcܽ{W0O>>>>Pn˗/c:sXͳ;w"s=Hrʎ;V\9sիWc?7lPqqqlK.mٲe,`U@(^x? ߕ Xpjz/k׆e>[|C=SZ ,P1Xa r}0yׯ`2erCXi޽t3DFForU1X)pmu"W,.:_z%N4ݬVhN4)} Yck׮ЧO)%(()oX "֭B ؘVZM6sΕE̵]pLjtt4Vr…^{ VZ/ B_fO=`ŀ$c2Jnذa5PG}$OcoY0al߿X^F\˖-f͚gMOOǵ#<0:|={]Ԉ)Eb̙gyF4jcbs͚5bu֭X9"=A\RA7o)(`yRubB=q4)o5wI}TִiS͛7 c鏎3(Eh:Θ1C'O Xq۷o#%**JcxgTt GS!ӦM&Ǐeq'.K6';e8.>0ܬ%O᥉I<;.czҜORIQ+jQdȗbG\;wpf1<ۡH;QO1<נ!opJQg,6t,j"oO>+V PkX1g՜9sF|85w. PykX1g 6鶰[q5ϳDL%O:UcxHÇ#EznVMժU1ܟϰ#?dc*Py,W,fM;waƍKٲe?33d\0`m0?k y ν^C">LӉ6yctR5ڵkbNj/J322phhשSIjy6 Wzz= WWW^>ʦM4U,f.^?/.hm-A]v5ׯ_PD4_1Lyj7g;WϞ=1|U7G5Xԯ_ׯQ W^%-::6m*Vl<%.]1s] IQla,XšԇȱmgPp]b4_<%,m3>hF`S\>#(d?_z56lҥK;t1.ϓ'VPl5|A>=jL ;wG*wū qgg̔P(PJ%G*gȐ!'fAK( ^JVwoܸNP|yqͫ>J!+p+`ZR%=-;1xϲDhRg7oĝ7/XC5r1gCXVtPUl-N4ZsEX777dصk"q*0]laXعsjd ľ6l'JWNcv*l(veˉ'cj׮--W(ꫯ ]czQ.\(Vqąe;ީ1|Z)5pcb(1b[||S~4;wI(-N4ZsEX֭[' 8PJ|R356Q H.oբE /ջ lٲڵI5kXo[BA1܂G__|Z}NNN*T "N|Xޚ(zJe SN]v-z0.ɓsL VKRoFܲ70ZJ !ly6N2n8[3+dFM9"8PJJ(fD_oժٺuzcǎ'h={Y~DpI^V\S?r^xAl-@CY'hQ%G1js5g1cxNc8}F:tԉFkޱ(c)B9"m.RwW-lD,=7.#ܺuKrGȆ[܀ׯpzMxx8հaCs> YBA^+Bcx4N|,ZH4DZ [pjN Ȯ f>&@s|x[cO_Çŷx}I[7oތ 䏤Awǵv5z7}K][ƾg3(w"Lcx=00Pl*dWfjs :T\j _D s e5줓1g U8.eСCG| X]}^z3fXKHaz9;;ۆ ڷo/=Iq(XB Çt磏>R|W-!CRp~g>Kx9 BH%\~]7.!!MMMMOOh )PO?TмTR@@@||dYeRKHAqFʕ ?e9%CRBBB򴌆 KbXB ĉ+VDFF&$$z.&BR4̟?GĈtBH% 4HiN;wuD6))OBJT,!᭷RbV\ ze鄐RKHAXdҜQJ@__ߐXN,R*b )'NP2zLP}*isΥb qXB ˕+TY:ul%b )8/VZ4uYv-j!KHx7.5Fݺu_B*Bqȑӧ**[nb }}}8T,!B'$$Z*SLMG4.5W! JSX~"!*B9(f}Ӵ.>'*Z~ P 255U,<7#c, ̎%K~ W䊹,2' qXB febC-  WNa )PX2--M[n_} \ Wd8T,!VCLguw@?kt2`V\!WN^ qXBŸd(dBAbeXB% <74+JqXBS0PX+a {:`ˆU!b ?)J=8j2+!1b vhtY_ (_\+ҕY !KX޹'BU{G{E0PX |u_ElyZe&B@b5T!\[Xuw>D#CbM~ y>DCbM.J .ņqpXB̆sg Un 8T,)YWoq1.3 Ezn g'M zk7]K,^3A;kcC F"Ѡw柋I)EP$oݾ[dFƍ'K^ءe&c>_&=% #sqԅ}RKSgu/o!; aw'YϡwU{GH %gCg#|C$Q?a٭Ͼ?333 KLr0O X`X1{/e^OKKeI逊%ƹ}6?<$#5N= 2V +t:-KJT,1N_;u Y7=<$Ͱ~eI逊%FqLpn?T=2Ezo0 +!!%*٫؛wAj_tttRR^OO( K~oG@#!jژTNdIɅ%JҖzc0:/}5aaa:YRrb~c`uZlZZRBb݇0Ɲqc6:Νƽ{Ӌ.ԉWzg=<&Z=.㏫f[x t?O=RSS}K\KwaV=cǾTSׯUBwfIL4ogӋ.f`BڤI=y}7nݚ?GNJ-* Y:uj S:^bz{z^G )!PD6$'ߟ즦n3?5l߾ʕ{`Saꈈ7۶m.Ro*L3oXNJ xh*R!,)L9fNXi!!!:NG )!PD%=t(Xx7!RR;?Zr_)|0ɩ\ڮZ5|zꩶQQ+Lo1hPWj7ۦ=:9sJr{jժ z=)yC<-#GBRzzND/ݻcժ[lHŜԨ2zP\"̚.jQѣ!4t r8رc;>|x_|ժ:/ֈoQ!^~@"㕢ƍU o%ΦJPdq%JχSP[bIi%J,Q?xB7e9f̋{=Dŋ'# jѨ}o/믟ʕ+E10pWP!pka;{坮]%2Tp$dMG7a#Qsښ5߻,yFަMnPXRL`[1Ytw%[|% [LusFٳ #7pL~]P_Owv Wq2qP*/m'l;p*YԊ5Sa"lKVZzR9)N։SN*&Xb 1TJIO}n,9D?ub0+ʉ]Ul}fvx bِ᫯rs >A"òe'2cGLGq,Ӳ+roTa)\oݚ+V19r8 DhcO{B3ФHnRF[…˔)#=RdF o%u6S%CX/w_yKyvyP4A%(< )NVMEAd0:b&gUQ9 HRp ;{p+\ӧQRe +!)Ĉ1U~?Ȕb}}?b2F`Ò j%ǒ,bir,WZYzh7KJT,QbbOX/-SltJ (?s_E_TNxԎR[o 6],ˏ*Q YjUW X\@H)Hm_|_ ߨ/{JJnc޸a:bׯ_8X_X:#/V1T+֒ j%ǒ,TlUOzٳ1 *&X"FRrE?M5 BfFu dd$ѻw^~F#8:fvD/ڵ]XmҤ_OWXl؊=A4ݥcp̖+Wv֨1ݰa)ԨbUС͢Ezm 局-ʡCnnQ+bFf*U*ΰG7L4;ky/Y Ţ%PQ7`HAŒKX:Ǎ{$ D_kժ)&.0JODvkU+e+…~I3;J.`4;Ą̄v۷xJ}DY$L}}?D@(/mʔ7 zjp|G?qSX)O?ץGͅ5; W9*>Nj5<<&5۵kVl [JR`%C T\/aϚpE𛾀%*( -E_~}\ۡayʭ[KbGLk|9͛F cvҝK?ÑꢤHM&J=#O<>ǠP&d3dJF'= },bI)%J:cndd₅عs-f~sLaaKܫn@z=0tV6aI+CVQ@#Dz1~ODE8sf:]jofѼCkr-_갤!GM\_KJT,1+C*e]I=2E7m*Xbi5]-B92EOGŒK)jL3'Q}HŒK~T}H[)8)iZvݺu1;R bI.Őz4d0V}}(?NJT,1ǖ&vdc07O SXhSRRҔKqףa.w|_a`m¯:.==]/ )!PFDo\0/'y L^'MC KWXACTKBJT,1&z>))i*_퓯~΁o7vh8skMM zί?~#\Wmٮg Wt}SXRb90HKKt AC}&O0p`j\T+e47:=NaI%y ,|֬b4π (pϖ^ȍ7A!*~J00_ѻWRjbIHMJJ ___18 ĻhW_^~ޅ>FRK,^zzzjj*& bcc12FFFb$Ƚ2gr"%(+.1X?h1ϐ\!SXb`Z(v-39Yѣ(WRbIV눁d(`@rj8T,!Vb\2{igr!b 7=jʷB*+pjuTbf%8 T,!V Jzk?UbφP?*w 8T,!!!zb'QY !Ku3׬U;u.ҮeVBc@b5ЖZ(.m}Şm_Qf"8 T,!Vȼ0meŏDy鄷V0PX+!݄_a.|ЉG%Ě/ >DCbM.Lvn t"b 2aȦC!KHNCr?j43j4!bs%J?RVbPġ~MɅZkM|MԐ%AEӲ,!% *8(%˯"ec9T,qDJ_EFڲ͛7_rev4iaֺuʕ+R~BHQCU)BM66mZNUF5k\vmر z-!E KWF-+M65jH,޻wˇvrr|MB)*X@_?s綫 j £x7o [xǎ+[smB):X(_K Vo$;B.Ba[nQJ۷c";s޽{+RtP!(fZ..U~G.ܲ.]±MQQQ7}Ŋ+VYfÆ hBHQBŒO~7obŴv7iR/,lH ]ҺC+Wtskpb״i5\aڵ]͚5LM݆pٳǠk~Z5]j&V}w8_ֈܹs… TBHCŒRN~Gm4ۿi$ff>Wujر/ Cɓ%%E ׯFʕ+;r,χPt;ztœ+VnJs~ۄl(-++ ;vFMul{;nKT1"u˴е*fn{vxQ%y =CʥI@{l$0pH1XqX(s/_g GETVeHTb)"U}-R6!hbI)^b7m>>plZ mZPhP7Dm*CktH+55d`@X֭ۼyI{=,xyy5hРYfƎ۰aի/_\APCͯ_sXBlKQ ~}s "]]⒐pԩ+^z~3졌 Kb 2 ʕKJJ xGqFggUպu-[=PUb 1T,!VFm!C3mX]jU*U6om̙3HvYb"%ʨMik+U~Vk׮ξs}ӧOc K;Zu!E KQ _ѣWWzծ];885jhڴi˖-m&e.B*+6YzzKbb,WΝwr%v룮!Hb 7ϥIj-=}jq!Hb VKR ?/ rʿ *CbodtlUPP*밣ʽ 0d=ZLP*pvcLYOxdAUht+Eb 1T,!V#=γd-)3T,!6%j7<{[T|P*uzpٮ¯*A'Kb &1O!bO: XBl K5=Na]A'Kb 2+}G`↊%P̬N,{ z@n+lm.R*\=p6.=T#sb 1T,!Jҏw Tpί~sa8wWT_=}+\7ā_ܾvCYBCbcXBB`j[(Om7 ;H^KnUo2(% #so54xpt8)) 5p +8  B,crBb iii:.!!!rCx9};v?6Z؀w-\T+H uC -‚6ʽ\e% F4!փ%_Ŭ[[~</aƢ@/?[<{xs[J*T,! Y6)))&&&,,LL(}}}Q@ߞPn0p Gqqt5)_s =iv}`\kpn|Љ%D^OII$2<<<$$DebrkO@cLq Gqqt5)_q}N*!6%Yzzzjj*\BBBll, arb.!s*7edX8"IaCON*b)6)) 24[a[(7ed/-\CO*!%":$Gd"̂2UCOe!6%ʨD}^lpٮ|Љ@beS Q؏Vn  T,!Vn:T:BH@beVbbwWn  T,!Vnk#Bbedv[1BJ+T,!VnMf#Bbedv[1BJ+T,!VnMf#Bbedv[1BJ+T,!V4iYm2bD]1BHBbNԥ0߬Kb YWj,5r׆UCq@bcXBC\{B`'QY *Cb.Xh5AX8\])ʬKb uZLR[s/+3T,!6%j2gMp.eEDzVb 1T,!V#JzHy7WDK;yI@bcXBI8:A'Kb &w&;u6z~tP*+&l:Tb 1T,!Vfߛ` K*CbLҹs[dj2R*CRXoOva޼y;}ı/HA:"r7CbcXB ݻw'MԲe˰06؊<ȉm6%P4hPzz~Eg}wǎKMMjff2SCbcXB fS߿K/llY*CRo޲eׯOz}RRRTTٳ ֩SG/{9xWk 00P~Rb )8}t([b*A?NH%PHŔ4&&&,,LVR_XE"6!!CbsXB ^OII 5<<* U$b2 2د9T,!6% @驩  Pi "ِ~͡b 9T,!VCSHT "E*C(P*G%P8 T,!6%Qb 1T,!Kb qXBl K@bcXB*C(P*G%P8 T,!6%Qb 1T,!Kb qXBl K@bcXBLs'iS7*P+(\e !EK&-R֘R4PX_.LゲƄ%\[T,!V%XB KPX*"b 2T,!D@beXB% <&-+JqXBS0PX+a5 kCت܁PX;Y%¯!Nnf+B*pqgYCQO`']/JQf%8T,!V#΀2]C+bCu qee&B@b5~&LXVMTOxk!KȺRM҇:PX8q¯uA'B*krqgrSw(vCg!K s F6@q0Xb_>t1.%7v38sC'T,#~%=+(8R"* ¯>C[&c>_8,!%vegdItв*?݆֑Su:]ZZZffBLCŒb~2cn޼ʕ+۵kפI 6֭[W\B@Œ~-aƲPFiӦ́M֩S'$feeըQc͚5׮];vA=8T,)6,+M65jH,޻wˇvrr|(Px_K\P,<͛7 ׭[Wcǎ-[ܹsO@Œbo;]ࡶQfddTReΜ9wފ@@[co~w/H6 Vg$cH/u|²nB⁦ƍbŊ+֬YaÆB*{+ťO?S Xu<tf:O~C=Ufwܹp2G%rŭyիWmܸޅ ?^Y۰)U}Cp$r{Re$}!F_ؠ.#7z`@Ɉ/LM֢(;!Kl+ [ ܸGJ+g\;oYJMC9ӧb9EuPlYqnjs֬w-)0X( A`,浘f|YjsAfk}ɦBhژTSoQV{%FXvDu+Ӭ9sbnwQbm۾9~?_=ͅZ8wW!_=};\7ā_ܾvCM@ŒKLrtoS 5܏-^u FQs;~8mcAWSvDcPD@#˺9yyzLd0WS"# +?"555===33S5%%*!qݎP Fя e6miiie3 FCHC%GbS=2EV=Ͱ}?%t:ɲgΧLzZ؊<*!K >ةޑca8}n7'!2^*tl1!vK`sS6pLB.و]Ъ\H2*{3! Kc-B=1&ΉNJJwE4,,+)VuCgU١ )nX/ 0?%oOMV/bK'\~Tٛ X/Q^rc0#GM /,,,>>^a"qeitN>b}Ԗ}^[m```tt4&iii9˻mx%ߨ;+ƬYNP8 BSWQ`pp¯A'bP> ;Ί`>= f}+WNS&^~7|^,'%\cŰbPtuFWDD_}pX}CŒXn˩U2hPKvmIw ;vl+W8|x_ [d /QewP )SxV5kV9rЭ["ȑT]s]Tu ޽;VZe&|3CTTF89ÁZjzrLu5ÜU~YEe=BC hCCaժ&Zxy ))ΝEXȏDi3XSVkE;Hu6U%gTQlCOB|Љ7T,*Ur^p_WjСH/Wƍ21B?>qbc|w'O~}ݺ&.n{11_}N.{6ߺCaaK1P޽~($$|@=zq8$n'OF6hPF~@u5hz}TϞ` r_.(믧;;WRTTDŽSO3E|\,^a«b#XX Џ<ܹ㰌Av@U1AT,ePT`r|E"&(| Ej-0ء{oHw;4)[̘?x<~h!CF ?)SFzb36z. lK6ڰFübX~ bwP>+Vz/5f$b٨6mZUV[~Q-/>Ą]UT]zӧc[rE6ED9#@*oTj*P}lc\{3V# XuisV%%H/YݰFüb/֋;˺0!vKSža2~b,UXAqƞ_(/5:Y>e +>T[ب–*v P2xi7U@D7m_|XVTjXQ T:ۘbe=vOI=@Æu$Ū[X^ftJ(nK'OFU/Պs-oǒ[ѰFübտCBŒX^sڮ4EzǏС`7m!+Wv.`cjAL.tXVIF"~jРv.Ù3[jPZ(-K[ d_EQ+S+",EV+m6ݠ-ZhRm@6e ZhіI63yq"(VEŭǁ F->ł(~7-A|/+_n֯_/8]?QQA==^^|wF+JKп|KΝ۽ %QrO:gO<)71cѡC[z^3&V4ݣ(Vl[; @K<ł(ί`+WrşKBjVVc-(_vT=ɴj⮪aaa^ 0$;3WGG27Y#,:PL֗ƹ<}D%P,p]Xp?~ň*@Ptֹo'ƎD+HZΊܞ}bP,p]XpY~ (X4q9B8~\R-[vnZWomqgՖ߰  n}&q#g;MAubM~dN+gq;ڱx!X@& NbgMDu .  n!orgϋT@Gv . nڥˉ-hLA n[Mв]<::+Iv \(x}w=6߄|D !5/LWT$WR,655Us. $ȋL"30)L%ﲣyXs*&&**`0fnB%b&Ȏ Pݖ"E w>8r}w_*lzz:-aiNrS. $h^]"t4KP,pX - CNNNr|foj@q'JNeᅭbi;99f C%u(P,oYZIP])liHļK,V?Y%^G_=iqkB6dVre|YgͯסX"@*eu:]zzVep =3A,$".c4hѬP,pX` fYѨ볲hah-8D~G:C(W4hLFΚ_CEbAP3&rrr222(&''SB܊hOW +?4h.ьyE4o4: \(-*8t:Jkהw*"h,ęm2X@uk4 V"(G% +?k \(A @\~-A b_$p^ijo6r@.2>?#SpP,p Xaiq(sٟ"bK RdXAH eÚmな=gM@%bX߾) mOk 9bK -пm< D%l. l\=v??[iP,p X$q̯:r^/2@N,7=_. hPOl1BX. 6 P,p Xd&ٔ@X. ZE?0zma5 (P,?룊Y`YbK |82m>dNZd46P,p Xd@W9oHi XVFX@8580M7(P,aۯB@%b9˾_-˖]0%JQTjY'~P,p XjBuZeiZ:&8GMgx@%b65+ eٲ5р6K~ݓ+|Jp+P,p X#~e,͌W>NCw7J]'jz" lGj3P (:V,j?܎q @ȟTeYΠ}-xECUu,W7q~vTogg8bYT*U]-oV*Oi?&x(P32GL&SYϼ T.d>ZǯūX<' |.(wM=VBqz:)] @ 1 999)fUiY_%,   @ !/cYX*¯3P,5ǶeW(fY(bl,=ƌ @߲x*+ { e3GX GDD#eW(`͋L"XAj4zLMM_pCX jH$foڞA:~ bݹU)0+n ̜ۗG5dy!P,2@  `@WĊo@[8 KT{)mb {0KTNn <<-η9S,EӇ/6P,pj.U MĐb kbj86P,7xXm~j^;^6@FkIɯ BTF@6]]Elq  =*.% e^J%A޹s'SZn֬;?GQQtlA UE#Yٳ) ZH,==X%%%{졅 '8#@5P¯,c\,kGJgقRcP{찯[; UVYYY@U@ӯc ヨ$ի?`%ٶm Fj2ZŶki;"6mwt̙WnݼΝQVu}{0١C͛{m#N:VlG$ۋ /,|*_`M0%/2ɐY;WE{u'.ӎU%N8dNab9`Wױb="))l>,oᅬs2z՟** ~l>[,<{vo>>oO>0^ۨCaۻ KK4l3{$+d{B6^;~\[RrXW$.2}aMp P,pg-UYVzKIY7kr )ձ}۷^K8ǯ$*xiYuf^& )~{F?ԘI,d8v,#~j+:nWQI?'eߟwGA^{QA@&XTiY$-C/Ohbf2*x1ޡ…Y3A ؘdm7 Gc nڴ׷>I֤8u*־ۜ+Vx`% | +,̯'Tt^o2䋟ˣOV P,pl[/k׎}FZKFc UiІsUyyy~TZ>Mk+x-(H[.Zz?8DfErLd@"q`~U|;222444***99YA \Z,)[y{5߃3}LڶmѾ}ZVT.(%aC;{n1$ jw\˭2##kؼ=U,hذo Px`ڋ |j+H?~-^Xa2WB0ۚeIHF+Wrw;!h&.aQRrmL4D+nz.i7{)KK[I,ůH\_ɬaB1W`0䤌eͲDӦ]tf``.J_%,[((  \ Yל>&k~b+A_Q b@ŀek((  \XJbK"lf! NZQ#\\uG *  زkJb ÷lү,hI yw*"+?5a%̬Dxxxtt4ʀb@[655U-"""AQBDŽ :J+JNȠ%V9ԟĭtBDŽ :J+JłfY?B2V}M{תŠ; :2t|( ((  fa2";G^d%$a :>tW @f{6mb۟}ܹsi#::M6:tزe ?~۶m5 +JbA&>+M4ΦF/^_*ےZeP ((  :ut~Jի7 ŕuر}111J 3=rȩSYܼyΝ;S+9}4^t?…  3MV//GW-_WTT_SNQLE|('nh-&MĕDEE5o޼I&jo۶mw5">S  ̈lnԨQnn.uZs%ϟ)@sĽ/t @^Xd- \$n~uԅ n @.Y' Z b@CfcP7U'¦9b@ٌFj¦9b@F|!:=|QxxJyFib@ 6QVE;x D%l((  l]0ʕrѸa@@iX$T!Ɂb@ٌ:=+;7:99P,P(6H;s d@@iX.]l!3(q:e/ s )J9{+(H[Ebṯ}z:fwy5p2X4P,{w{ro@i'W0Fm:r(~t(NɐHl>XeTa_‰4@@iX 3Fl,+"qfҔZ(1 B~=8f.i]vTmnIaڀ E&L&\ZZ*@w((  廹)O Y!ΰğeo$X\\j۵kMwBI6'mu;unb|SX?]Zz/4̙=V\RSKV;}8VHaY9{voڵ;l_ڎ+*om_F@݀bA RS.ZTVl:eѻwWVkC iwZ0UTޗy,ZKk]|5ܹo}f/;P ld+YžZ8pZb%Nt2xwAimGkIr0R/S\b-Xn@T~[\|USSScтƤؐ9NI7F=6Bװ4v BA,T,.>D'N/w_tԒǁe;2J}R?'?i-V h!IE/Z@݀bA Rm4efRޯ[׋6PF|R|Ia.嗇[~AI_Cm0 /\h1/fǯSǃ Yz(ZKIRUzu x v?u*U\jq )V[o>q"'V h\0$_D06~@݀bA G Խwﮡ)_[gX:ɦWXKj_ |(Soj?pSZVNkBO:3fgn,pdKIxtHܹݛo@IL\M"iٲ'zL\j)]'<8AA=*8XrF2%6 j=T^6h JЙ3{ho|jSl]uDu^XNk* %eEE{EC*+KX8!>85`/C<HNP,pXPCn:رhJ8q*C|pwkX&@K;ǏkUekMKJ>=4qkK;a[błߋET+vyEzxXX (xwY?ٯzq& (ԜCn\R:d (8@E'_ .UuzMQT1;&@!9BIشY`QbڗthR,~ P,p殷ZbbƟeO%bIzX8n#P,P(8JEy,kw1Iaڀ~ſJl0fp>FX4P,w([%{_-p8{{F4%RV md2 'mJhZyYq匟vr cԦ*w䰓VWAdȊf$4D@tyί%bNG.aC@yX )..6 999 V ?UJ^7 M@u73?So&SXNNNIBS&̝]^b@@eƖM[Ö 5E/XZBʻ<^G[=inU&UZYʕJ41ǯסX++V- [j&n'%#?*BN4n:ti4@S&: 2C l6L&ZPVȠ$LٖV3%Z5VtD馓N&M 4%hb8_C@yXLd\(j&ngת)qZUX)M3+Mg+Jh4v"(qG% +:άU>P,P(Avp@ߵ+NZ< nI2 ql ((  ]0 fɚer((  <ĉ/|ۻnvT\IDATI#lYxbo8bDoj)Yhb)v<8R%Zc kb'nX4P,7ɟRe*sdYs5~~ Rg}v*6o(bOH ^靖2M{r!*:DE)gnX4P,Peu*)PjYfn=<Eâ¦8 @-+[PcŖ>7Eu  @-#?Z>7"b^ "'̦5aꖘ_EKd<=U{b4ntJ`|˦j4ZtFDD R֎(XX!@Po'Z-Q, ( ptjO,h>])z~7~(rӓ &8> ( @5`5?#I1+++CĮU_S޵jB ިOZ8뼛 ( @ fҡd"/DE&QΏJb>c3gΜ2e mZe˖%KoӦ?-mO pP7nz(nt  3O~}GWI&ٴѨQ_ŋK[RRB;`Kzl + (utQQQ?0$&&֫WoR% 6+++رccbbn Xq N + (#GN:500p͚5͛}}}wܙZӧҥK9n.\l >l1\Qa2 3OV//GWR_WTT_SNQLl >  ̈7yE&MJ7oޤIZwm۶}wsuql 3m65jkuEEE7?yy^=q.mXdFW\٥K^ \]@bn[M .\ >  %ԂMT{) x@. `'_6iSp0   7:)tl 3MPNoȊtl{veo#(q Ne/ ݜsXmXPE)to}z{7[ }1eׄ&   rvϑn)i猟vr cԦ*(~t(BɐHl>Yrg ND+8.H{6P,y_,Ŏ4Fl,+"P.ޞ9Ur|_ 3R xBȯ|H'oڜˎ!P&3 A߼$d6KKK=`(nz;'CV3 t[Fڣ5}sFcqq1gْ' ml#2ł[8e:CqC nOh&`Pn~YnIԆJ= /P,QQv>8ׇw6.Uuz?;-''oZf@TN\((  nRr21r8!9fSyHʷlaR6MQfY  mSpNJł&/vLW'ƌFl<0fYN_YDIGRpNJł$3qC H;10JM&-dXYU_GLgRpBW 7v2q#1YxPvoBZmVV`l3jL,Ե{֍N ((  n`HTqC H#6Ҝ [J bYfz95_P,P(|͙1L'W,\8ȑHq l?EI-[74(hVXt޽gDӜ$222!!!''d2\e7=݈muotb@@iXpnfęNh۶O7nO6m}76k ox,!.q(:f4'EDDkF*wEĀb@P=!SC^9E;!n#cȢ.]:_?m+:f؈y&##`0jv5щb QlϞ];w61󙬼SˏݺuNJ /(x3UVM4ᅤ>M{C@@ F\ 1n6i.۝=uaak~5hӾ}?Q(XH$g5+/\8ءC'A\vcǢ0tzy +vYj3lX/SW=#'ɽ:*{m.Xk3{z֡WJc}ܘ xRZP妧ʷc=jpJłأXL.Iϯ!鐕ש㑘mS9P!eᐐ9YYpSv~WBCתUk߾vhє ldj))9ܶmjWBv'>Ah4[럆z}-ɑ?Ҿ,䞂ߧx$6|1Od#G"s2РAݩ ׫WIQ|>=|NS#@|{w><F2[KXNnٲJcv(/YrbX %<5j?,P,P(NŮ^ۦE Zض8K/ 9sfDZmJk Z,(;?)Sʎ gPϴ:5kpVNEZ+Kƾs}L Y]ڝ4 Kv7Yi#͍W7rTxڵkshI^(1`h׮5Im1A%'AT{"($&P,P(NrLM-wXC4ٴrSTd˗Ϣ^|}3nٲrSi]#wy\XGbϾ+6/SgoJ&*L=O<щ)6%etv`w2LKUB]}?xcI z`!=P?zDlժ)XAvd̹طo+s2֗b3`&ņa۬==ϸ`bX ۊu\mXp;/]ϏiӦ Y;/}:0 Z.쏋[A43s -%\.5{ilLn]/ڨ:Ȃ>i$[79ijIa#'NӡŊ1`ׯ/c.A%'bł1{zǛ>Eɗ[7 ׯ_è J$//Oʼ;PR.6:@. ;{lO垞ufϞ?;{YyoR?7 33yHJ~ T<*^>_\C ޻wdM:RY<Ç#:thKKbW {T2q3t Ŋ2`kKZFhN 7/YrbX 'X@|"[\+($e[*R2kAIڵsw/OMݴPw%6O} ,Tk_Ap/dn:Yth/./-!uDka0FZ@1|((P,Aʟ3ˤ2n:رhIYqwp_DUGؖX@&-jk4qqJlڹii_?\P7}mQbeObłmi-@ Hb9FP,p]Xpkm8!3JhxYVCub-iT_ g=E:=ի6TP1=`(-dw?6` ?@ܞ8LSMxѤXO3=`(6+EP4~"HCw$WR,655UsqA:byIjw"JG,ׇiJ~M~_%6 f   $0LI:=;YQvO_.k܏>V meii)7K=`(H@kј?*zOl a; NZQ#\\MCP{"թxYj߯%bNG. @@Z+ Ma1cgDj@͇_xWd-+9Jd4iB=`(H÷,-#(El [&|nPļK,zrբOV"nQVO+,GQۙViJfe+W+ijr  YVӥkZgy0/+A .Ԣ Fӌ&M9xz]A:b-eF^ʢUEBBFJܑY~HT M!H4hRԢ Fӌ&M9xz]A:bAP3&rrr222( &''S6Ն܊hOW +4h"tIES&M3l4hYu9xvDKK ε:!62Z5%] +4h"tJ̶\   _Fn%/2w~THAS~q\mXdqvl Wl}w.ȅ8W{6P,2.S 7mPͦ@&=`((`6Ȓ5?q޾+>`0 w @Cְ@\> }aS    <֫jw[C7=z=zP)x@6[݇Iq v~IȄtl 'uz G^.Q p\mXdYS7ڣ   d fntRx@Ng^ÍN @L|`JVYq\mXdF7yW+8.H{6P,2=j6%>VYq\mX$֗{Z Y@>=`(`~͟5"aYEq\mXW̲j8x@~[6m\ʋtl(ʷlʸ98.H{6P,aۯr8.H{6P,5̲e̿nIRT, @Z-gJ=Cm @ԄJ˖_Mk؟Eޯx̯,ĖekZWTMh2I=`(_Y0&S5VMi(ԏ2 V!9bYwu+g"aM$  zZrl{?s'^֎|'؆c}2az!gPGT~cUY((  @qIJ|HF }+,,LRl3'ַ_Rg=pmJPjfYW3oGFFFEE%''t:t|ꘝƪj퍼b@ԐseYίx+Oo @@iXj^O=NJf ](~e۝nP,P(b0rrrFMҲbrKXA5b!E{, :-P,P(B^Ҳ3b9- :9P,P(fY3bpeIçDGGïN  @ |^&2¯  @8fpyDDDxx8=Z~uBX4P,,D{Dz ZVcjj*@@iX jH̤ĝi{VVVFF=t: b@L\J߫R `4W'J 3,qS} Bb(qFGOG*6 @6[݇Iq v~I8 P,P(8ӓ6G^.Q J e̚_=F'gJ 'Yf0&((  ֫)q'4((   ĝ|(ap2X4P,2L;o ap2X4P,2=j6%>V'J 'j=Z% @6_gͫȧGXɁb@߯,e3'-2ť}J b-6~`e ((  X+߲) !lufX4P,5̲e̿nIRT,b!Z-߿ϔ{ +  &TׯUZnrp\ Χ/mJPmjWbҲ5+*&4B{b@TGʂY6꟟pOl:Lx@AH ~m*:E)bYwu+g"51 L@TYTbgڑ6۰o_cP??q֯tK~;+< @qIJ|HF }+,,LRl3'ַ_Rg=pw P,5fe~U=vdddhhhTTTrrN3LavMް,wP,5$mjY+-^Ŋ>S|ï,b b!z>aL;-+5v+ow. Qӫدz , PCȋUZ~b9- @85˲_pgXElFc)+n  -:uS/V\W(y,>pyDDDxx8=Z~ b fټ$ReZFT7@NȠLRlYYYW()ܝK^b@̹}yXcJ  3L @ uNX&  %s@A7((`6ȴWSp YfhɬbOEi<s_D@lvX!1>ިA¦@6[ zGk\~_6 P,q"8\]'E#/n l]0kf~U{6pgX$k ؤ6pn ֫I MF'(LMof@@k._6vn#FIG(BT4.^H\(ܤ(/Z[>y@Hl1,cv]NS\(X8HzW)i猟vr cԦ*D pGѩӣ Qe;"+iX)p @NEyw\gʫG !ޞ9'_Bb1s)MwQqC x&3 A߼$d6KKKSukz2d8!ho$X\\ u_9bYNPgӾ o0`Y@JE'_೸>pOV촜XPRrȍD@8m1O#qc(, \(MM_~8!NW'ƌFٌZ $~&g;@8y?JLa**==]כL&,dź#.]H7#\/j&Tfee ,dź#柌S1[ p0Fl,$:::55)sź#/bGDo`;ܝ 7JJoٲpܸAAŵҥ3,.w8'fo% 999&I8pXwܾO5 Pz<%35ELfoܠvh4 8@\8O.ׯ'.w$!Ν1dQ,8(:f؈y&##`08أ؞=]vO5l;bD3YyNsu뜔B_P3f "uX174i ?D >|ٳ'>X&z5c=5m/8];S#{tA!I0RR0~^SPÆb-gYFաCx?ۯ_ |ڷoq֞h#yD<5ܘvX4 vWԻw͛X{Q˪UPY!WM{osIZxb7fk=|Y (P;bb=[t&S?!V^GbjMI[CB$fe}N9^ ]\V}ES78˓ p۶-:vO]I#2 ۝{pu,t.9$m CrO]@'3A4;az2)Z{QgPGK; H#׭%V81A%ϗb ź#v*v6-5h-¶9-G^ziș3{O>mS]FMgA9X?MyqTT`>{z5g !GxO0j9rӬ]$YXǼk$''IOZK>m<\)SưB+.Ndܥ]k/d>{voڵ$/6f=^P,p!XwNroRREۖ;v ldz)ZK*VgsvblYHԩT| B<$ɑ[xlաC[{wepǏN/S*9*)9%Xqog#&V==p/Y| \(bxc6/ixw])._>/SfoJ*|Kcڴia|VK~ B r VP~B-ݬQ.5{i_\C ޻wdMz9V^c Qĉ+ʀM q ك%K/k} ( Rʕ\)RX J׮쟻票hyj2嗸+k!x6a-hx\juwWUp Z>_dM \Z_[C<`C8{%H(/[uG.V @_C8Ctֹo'ƎD+HZΊܞ%=¶| uS[ }}"%eirĝǵ*ղk禥}%*w|b尰0(8?P+nKp(tH~~: \(M9rGPgV,y^.릔]Kn32s,@8gSwVX@aIa\@8a0]UzFJBv3߄@ - 6::dcM{t⤆@8IEڣ$+-aIXmjj^/..l(ɋLR<37p(p~ۣɯQQQl6 5L$M Uy3a9p(t/idׇ_*tZ.--Nk(ݡhtG=}' f5䰓VW! xǃ?We˪U~.t:XguwhP\\l0rrr7Ōb΅P 流er%_ILӕ&-M],a3ł[,KhBÖ 1/8j/^\hyIL+,GQۙViJfe+W+_ eu:]zzVepm0`DeA aIHS&$MK4EW*@̲FQgeeZ!!!Ap ZC;h )6#oAM<~4 i*҄iI( \(܄Җl6LJ\A-99r!4,W;ku|RX&M9x4hT IӒ&'MQ P,DK ε:r!2Z5)vת `74hJr. l׭h4;ȋL"G% +dV] ̜ۗG=?OXp3XdJͯ+_ pXd- S 6@@*_V b}MfK~x)=bs-fkп $6NmS{R{86P,lv8-Q\gwN/ (q7=ȲbRz D%lpXdYS+,F'(97#pn ۟Egnt́b RlrQ ?{gUqEEP%Ҳ\2\(KL_-Kfnei憨^ȎH"2ZBj(hLޙa`.<߇ι{{g0 ΀bIܿu Z@=2#* $7o l(Tģw|ڌ>ڃ &Ȭ7_8PVz_xK`@j\M9 .ܟ˜ aۮ. 3?UvIzcnpy۽ڬ(@x{`[@Xee|qo![hR\9;mT<? 3l(4,漷l UJ~\PT!{=hׁM8+ـuA;S=6NlVxZ.,,ełʹr|:#( Xs0EӡYT*, l (TV{7v~#5y2/ PxЭA$Ien ełJPNx Ȉ C؈ sJZ.*¿O *!̉͘"HzL&KNNh4v7H/du|R06 QtFMC<&"Ql;-hu@xxxRRMd # q=#+EͲeӏ/wfIY\|lǎe'>;GvRIJ+)t,Yh# q` |Wd1FC~rqjnX7]_E2_}uěo(.TzƘc V(*Jx;`m@(ĉ>}z4jp޾})ɞ~ц ]Fpz̝;ѡys]A~ _(LS usnӦ?Zpwmڴɣb[t\:B:ƍ]m=u*)WAu*kViΝ=:]{CҥK[?yX 'Oiٲth77Whѣ75 h{:>ůEݻwE Vʕ 45 ̵j5ZL0F4LW ۲eIL`O>} ǎ׮]@%Ƿj?z?_E7 `ѹs߰Bi\^:u< ?tFrÇurrd?:KyFyKRN<6U?lTomhW^N11;СlOv߾gK7hPٚ:qР0:Vq1 MSa˖ͨa/I-vVrP/6v[&Iu:rIT9۠-bMCTX 4mKQ! YY!4~ԩ ӂJJ4F\=gDLƍ|=2gχ?]nO>y-<իխ[7GYw۶-m!ZY3)vÆY!Mr5Z@X`@*68x%۶L`lZӵk{{ڄ4^7jԐJ-?p5kӬGN yUX.QgmZU^)EfMEI9C;eҤQt患4lЅ{Ub}+AbfZ||ju5>rǥ/[F l(RU7}hNJ OlLCm@"q%l\~)u*VqIdz43R:k*ti7q;姟~GHoqq+fO<}„at2t8$K~)QV={BaAgbi>n\($P,0Dkٻ#W#9;;mӦ3$cܘ;xp\ 7EF>-m /&iu ~}g*6YyZ:[Rrf3hڋ`bu֦U̞U8w$ZyM(߼U.V,=<{6i&겲YSlWXNh.ՋvKݺA5seb0' 6?v?8v,k4="8R&[޸QEEipoժ#9dMMi^ѸߣG~{KΩW)chwQ_m )_Q-L%l3yz6}L0fd|K/ ,״,| 5\?S,mC˿4p5lҠA}?|*}TN:icSk19} l (˜/P^4ˡIW<{Y׮+R\9)93qiE>D\0??UN{_oYSϦBc.--X`K@7+ @ЧOg}r„a4ѡx4?Z%/h.(P,ؖ#/Voڴx߾ AFAbA%d,sVbbJo;y_p)@'R R̺5tWmbłJ(+;mTȈ p`yIr6 *GYN &92r2,00P,xjjt!w. j~Wo6<<l (~[pHbd֨&CD¹a zg 7P,lܡp@I|UF@2_8 Uc|.@&`7@HF"2i9| {@JRt> %WS+;t΁b(!v/ W (&O^;X'C#bHB.l b ;ߟ̑&wza l ל9̒OZHΜ`Y,(̯?js6}`Y,(+fTBs lUf٤鴬VԩW_}գGvEGG򨨨nݺ5l*P,W1`YRl:uy'Nݶm=#"ݻh]o߾Kk}̙eo#8ɴllҤQ}'-mڵG:iGF֭CÆ.^^NROSlqQ/A_e SŦ1¢I8|n2^m+#?6$?9>ܓ's'kV,9,;{[O=ՓmPTtXXӼ` O?Eb \\o8D l58x+):+>R%-Y)S̟?6>훚ΧQ)`ǎe7oϚcss#ƍB7?{ }1/[ba,E.]څb%Fǎr|6mZ~;$Gkb8T~}gSRȬ{=p`{ckT:" 4(3$%ߺA >hk|ĕh*ޒ^b% W2kPxRX`:P,bT*Ufff9-kRY;+j P,0(X1XV_Wb@eu~5( XVUX`:P,Ė͞j,ɏ:.?!X3#UX`:P,ĖӁbl_/]žZK44M^(^UY_ L6߲III p!!!yЏ+jdp|jz@tX`kpU*4R"ihV|^K ˧F_ L6Z1驵~]44)\Q%ӅS#ۄRTTDvh4U-GCsnX{ؿ+Vy͘1[jձc/5kV֭4iqFVb tttkb@H`h&>cUXy料?ܼy… dqq1I_3Ӂb믿:88(ʰ={RIlllUиqnݺu%""_uӁb=sL//7۷ݻ7K.Q[V\Ie˖q;3"~*P,#iԠA_ڵK>xΝ;ŋiL̈y@Hxh&lrڴi\IXXgrS<<<ڷoߥKp3"~*P,#ܲx[ES؂2̈G  -(C_Ջ{@Ubwpc`h7ݸq㟭5ӁbEF?4O*ӁbCgG){\96ipS` @tX쀬nߘϟ Y1"mnyM5Ӂb &t|)}( ʹ,wO+ ZIrbSП\{.@&X P,0(ɸw^?Y">:Y/P,0()י_2'. L@J?ĦNV  L@b4DCslb@CYI?Ӹ7)}#>Bj9#J q\b@VgN|Sȫ?~결Z1Eg hP8L='տo&C̛O*K|ܡ?5ov$=@tXkrCά]SzV -8;)uؖo8#Nb@GY7>l%&ͼci4a(k}dM]./# CG_&*B}b@V/ "H5srjT!?R8_k:+@Diqsģ '?.gXҕ\w+6C*&2^X1ESCc^;ߟu7b0(jy u3ֈ A+)ԡ",))ITꢢ *<_Vlki %bSR8{sDzx LhߍkM6=2˹b_c=Gi,vK Z ]'yG8L RdM}_thHHHTTTzzJnU|'T33kC*I*["U ـu'%%Dҕ򯂪ןE^>kqWЭaAbVA~11{h42D5bK=O'0(:(<|p0!a-~x Wc#n,ˤ۱cĉƟ{YTΕ"[ǽWV=8Nz@4x0=ˍp{5v12>yժr.B~m3(h7|֢EӚ; [sLU_ppBHMMUTݴŵi6tXc zW>j.#|ET]*F˜ްm͝>?|]^_bN@ցMtƮG~=wޑ{Oqq d ?~ݺ/۴ic~`/\8ǻ6mde>k~~ݙHءyJ:GG@dO?(UEB|:ym[O&zh'8s'9::ХuڞT(^ViΝ=:]{ӨQ.]mγečZ/[6Nv;}:6\G[8NMSSil߾T>aoY_ K|#ŖOz}A'PmXHHݠA/GÖ{c!+wp-S9 @d&; ; m $QNwYHNrNNl\..>־}n:DEY lwɱc!T s?Ɵpܖsw'>s:xl'>j 9I tQ\Z5Ǐ?lTomNpZܹ-]TȎt\v߾gkečiZɫՉ&yR=@ÆڶlYR3k }">UqShhX',m{~6;g5.Y獤3K֦{Pu`b7l-+zzϖCgIqzk#\I>gDLcƍY? m={>2 4ޅ v_b6LS+A͔plO>y-_NB ^]ݺuO{?S|DQ:ujCbmۖn-Ьgrzk,Ͷ/o` MivlC-#nLՊ1c<+d"IV5>aްNXŵ-ۆ@ Ky#aHkO"Pu`b_0GٲΡ3!a#mߨQC2xnS:_f>Mzڀ9oǎe4 ^$؝ po0 b)'ؒMhvm2p`o* _E&XiGY_~K:h֬ yZI9CS(A'MEg.8 {hečiL\s{5a9a%V15p,tưbҲyZP,͢ƍ–iܳg--tqqfC'#::GJj`=dw4 FEN(L#7V R)<`ˤi.CM Qgm ,b]8ΝO?# }^6{& =424Z={^^D NSpND5w,m6i7V?PUXxžsneFkeVNCٯ˫S4t޸q(2r Cii;h`e3$}iU8qM?8ڪU}ҥ}JeWٳ1M6!!+7tN%Mи_\|)8zygѴ:6}H5ԼTܹh-Z6m<<ܦOfztz.VXZFܘTk@@O_ޏ^=^8;;тV}"SgSӰNX|Giݶ d7@b#zoЅ^4\|f*lР|iܡQɑ-?@ъ{__TtWn4_l<|[4a)32%M.Y/#)F zر]y?MXzL2v@&lԨ! tJ'9y -S یf~h_jr0ӆl VWcVZ-w|r=4lX߁{~N֤;D>ԩ4wۖ;5/Y獤3'BbA ZF~"{EnirA!mVZף/4޿~%)IFsf9Sw8f4=}  j'cٿz; j(j0%&ntiA(lMZ#ޱYSlhPP j(jdG)S/ه j(jy u3_ H5r%%:| (P5h!% UJڐv ZErj~" '?.){'<<+ (ke2O~QAcrrjT!ci Kr%Œh Ӂb}> nG_7$۸-,,.J***: L>/^Jza.uW~/ f^|\Ѳd#U ɒi hJJJӁbzV9l2K1o1uFW-bRI}? RZBPRGyjr8 80S+%x4b_,^r.n[2i$Y~y~FĮ7\ILMԧSؿX PUYF4'ňOH""JD]\_e2_/(HkpU*QQQ! xUb_.!>~H-'tB&/Bj9k7ŴJ&_P׿X P,V%yLLB$,,z jzCCsGPA}z 5w/(HkPTTh8 4PB/֑/_OCP/@=#SPϯA@ X[^sU*4KTPEC~P/@=3+re@tX[[ZNNh ͹aqP/0^|X`:P,6L@b04x@HC__ @Ubwpc`hZϦJb@H@鍢H!Ҳ|a{ttVxX`:P,ҐB,74_pʚ\)X`:P,p=#OVs44\AV^?*n ( dĵCً~vsp#`%@tX$̚PS_Y{O9 n( d(S ܡTӁbeA' %3QUӁbf#hh0VXP,0(ɞ% 9s W 1c|xb@HI4.ǵ.8wp5X`:P,;GͥUӁb_YeӦ-SՅ%%%}Ӂb_7iJe ( +߲ L$ ^X`:P,Vϲ7~sPP,0(jb_d cR_t  L:TկZ+G_Q p0FvӁb2+ز4myn?*obCcXX`:P,U0 r޿pORs4#fϹ 2zR  ǦӰ8߰X~T#7FP5rgidjx({ZH6+m~(1{hJ~-|Ux`4P,UY6g^u+>:s;AAA2,999=i>l\#qD-<$ bճ,l̬xRhn\kR)²P,dߤU,WOǦ,`@Tq,߯d֠ T_ |6 @5QTcTjY_),  mPMȋX~nb&J- `@TÖ_sXLBeW -ћ~`@H߲{$~u*+ } eӾ !F,[L?i. `@HlNh)6a(BA?W()!bHvOR `@HL,RDߨj;@b!Ū3x!P,b(b+@~9UXZl X$FQېki[GG7Nk;(ixaqYS- G˅(i'WQ9Rdur@2Z!*觼nhaI=' 7 P,qfM©ܯ,ɽ 7 P,QzhO̯rQMN3P,RBؽsXzFb"9 t΁bf#H W ('̱d!)ȸī7 6@e@_}>y@0jf=)^e@l0js=S,U~0XRW(z,8q, @@_=,FłZ~ j~W{ <YP".7oj-Q󎘻.P,% d99Ҧ,N:'Oū©S 6BPm$j#-{a{LJn6:l(J[?DjB6Vwf77ףGU읋Wp$7`!P,q*…S5k2bDz[ xyb9CNm>ݶm=#"{_v]߾})+?~h6maÇ>IFի1٬_Ahߨ -}x;ܺѱck-9@9t*\x(:ϊb3gտy̧OO;llҤQԯDKݾ}K7|}[{;G^}y'(b 2#6EzǏ.={>?+V_3oРbkEMDn֭C.W%&eBۈjcsBC9Wط},+nѢ)+<}:^z4 끟mۖ6HHH \D4jԐ3DE'Qݾ}ۋ_+P,M۶d:)V|tyz{?CSa/^G1pV܂g.VMDkI+V̦Ie92 EZ9w.qOiڵ wH$BڅkeѬf42>Qh-ɕ3nXř٘[2Ŋ.>O6@R[ֱ ?Vgd&u+CNȦj@[gAY dəIQdyu ʵlmE"ΠXPTjYR7|Ɩ7mZL>#nСC+V߱~A.lּ;̀I;ǎܹs1ci+4okM׶w&nʅB7_f,X_gg-͞S$.ӦB澾s.SȻ tҥ]rm[}a~T*54M^?@XX ľI [2ӕ+~IC9~}L[1,*GuT!4(fa8l)8 ~l~{*1ge~.twd կ]Kl&.+M^ŊcSbU ΠXPKŌge-!V#-ի+5F1=|2N*VgP,%T*Ufff9Vd{._.SSXxm,brSXz_C ΠXPKИkEā_ #ΠXPKИ @W`H3(~6:bAZoW`H3(6b˞xi2I~|Yu U֛|{@ZA -XAW`H3(ec:|ЏW/YuB'/6yxg*fV"888<<~6:b[6))IP($$$ʡ B?~١爞)zW`H3(βJ255&FQQQ +G|=ug+zv9g/$ fYVV4pZ9wQwSg#z_M"ΠX`~h.**![حrrB; WX?sD=_+I Ġ;`HX$E  &nX k P,tZl ` (tr--w ۣ:b*epaK+krKEZAHB,9ߺ-θ&ΠX7>m< 6,H<*>VgPQvj<]VzzcـumWd%?!8{S P_;a]ܟ>gՔ^ liuZ ੩ԌiC^9[E,){ٺ7S>_kʄ. ܞn6$?.HYޢDu!ΠXsݹ*1g֧jykfߺrއ5A'gG4R`YGZAf&5r7AKȟ{;{G=2.QXVgP9}ܡŽAˉ:t+ q1gff²:bwb^9$XNxN6읤$RVliuŚ6MܟdNh>T&%''i4LdmiuŚ?OܙPm;U*&6:b~FSlwfA,-#vP OJJlaaWGZAfIj+)\YlriSsGy}]\Υ؎&N|wxmMǼGXi\.:l~111Fث#ΠXjLܙk:=BMBM;yΫVs];Ϡ|kԹVg]1ّ$RjbjWGZAfCr|pS*î]WJU(Rž7|-Fn- RiGt㟝jGg Sl_ppBHMMUT^ iuŚ #{Dx>=5j8~Ё{o߾ dO?hÆ.#G~=JΝм{׮__㏑Çsss~~kyu:+dkiKyxM6?OT( NyM6]rX!\?<5~.nQg=\G8:=Ϛʅ =g"n;roS:<7Ɣ[ߥ kj1&$|6biuŚ #K6/߲eIL-O>} ǎv*9>U|0݋o߲[QQȴ#%i(0 `ѹs,'E5)V|juAiФ_r\+SС|h}Wic_9ٻ THWD%6-–-Q;,_>֭ ~:^lI^p'Ɏ+~(n4n=.MP}س>7w&?P ΠXabi.M\yk4ݥ 5(68x% ܛla/vm2p`oVhc2$~!:Bn310>ʚ5iңG'W 6hԨmo ,+hZwA]W(Eq4i]zyt߭dNir y;ࡾFy]F>ݕ^Nqϵ]λKg k'H3(l~CB^^6oi!zB&!?4,xMGE BOUB$;~|;7j+_JXI?@S6;s _Q,w]0..*{+|& W?:NJC{ƒ..:=t^OwƯS>:s 5(Үgޏ^=Fgg'Z㏣mڴ~S\|+#נ[j޷w_O ӊBOUB_Y\׬Yy&Ӫ7CLv0J!q 4GyqOGS 4V!XߙqVb <<ܦOfztz:+ڊ6\aΝ;='^^h52ul}5Ҿuwi%}6@v:b͆_=qhаa}97)±c!]4 8R&[޸QEE5>-_4B9::,\8EKAnAg?(чVգfzu=:?2(NN4-[66;^S;]ข:Mut4~:G}^鼻>\WO,Y6 5ЭOYY[ %o5r%}' W38 !R%3q ?4y)\k뢳OyQՋ$ ڙDr~h|l4"8 S.M}n̥鼻t)gȾ@1x(ֶVgPnIXJܙӧdz>9a0z9O/  Pm#ΠXsuBOQ2MGYSlhPPkH3(֜Z٫QqFĂR9`*ĒC}/WV_y!6+1Sꭘ* paaJ̌ bDʹ%u,`ɕ~JeCRoVVgP,RsWК!>~aKkd/_Xt XXXvd?7LV2+߿R^  8*䨨(":88uuX% WѨQ>H=#JVgPe,VsLLB^Y ``ل~CXԿQ_GzAW¯:b-EEE^2SLMM~O^P+eSc W2kQ>H=#JVgPDK9*J:X6wQwSX<ԿQ_J}r+kuVU '\,_Ъ#ΠX$E  w ZqwX2k P,tZl ` (j2u~w?pZ+`H3(iH9WEvW8zeM].`H3(iG`OgDKGM:bؖ-2~\#]Mp##ΠX$_ܡٲȄ,iu 7^Sl~QMNX @JGek">!ΠXzFܩ|"V:bé;w+\x @>I7^Hȸī`I3(JȝzF;_x`@#ΠX 3磫i2#=)^/T?, % ΠX;GͭQ`Y̎:bMmYvߤ*GZA6j:uꫯzѮ]hVխ[ zyy}j߯,̲-;tm۶+V,^vcjɒ%۷P(ܾ#VgP,MHuyGN8SOQaii)gΝnݚ5kѣU`.,i*i޼yFF-eff...7o޼p]ɓ'RTiuۄ)6++ڴihԩS`GEeWRֳgO*_ 7nI!hޥKuAZA6a%޽{bry-gΜW^AA/~W{ ^Ο_|\Dz3gڸq#=ܾ}+]]R.]B\f˖-_ l-..&'Dvҥb' ^RR5mD\",M[4h@rzk.ܹsx""W0iuۄ)})11m۶|...M6mݺuzz:aRlOİb"˒D[l9m4===7oNstzIѾ}.]8p `$ vM^/ԯ Nqrrl֬Ɉadn:4l)ܺѱck-9r:ujyOڵZj mG;Ϝ9b/~A/& /ׯ_TiuZ" mi&2[Dy}^RJKzu۲eɽ{Y%%=>o=TO*7v ^yJ1m!3|_"U4 _׋0`?\TgP%B=\&[[,6Ax]i!>~{?;GDОO%~glyӦ$Tc:.X_gg-͞_P{n&˖Mwssm_6XW٘YaaaJ9io(A{},?g_"(q%k㏣%%p~+[k9P%4 zTIDATyyy1o ߯d֠ Fq-gH{@,TȱsnY_), AsXKi[`Yռ9P%4 -[~5;}v7k=ͅy- Z}v7k)-(_ jsY~5#}C|k)#k@A{gSPÈ-3磫i˱¯fpH3(R(Q>c=ipP-[_Gyk28~囸f# iuZ&-; rITBMA÷l_/]ž?d3+2ON2:N*fM].4 gS8 <6PZ*n je M.CBBkꧣбZCh.{. Rד;G7kY$<#F~'7kI=' 7:b-s#d~D]ej?HKOOOfBǂ_k왫PҖrpl>;DZAE鍢Ȇre| 4 OU3PT?>%bT>fhH3(86iy'M{)6N:b-y+6~6azĵ}>VgP%zLy'o=FPMMBǗ+} ]O[Z+G}$^eP ۷UmXrzb{/JojqMRٵbsgo !U )ģwVƵ-n:Aj.-GfRāni4~Z=['g~W=b2XVWS'B\nX&nCĒcɽhge17s8uv/6+?vQ#VgvX*j7XVW,{;{C= +v JSosV:G¯Yv*| Q__T{,7 C4z6'4N Jf٤СCmƖWXxbZo׮]׮]wV-Y} 8x35ـuVB\s95їfi̾ J%>cU|/R322h-33vqqy ԩsbzdU\M9^>}1o5cۏ{ J=$t"HkMDeWRֳgO*_ 7nYZZڭ[.]DDD멲"OM#ef^̡ ߫ՙ(ְ_-z E\n إJY{̙^^^7n۷owuuݻwoR.][n\f˖-?_BAQvuVq b!ɜHV_@c,+BZVSv\J疧N)2E1{*F ·ldtZNNN 4 CZEwڥj,Ud##,ye˖ӦM㞋0wwwOO͛SNyxxo߾K.=iVC\Qg[AJB#ZdyyyFDVZUSŗ<'6fRj3;ujt oرFscbGmΜ5}xZhٲ٤ItmO 7ѩ];Ϩ-#4iԶիgfn?ڷk۷/*ԩ|OƏڡCMl!xɒ۷o&W3^~-+++x뭉n݀#GsY~!FEEo"Sgզt%oaP|Nc[Fy>vc7nK_z-oiTIۧ()9fnG阝筧ߌ*!-\80Ilb tz:y>꧵qqTPi?O?Odwda@\b ̲Cgƭ}k &6Z& {WtFM{1bYuA3oZ?вoedU6ɦв."\Tz]}m١,"˕6mi'i Z(b 7 [EqI46O陙3gN9t:JII,Mėt$:+ n*1 lY&*n 2I(aڷoš5fRG3E>m[(ĉp,)iSM ܼ!m@3իM& `i5\C v^.-w}.M4,龮 s1xʕ3|ZjJW.w~v-,pD=Y(6uJWf^Pxx8b/"؂2m9[<왙>+fٸq{hv>/%ِko[~؉b5kgOZGKY~~u/zv*~ńU}wZJ%Ο>Ni{Կv 鲤޺i$M#z\PgiQ`~FXX+5^*RTX1ڵ4NsǶnuv1F\b ^}9rK-%IWܺ)6|~FtiiifY|I!b }wyz8_ׯo9eiv: JGiF=z'ggjU~ԩ/ _iu~-;H~߾-[6mI$ӠA=9)6mDo8n:Q4~fbԨ~ڵ>yw۷JMFJK+7툈Ȥ$Ѩr67aΆ{N!E/18j;v[EYn%~P#ϟQz:֊΋嫯bN]8|n=v8jQt;{]͒vӏvٰa.mv+ۓp`Ps +{29'3+7@D`s'} `wIEn_~1HK4rgAPcփRv~w4YD_i TQfUfBHǧJBFL4RPXŋ D'GrKߤCGZ՗ wI=^ 4d'NS%:QNv.t֑Q%µkg o5k/<AQ'"(i(v8th/By1H[n$Ӆqj$5Lx+&[v%^eÇ+\Q@ڶm[HSvN9{' &j)]+jiZ) (bC=^?Sl\AId3ӹenЯdְ"A1 ޜ-KǸ Ӧg4m$~Y2S40EFv_b*lv*ҥ KVV85`ĈfҖiӂFD%-ի+W<=̛oe“Vx^I VHF;iWiɻ)@pc,Z:+9zRNl7QKu_wQXӻiAnQZŲwppJ;j,+{HOa]yVYtec޽h:ujmn4O{۶-*nk̢9MNmj׮̴s>E;kժ( G˦:fѻwfUHO^GIKZIb?RQ  J=N? C,Ç+JT͇݋U,_qԓpD-e9yEbZ'+ܢTvV)F'!4òeJkkƌ d4Ĭ#5jTcc\A_+O2gQJӸC5κu̔F.cOhwfAGaݬொ yO=[ȑ]T&_iVRQ  (?/VBGvqܸC%G򯾊TOEӼCb'n8Ia`PsE]6әJOYYZ nz6)F'aYC~-TbGVٳѭZ5ݶm!+o׮ԩ/~W4wghHOI# 8VOT^lN 5?sD*͚5Nti7 ?~' MMFK#;'AnKCD789vDX^IƘm&G-JP`ObԤ M2͌*'!c]+ᄏ7qj,H;JZ" p& [<6;;I9QW]OAѾldnu즟4'6ɅPʮsm6Z579Qw&0ڢ?eJk<=Q('-XeԲw5SZd'}a]^t¯eE_bO>G!t (޽3t7n i:+ݠ|™e5vljbۏ+:G_G#Q", ~-,{ z|G=H;՛7ۿbBW͆АHF'o?i+,cQl9e2"lªXR3/Js?#'Α|,\JPFEE)ӯ咖Y,Hhl 'A [ǵU9Q,{Xqq~X[W˦PT,<<\ Hԟ l7)+17=e\R~:ĕC@ ʉkWf+7;ROr}R?r_-Xh4RwTL딁v-_/^ǨߨrIZ O|YzU#%D^n/iZGuqggچ^aY_*222Ҕ s1w/&G돂UzjL~0mJJd~pUx97D۸DE{mѢ[/~wn,;D$jvMӃ'"OEOG ޱ_ɯ{? ?%6͹˸y (VH)ZѰ, l_,$^*.=2;n@Jờ|hퟛHZ31L9>>^oN`?8..Bˍ.:ː.F#*q >'rZ+Ғ=jһf CZZ]IIIt+9.OR .7ң .C]H z7zF?)ślZ \k4Ws:]R 7Ow܀bLWހ!ޚ7E2PxSIKe>ER-`Wy (VY$6 'VI%⍀k - mT * k"tz|?ɯL%⍀k - mT *1{s'5-F'AZ7@ڨyUG+ū - mT *&]D~FҀnQ9&D$ - mT 8ϿwT#ԟޕRUP&\/-z@ڨy@)m廒~yBݱ/|qGʑ7<{nFOqdM콚Sood1u]5w@f{"N ǔ&-jAڨyÊICggͿ{3W#_X:A7*8vZ5Q9&'K{툛$騊@"泥 (RFθx|v/-Aڨyc??L^%? VO 𗯦ma7mz7<>5)Q/?IPy }ivE_|Ӝ>ͤiIkiWH y35oh0@_z߷#`0-9bYXҒi-إ`țP,MaZ O|Y:n".F^јzDw$lvb&"0-X_.Sf!ox@WEIM8v5%RtPHJJhX, `b#DxțPl+ >[ptD \[ǵUxTdZi"kxiW̊cϷ>;Ĺf!ox@ zytD J_n:(<<\gddf?+e'ն\7:W(h*&@y+6mu(iY!Wg]QQQ)))4ɡ4K0,PغqpG3P!7[,;%KL9y2BZFZ5r>d1jUDv[4=&OnJ]d vqAlvQ{6lKP8x ^tNnz*RlO\ THM*# G& VTii-Yg4ڶmaդԮ];ڋ4@{jU u[h&];?V͚WyEXk͚Yt.:]&S,޽h:ujmgi0-mIiKv} )Q?/pثiiS\@DPbChDK+ڵq* M&ok!ш|.o|-M))[}`+=[~QceFLZZ9RF5FFrX_/,d"qTgݺu˪>%Zꨄi C &-۱v-XGPl{^/ùb]OKx6*GPboAzSy3#.n=lnժi.^=TZUZwo13'͆>ޢE#h~ ~c̳֭gmvZNk{L0a8?0p͚5NtiɎE@Qz5916v]FN~%c3IDt-jRG+ݷb C8z\ (FțJT,4iڴ|}}f̘=~ɅT.,qТZ ˇj FfaH[$D-v\l%(pz9p% irMV+Ptݻwzn 9ͥ]xЊ ltҬ@ڨy{VltkΝs͛1ƒVT(N-ZUS(FțX"P(iȵ@ڨy5NYנ{ *@ܽ[t Aڨy|6D|"4Ŀ'jCC@F6*Gb >5i_矒+qӔkq\hh%KK@ڨy-,z$mfDIHBa_v&**+JvFIB7ڕi Kr%ŒhSRRL&SNNcn%H#oxLj˾>1Fىa6.[O~~Jl6ssss#-@ڨyc%пҎڍr B q)[Fqw0A˩4Zyyy|@ mT I҄bdl 8v7Lx盍,ۯsj줰o6ޫRUP&|. M =,|mT4W#bH$@ mT I҄#''l6 b@FLB'JNe%rRRep [nZQ9&'[W\v]aB4G.\gk֫(^O#C|x*Wr2򁲂r2򄲅rƑ_ HKB6*GPbL4/]k4iH)K8?ԟ6P%SP&f q.W\i TFțRn-YMH<(^Jw]B"-AirM*V͠?eirM(V)ܽµE@ mT *v(OZn \i irM(V)ȍk4?'Z8 6*GbK'ELT.FțPi* MiMTBM - mT *Tﭯ۟~F5 6*Gb`?FϘiJ@Z7@ڨyU7ruzs|-F'AZ7@ڨyUCfej2HKH#o@zI['čNi irM(V5|~WnQ9&DSl_N?+@I@Z7@ڨyU"R5W%pʑ7Xqv'-WAZ7@ڨyU̯gg-((8{vBX4 - mT *_YJ'/X,999%BrnQ9&~eqz|*?aleKFțP"pW,<=XD - mT z~ei irM(øW,#w/W%pʑ7XO_Kdٜ, "HKH#o@~-ֲ4m1:Ӌ*=uU{]I9.8AZ7@ڨy Բl[70cj_HKH#o@4~e,.Jh9"Ѡq+f}. Wqq#Gr910y-o2t'g" T0o8ʑ7Xpvz6/AԄ^jk*d:G_?I^ _+pUf*Gb=Ci,5Ͱ0RSSoOT` ^ ʑ7Xe_u#d4Vܠ`m;x9pf*Gb=ɁJdYޯ4y*+ zY"f*Gb=dJ7E Jf +}PLӝpf*Gb=l6 q^e~姰T1hrM(֓], @LțP'=8¯'T z疅_(g0y<, P`4S9&~$䆿Pn`4S9&33*+ @LțP-5ܦ%k5 , @9LțP`=Hw6^gJJ @9LțP ҩ?3HKKF~hrM(Vd̢K6b_(0yU"?-əW/";!C\f*Gbӫ9;kg էrM(V?]e\}*GbKcεFpyU"OoDW/KqOțPAzR8<*?a[էrM(V? G~eq^>#o@JE8}pyU"OOqrw;oW8gYj#^\}*GbӃP5tbҲZ"q$GէrM(V?=uU{iLNjKb-KGc%?<WW7^}OOțPAzK>R]\[hR_YH-˦1u'/էrM(VXQ"<_X[':߀}735m`Mz?7H%1{_ 6Qnqq#G/ck U""G(6D iyS@^AJW ԥO5rZ}'_Y~tltXڗj:>Dm -躣Ae ԁ:b%n"J{mJr~Ykܶ,k78KMM\ǟ*SyB!RSȫ3(1ɺje\_-bw, ;"""444222))h4Zal~Zj#ȫ3(` M[re],WJϏÇ{O N&)yV*; fb-++?x yu&EW, Eȫ3(7=8¯x }[~WgP,‘eWyuPZ~$fTT !ΠXd@h~rKU&WoA^Aot9D/Yh'eWyu ̲g"rػz^t3%%~+WgP,rB5'吹}OFFFZZ4+^:bYt9|%bx @f`Iȫ3("  3^~Xdދ/ XbG𮸸r1P,2.Qa-@fpOzLW+_[@@e P,P&@@e P,P&@@ bPV.nz: {Gx(+! q ^Q,ʊ@q1Dox/(s &b(b(b(b(b(bsebk@;UXZn P P,2#7 Ub'Z ߎ 9W,EL>'&,o PP,cg\q_ f(ӟzI)@@Fl&C Kvx#jbSo"&>1߷#`0bU MaZL|Y:!ʉEw/U5ʡ(順@(*.[Cb4-Kn.b((Vd4HG4BQ븶josj2V+&@@&!Y/Og#}覃z}FFlD((V߼UM:X@(0έ\UcWTTTJJ Mdsrri bK )ұLXdɓr7p|/kK+qΉ;?8(htjÇwH˕O(c57EDD *Nk^gl:erC5ؽ{ {;lJ_[jO7iPʥV!-w;ʴWEQƆ/\VlXi b˵#ghI2Xpow{9_[]^ϱe1ڵ},[2m3M5NKKK3@I@[a_Og;>ؽ{ZFZj;"[7g ԛ>U6e*QClH{/&dd|N RJGrKLMvU}<9ѺÏ=^Jd;ĉpD[#h5p`~۶իWc:a{QyBǥqW%]uEek'LJGײBMmBNWwTK ;th͢woAVb Õ {FXWjOY6^^J^5fL [=&f--Q)n>6}K4?R\}lح[G-4jEXO5||U b2NּCB&&/Աv8n@~-+t$,t@N,:SRҎu>E~[i~)U+ &)6$=z A6^^\To<{6U۶-dڵ:?ՙSo,6v;ig8GCiի͉TaFgΜH?|Y=zԩ=.7#&a/y$٥W$/VV ԛ2e͛qqͬ7JxDi;H9R:: K ZҢE#P9\wfo18: QX' E~_ziwؾݴqP,:XbU0 xWVe|*Yzpt?A48ZV˨$iH?N?xpܣ;?FNm^'G}}}̙$?3s7)p{%4 5[6mD g̘K&7Pi;no`,/='NwКKbבMRĤIt-p`GquԢ^r޷S6^^\yvܽ%gGO\TH8 ;e~˗ VAI4"IdDdg +tIvCN\Gga%5R0 v jPZp1mGO@[bՋ+P~]ѽ{g6n@{DPWB` /zMP,XrVlt5[.]/-8wNq7o_xK(,< 炂=v} oU5-OePI\aaaP,XUcנ{!.+i}zrτbŪǹJ=,HG4BQqf|JOmv(xP)(8+ڍ=tPC 7MIvțDž쀷ŪND4C!둽mkjwkHJv-@y[m8HCځ'jծLSX+)Db2rrr bAaCޱey#ىaqm~])l6抳%?x_Kɲڍrb]@i\9'=eJFsw0A˩4ZyyyT@I@,Kch\7Lx盍,ۯsEvR7eHW!%7!^Y[><(捅ڨhޯGFSX|X`f999f`0/ݠ2%@rC3C(+9KIIIł-Kۭ%w p՞Eᖮ.[p'Uh'#];idV6serJKxP,޲F155U׳9Fa#I 8ޑp\ . 4J>^ OhS"$F,})()`flrx~+.X \J3 CZZ sIII4|Bb̳R((((((( ))!)-W]@>L4i]k4iDpʁ !ذK%%oVJBx)P,(n-8LD")ldxpJ0hTXdڑ3럝 ({χJ+\ PP,2xީ"sSjbq1 K?֊w(y82pjl~T^15X)@@å\_ &Ś7E *io PP,S߾6biFhF '' &k{?b'Xd׫7Uzr|/[L 5 'xU`P&XvSbcÍN(KMxxx@e@˾vġby_QP rXs/_zMx'P,br/X2G&lι{5z~JW9iv Z{dZSܻu[,ίh}^X&񂂳w/vvbj@h-nbx{Y,뉉~:*_MSR#u9hZx)P,zyKWA'A5=hXrrr`Y(Wihz.H~ei9y`6aY(oӤld'`e>X 3GΉkɿ:t=u@@<}f֭M qP5/zhXrsxP,P;_۪֫?ո&辯pj2V+&xP,P;e C,6%Eu  ٌ,^ Tw=ϭ\)r⛐u\[GEED6''G|&U?,HŦ>z'52,xCDDD|'*>b99ʡ(ؔWGS#5 ϊ-Lڑ3dTl j52|~FtiiifY|&U#b;w~l UboÆ{w2$~5~VT2c__ƍ:thOG:v|l;%&H+_w\2zt MaTI͚5~ݗOwOl_?I+aΞ*Ut.qY@x)P,P5+vѢKɈdЬp}їibylّbJدӦgˤMWKbq]w/gl١Ck{wa(Dk۶-X*P,P5n(6%e+)ibCBcˤؘsFjP, -o9_/4ݵԮ]˩S_sm^P,GO4iPh';th]rjժ< 9n_:ujݲe~Z5j֬< ##%2K=z"|J(4PrDŽ%uZ[&޽%ˍ`PԬ]@@x}b/i4 ,, bd6&5(j \֭MqP5O;s P9rN\Ry<_=,'44_fwP{ޢ 0L&5g#;1k󣢢HJv (WiHf炂TCa(’\I$ڔɔ#>bxV#c"<w/?pGFF ?%6͹ 2reNOMSR#ø}ȯaEpJSX՚'n=@y@< ?~ kW;S8@.x+hb &Uj~i>ιIalXW#]( fZQJDJO[ ~3?HXkRR`Q 1[b޲}?FOn2vOoDt!QcGf~b eFcjj^҄G`f^E9ȉsf]aZ%4 MaiJG6PKW(1̲d2edd$2>>^ӑ"##I~48dLX`zWס:fBǢ#qj 7`Ynnj#I`0󒒒H~49Fl=)V|xSNꧣбt\::Z@8fkF#ɏ&i9?vW8ꤚ~ެt\%X,fǜH$ŞLp PbkGΐbvF2Xd0Xd?/Up-@=@w;yK M@o^K?UWŒ\>Kk;T xpG]jh4Ͱ,(SPPЭ[7_ м{~~~*Uj4[^:tHNN6 ,(ѹsgaɒ%K&MTZJJiZ;vinJJ e-+x#P,%fΜ9~!cf͚%+-Zt%~K:TdZ1BPbzꩬ,Wv]V‰,۶mp^Obٌ,(Ӽysz.\~b Vⷧ}6lBٜ~-PbWtRW+VӾUV%FDD (#ŒbbiκvZ~{~VlX  @-vǎ+;v]vaaaFӥf~-PbDw_tiѢE(Wޚ5kwϝ;wȑP,(#3gJ-~} 7n߾ˡX*)44M6ATxX3/]aÆe˖OZ~>Lܸqy/&v'*V栫VjӦMΝ{iii3fh׮ݣ>xbVV  be˖ :\۰aJEOt"9r\QQQaW|(gNNl6 Lgxxx*/ _TxXJoYz6[%jЯTHhڌ6_@fYb2222hO* _Vm Pbennj)`0JЯTHhڌ6_@-MOI4O5 _VA((XLb2((XLb2((XL|bh fIENDB`kea-2.0.2/doc/sphinx/uml/select4.png0000644000175000017500000053637314206773363014154 00000000000000PNG  IHDRR)tEXtcopyleftGenerated by http://plantuml.com09iTXtplantumlxVKo1rJ*Ғ'KU!gK6mTw]z@go ]kj>f3|ß{s)vj+=ɜ hshF,q/B@Ae}P9ySo a@?h&&PDּ}x|ɴ[qϞ=wvM(׉Qrٳ-￯C8qBm[|yͿSOь7s8}M8QձW5jW_}믿YIJJZng}6w\zܸq#D\ M4r+77rK9_3f~ǎVxEU 3k͆ׯ?baÆcġz]\G2t!Ch'O,Uז-['W//bɒ%{ +W\y'|rGQ\x__PիWˡe˖_}SWo޼y3g7n\n ,(~Zv1GtE#osɓ'?쳢qszJ\u;e˖*1 uoW^r/&&ڻO>ӦMSrIn֪Uӧ뛹.88X޽{M%1zjڵEGo'XlW^y{d/1\|1K.b]v>> )͛7/P޽{o #18,֨QCŅiVIxxܾ}!z뭷fzzzժUEk׮̳mҥbo1sb"ԁ\[%99YK.}e'&mӾ}{ܲ[nӾ2[ԬYS<<}ج[!&4jH,X@tu&79]^=mlE!򋛛Z~iJb"r- ɒXtv矺 Mo?qw]\UVYOqxŋ/Yn2{l3uTm%3qXvm˖--gn,81ܸq{СXAΝ;׹seˊݟyU̜9닽3wԳ2lUbbgVZ y6l`8׮߻wի,XP=zh~s0ꫯ>1>|xܬY3yǏ5jT6mJ*UX'|N~bҺuk1S!7׬Y#6ԭ[71aZʕ+4z{{_vMvw.uʕ+[n&&ϟϼ -1IOOߵkW~,=ܿxk;>|ܔFw}v7xCՁl'&7nʕ+ @иqsΉN>},U'|b f6b%__ 5O~w-7(eyY.޹sg8F3Zj,~BsZjUpŋg^'N^`JJvd?1L"_PJi=$/hOrS~M69 ILd!3nN뮻ߪtҖ򔗈IIIZ:r%K;<gYNww'O6l4i"/˳|7bc=ve(|yxn儚5k&$$рKfp 7>o߾LWl6mTfDX쯯'*UJl꿣8!66Vt .ͷs-ZMZJw$Q+_g#Q'&qqqO<񄗗Wr.M+ޥZdMk%)?ܢE 2Lo?̎$&ڵio\rE^FEE)R%J)|)mt„ bgϞݻ-)/]TL9eʔ%āY2D<2C̙3իWdd?l&&ʝS_~e/! )˗:VOdDXnOF~V,Sx||xRDE_ϕ4`e"wePF )۷>hҤ8ޖNLđWVKk׮ 4bĄu-WHLNV"~|A1xb;Ӕȏ,ZYоd:w\$U ݿ}mظqMׯ_O/nB͆ d?'} pKf1V >L"Kó?YZl|*/.d ѣ|~b"-[V9$2dH||>j.$_fFO%JKcǎU%7Y~x҂ ZuEjڴ%,,Lߔ?~\n_zgs0;S̛7ϒyygkmVl Q;AdܼY/^t锔,USykR1SG(;21}Znԯ__4k@~Cȑ#=D؉ /Y~+dnWl8>HHHpss+Pmϕ  7o:Zb"}ԩܔ0X%&-[^㓘MClvAuV1#쯯rN:W\Qr(..˫SNX%&ɓ'WPAl;V?N p5?SL/W_}䨸`_}ĵ\pb9sDo߾ZgŊӥKۦMyOK/o2|___9tg%YLݧ~Z]B qZ/.0uԑs??;vhϕΝ;9k׮0~xK}g׭[';v^5kdh"7޽{?>V5o."S*@9q#dc̭mtɖ[k 1Sqʕwx:l q~q<@FFJJcbb믿j7ĵ}jFWl%;/!v+ONHHD9K.ŕcϟ/Uae>|X+WzDzzxUVmڴٳ [N\8ŋY7}\ ڱcʕ+Ձ|c„ ǎS0?;uolٲ%..NHLT$&* @Eb"1PPݸp6|7en\5l@?emܭ5J&&Ǐ9H)ux ? 1=Rg6 ɲQvAGb@Ebbdb`aaa111`~$&T$&vJ&&s  V #1"1SZb2gΜ( 0?*;Eb$&T$&v'HLHL OP) 1"1S$&@>Ab@EbbHL|*ɓOrի?[Osj޼~ÆwP$&@>Ab@Ĥ~}rJ͟?R]5kg|u5thZᑘ UG}Ⱥ͗^zG*[Oue"1 &&ݻԹsqj=\dpǰao-Z˫@^{6sҤ˗/5p-ZС?,^|)-ȡR)ԩv^9,'HLV8|8XVBVoĿX"%JݻcM-?[gծ]L{aB .}o0|H\sVb}I. {-{ߠA7~BwPm===v}~ƌaj=}_+Go/"1 '&y&M===V":V }Eo9|E{,. 6z{{b9qbܬSjzOswwo׮?&NKd, 1~bOX-K}u\,tXm$&eY$&@>Ab@ĤA[}-MCr$)7WϏ4i*4t0dHw/KXb.渹r )OkٲhE _m/e, 1fbru뾬iӆ;z(] S^zB|蕩Y|>%eHL|щJK]ڃG+/Y$&@>Ab@IT֭ZzC{պoX U&&_$&@>Ab@EbbHL|N "1 *;Eb$&T$&v'HLHL OP) 1qclHLdbB)员Pݸ|ݲ`sے!ԜDDDDGGg',,,888 `N <%N8-DFFĨg gΝaaa!!!Ay*⯶8!ⴈ5GbȈ<hW[yJqZɉW#1`O\\\LLLtttT <%N8-'%%g 0@G"1`$& FbHL 0  1`0&@b`$&LHL 0#1`$& FbHL 0  1`0&@b`$&LHL 0#1`$& FbHL 0  1`0&@b`$&LHL 0#1`$& FbHL 0  1`0&@b`$&LHL 0#1`$& FbHL 0  1`0&@b`$&LHL 0#1`$& FbHL3?Ter gtxmܭm&8 gtbOs-4sD_3 o"OD&&w-*pgQTm$&K7>qw\ 'NFbY/.hEkh_T%%oz: p\S~⟢}̺$p+zB2.YR/31HL8-m>Ɋ1T 1Ԯ<ЭnPJLKJV1HL85u,w8 Rn\J8*jB"O h=dq4HL"]?(sw?Np)219<~@i1$&#1\LL5yzĭ@ɑ.%e&ܹ3***..N[S$&K!11EdЯ"##cbbԿ%wŚӧF">XHB߿-Gv[[vvrGy致/#T/.%I/ujҤ˗/5p-ZС6yիW:p`~}b2wb͠CJ,^̽$&x􏃃lj.Ç UݻwavM)$1l^gSD}_MI ښc*U*^}Ӧ 6E {yg>l"1 Rr4i_Pҥ8ts ;%&qq ,Zի?kݺ :WlիXjo$&bŊ(Qw7\e|QӦ ^VL^ҲeadS^ɒǏh~ښӽK⹺tyN6ikL've+bŊI p~$&KɕĤpa={̓9~x|FooѣW֩_﮻c̑*UPop)(QRٴ']#sO=-Z'Ѿ"j޼wZ*})t69z85uȡe)Ѹi&_ &:|Esٚ# HLdAbڦM?3ԿɓKj֬ʜ9#h&&˗*Sҥ9vl.JWZu뾗M"k޼/5G < HL… zծ]UL h~ x 6O\>`@Wڳg{f䎹{/^jՊχ[/عs+qbNLOnnn=4l9uj[۶*.ʕ /Zb"`oo+]}Ԩ>ǏO~DԩHL3,U'kЯW!! mѢ}_yܿ?h۶X?9;ЩSg'#G'+WNNHت?}Ώp)*13g߶mٴi j*3CC'f Yb~U/@L߿k5o^_x1:fL?^GLL`LLi=0ͭ]&5vWWv(}E9/oر\pao+q4HLGbdMf fv9"i=t9Wl^)o%ɓ?ʣ HLD~:C`"1 RDE-oիZQwV$&#1\#*׋p~$&K!11EΏp)$&(.Eb8?HLGbS HLBbb"1 RHLLQ$&#1\LL#)-"1 RdbBHLgFb۸[ֲQ Z\@|jwBBB"""տ%-jğFğ)222&&FS$&ڹs 2@2? gS~ uxdddDDDZՖ??SLLL||HL'.ţ/⯶OiH㓒Կ͵DKL΅}1TpN*{<-C ߵ`Ý=FS pRGZ.)݁sỂ t[:r ghU/EQhپ4p+qMZ^3vHL8 =˸dIƫj 1ԶH&&+jwPuSzBA,+2-)Y 1Уށg0$&qn\J85!pqOo׍Ww!!1#`-9(ʘ:q.=$&qdbrx:ؚӽK␺tyN6ikL>:=z3ժ;T_W4:"1r0N$&:|Ϟr%ڥ F~_Yt{:}NX-7ԩZޣڐ_jx|Z_ِu-ӏvb}`vb[sdJgQ@[Gj1]&|M+:;$}9Db'Wv5*U*{:6v-{߄ SSD={pti2$šieE-ZivԶvVV<>C?#mHL[T__!C C$&qL:HL۴??_J-\UZiRvn1z&eHѾnriӆȚ7 9Nbnu`vў25kӧՆzŊei쯐~it!!8HL.\XWUЉ؝;p?>\l֬YE˔)$&=Vǧ=&'77{JKmkɲfgm9ujץ9vl87h#Wlh֬2-v<o=;$}9Dbʕ~5Fݽzu xhhﻯn[X ^(9r{:|xOq>x[Ϯ\womlq/V5;fkSۚӹs+\:5߸qf@q_U%J4_~KL[tى'`4:"1r0οJL!mo6mZAЉLT+CQ₼JD_|x0u 9$ԩE[\?h>ݺ! xj^)?"΁ZGԶ\cfr…/+_u-[6Q-Zxjg *V,it!iEb `dM"[a=$+11ԩ'%}]E~A"TJJqHOX,[fk̹re[tԛeDo*:=t HL8"1>gA9Ie)#!8HLnS:-z: 吘KѳW7}O:k1q:~⡶ڡp-$&NzG^>~wn#B|qǛ߸p$&̩z5k )Zu K 1g7~rQ5-{WxeH#17m\Y}zZ:`*ג";qLFb oljjלo?zm%_& 1.?L$^Z"?Ry@HL]>9jάR~ҒocLѮ8f7._Q\Bm{G1Abh{u-f 21f@bP◖lz-&Np9bks~0#:8F]߉_:0N굤e?S6"jr:0Αooy#$Y: JIpJ$& ⡶qCZRwZҾ#Cb '֯gN]v_ q*NA~zS7ިt~xpf$&p6|jg/ockSc #lzBn>|cGkjvu  1p]^@wx\2U5˧vDZu@"1XWOi~u7<R NJp.nuU;x: p Kiv-&N tTu HL8vtvaC[܇:X$&%z[V`[zJ7?_W[pӂʑ#Gbccn]xj }}.]Z%&G^x6l3ψg}uܹ@#444--zڵk~駰0???HL}݅0 \viߑrӒo5jlR>-ѣm$ݻWQ8hРbŊǘ1cc|Ҳ\j׮]̙3".+Z:1HL䲟_7M}hX*UM/V|˗/U&uUVMJJ{ [KdϞ=r/WJLb4uMO4ui`Ȍ3QIqFC"|󍖘;S@ӧOgXjd9eWJLnllޮ^_@n};$%%VZUTYvmRR޽{O8lX-[y zxx$$$~jjjٲeP/UBmfϞ]HWJLkkwmlu@n#1k_^Z8}GӧO[,77{9)))M4wuȑ# .\jU!CDÆ (ThV\.V|U\Eb >cgj7+񱱱jgϦ bl?-2Y/︘ߢ~̺Cb w$^[v_@n+TՁ*neZ&8 m\o߾aÆ]xQǢgX]㕔k@b H Cicb䠌O8>o$\ϥiz=9~M $&nuz]R okiɦѳW4~dS.l 1p{h_R`Y_lV|!1u*Iڍ_ۺWp;$&now-|Rsg/ V&njeZUnm~LLDhpd"uUZR[]>:.v,-(uY;\-@]&%_LXZ8umY$&.] 6ZVwywO/>8r`[QRDƍ:>bFz:Ʉ 6aNkt9 OpD::Z@ +$&p,??G$SѢEuל9sԁsyH/ixEEE@ń%w=Bbǒѣ-Zϊ [\̒qf Kf-o.C W\ի`ZZ֫Wok^xA4ǎ+s=7nv{yyuܹTR r{UBbǒG ( ݻWmڴIt<<<ﰖ+WNl\zU>رc/^\<e˖_]l?~ֲyeD*$&9%&iKJ4I Kf+W{Ń=zhC'OOOg2+Vr3%Q#G5d E[h!6۵kwق vβ,IJbbb1A2Qb"7}/v6 V$&p,[9%kʡ'M77:6lӧ7h5joXtf',m-;c /\ rCu4 V$&p,__ylٲ.BK&&F4h ̈DNKLL,Z\aÆik٣G]?(7ILsJLbBllp+8>19u 8:w,6ի'6}||~Çif֬Y^21Ydx|w߿|2벳tRٱlʕEH"7; ̕\[v_ V$&p5kŋW_}%sotQ /ʔ)3|ms7nxg=<<۠A1Tzu5k,Iߵl-|˒/b#pg̕K7Nv萘 ݸqѣ ٳg[hauL)eSSS;o9dd]7GWtHL`bw߭(@0]b~gBboZn=uTu0?{ @rtӃwvv萘@N.1 ټu? @rtɅz @rtIbtlh.)%&\XVY @rtɍWhv萘@N.1IKJ^P!12]b"U[tHL|-uϔ_m!1B+@hs$&!1kwBGbw1]ǑإhRS*jkmQ-REjRUR{KCV"b DlGKB-Dv3{_qsνsgry!14~8QⱐCbhQb^I >$&9~޳[HyP@[&m~OyOP@[;.d'Cb !湥{R<@}HL}W<1@}HLMHOT[@}HLM~gl.r >$&eC@>ܾzHL!1T3^e_sHL!1,5.]/& GCbپc†Li$&u 蒹}ՑCbӭC߾$&ڜv+}u)P@Un*-HL!1Kvۗ$&K[wxZHL!1ޞGۗ&@}HLvvΚ>%P$&.r >$&]%6Pp[Cb `g} "1ԇ3ܾCbn_bHL!1/Y$&ۗ2@}HL˗?)waHL!1ٹk6ۗ,@}HL[w3Why6P޹W ;@}HLWaf]@}HLuu[_Kl >$&uWG=$&(5.Zܾ>Cbآ}=Ƅ .waHL!1lN S*@}HLr/ ,Cb;?LOTM>@}HL qvP פK("n<}O4;@b kb ;{q?{Q{@b kNX ?$&foׯ$&@zw%y{WYEɱٰm$&3]MLmK|uiaDb ;6p}fk4suAyvPKmgjA"-k{(i޹+_6P Q;R ]ak3HL!1:%193{Ƶk(6̬$&9DD,Jec I#1,#1:KIL}sʗr"1ԇDHL(/%1Y=nۏ9-_ʹP#1ldɫV /\Eb֑P_f@}HLĄ"12#1lHLDHL(/k 1,#1:HLHLĄ"12Rb2w?i駽6liڷ#%? RKV/T-әmw509[ s0dzz^T$&@bXFbuML/$.u+ Խd%J8 nZ!o^dBvM/VL*M7&=zax%kW=q1{|Qg}ٹhE, {ﵟ7響GPo]ۭ[E bi\WAq {w;+VEaaioXL+P طw|pUwpW| zz?qF5<{^qe:kׇ {Gَ)jժC~m\/ ۣ )@OU+k 1,#1Ѻl%&bܴi]MLܯ XϞ=rܸ[ko|qסC{zxٳ.\,_~Wl(sJ[tGQt͟qkLkܸ2").\lRGsz'/\ὼf/^<{qwFAڶm"n-Z42e3/Pڻwi5˕+-7O>>}:7-[+OTD !Cܻwo#&߿\9l^qe:յz-Wh8"E /_f̘~P r잞6W$&@bXFbuYLLĚWjnVA_(:_|[y(-[l~+Gť%b ͛ *0uLwݙ?c^픡+!KÇW+-|ٲZh<4.q|.:t$AAK v+qT4E)s OQέFJOyb;**@>iOdW,'&?ŋ j6n\0[9D벘*U|РŊi%7>o_(.~k`QbJʐqbt`I|޾}31_*F\V-Ӆ͝XiuwR=kH4J, MB '?q'b|2sJOf4.=VL^v;Vlggʹ}{BC"tmWC۷,I."1,#1Ѻ,&&ʷr~i6[*noZb2dXq{x2eJ޽/ӦtYIL,ɓ6Wq+\g϶v-Zj,)# }v^2_)s>SuG pb?8ۿ0aر C:=ɫ6W$&@bXFbuYIL6[~M~L\!ߎ ~f 7hiUݻw{1AXwx;7Of_?=KKnӺu#Ν=o(XH!?IL0Hyb\&QhV1eH^5_yh͛w 8)7yHLHL.+#+9`xtHw("KtH* ?],(b//Hea(6̽(Q.ٚw >Z2AiΝ=ɋ]f2==ɫ6-k 1,#1ѺIL8>n@QHLHL.s;vl?tHLHL.bX `֑P_$&@bXFbu$&5h eEbb $&e$&ZGbB~X `֑P_$&@bXFbu$&5h eEbb $&e$&Z$&gfҜ6)K\$&9DĄlHLr"Ks0zt.^ vaMV|΃1QRTj*HRU$&?b?{X۫ 7[ٴϊabyVW>UzȑhU$&Yg֥׶vVhX-<1qeS\Ν/\Eb k#Go7mfo|탵E[xy۷ȓ[xbקJ7WCbbccZ422ٳaxn}ϴܬi9`fW>U,_Pd.տ[s@>歽sSXj >$&xHzR?w\ßμ}<YZTkHL!1n::pƒmv~iv]J<YZTkHL!1Ѻ{)6з[.K?AbXj >$&u##zv y^N'b-5$&h^p2nG)!<O֐Cb!i Igv`~Vl%3CXj >$&{쫟=kǘ!'a4֢ZCb>~ K9L԰@}HLTj]o .aXkQ!1ԇDKD.߬KMa}EPUРϖ]^/ia-5$&^iݶ-~nyOkQ!1ԇWo\g`~W6GKXj >$&v[lփrkQ!1ԇ.$&6$RLPϷ} kQ!1ԇ6~^Y| GXj >$&/tlk6 y ֐Cbti'_\0kQ!1ԇ$>ݠN#/_`l|-~'@bEVnݺu@#1 zY>>۷FeԩS=<z2GV|e3fmѬTNP-R·r` $&VKM;M/;!A kQpZt͚5+,,LUx~vW-d+VtvvZj\~~~CLO O®ӋUj911J92_4' cǎ5kLNN.]IJN>Ebk 1mM?Ըy jga-$~/_,Y"<?hGsƎ<4$TXJ/0Oy0#ϟ?իW1sǏ ?s4w, ֢UVm֬ɓ'=ڸq Y=21^^^bq+|mwիW}NKKsuu-[XĈhbbtpUj|ICFbGq/1w/_^ ^x}-[VhQ8wf*oZN:Æ S:s)R-|/R u=vaG}X~}eېed֭3<3ydq5kJ*9::*{ްaCʕ+#711w1J ̘Ȗ-[z{{1w |رVZUpaѬVڨQ $}Z&~&f)kQˮ_~ MqqqJS/]YEll͛7 71,ӫ@\W\_|Ňe0=TJJJ$&9&->qg{xOde- 5Qkb.\xԑ䌤[\ >]'A{ls- !1)ǎooݺ%OI},ܧb33UCbn=UҺm4֐CbD"z}3vQyZTkHL!1y|gg%EyZTkHL!1yL-T[kZT{HL!1y''-\=):V2@}HLoν֢ZCbI3o֢ZCbI617AwaEP:[~r+^L@}HL՛kL'a-5$&<څ[|+uN#f@}HLjoƝXj >$& =U-v1y֐CbbϷ#ZTkHL!1ܽ7vX+YZTkHL!1Ʉ.%u[ӏNL\$YZTkHL!1Dv^֐Cb"X쳹{Z|<w:F>$& 5"1ԇ_-ByȚ>(R M 6 0E !1ԇ_ ^X+~}Wظ#t]kw*R !1ԇ+Vy;v<dVUxqkm,fIP#@}HLIqǻ|vبybP( cʓF$&##] t)k(qg6.3R$&h=1*ߥ.9Eޮ_)ɦ#P)@}4$E]rnwyx\\_uy9I$&h81wvr<}:4>1q<"1GI ގ3GіQL(۬;w_l"1GIn.J,ꔻcP*P&&=1wkÔv(*̬$&2$vQJ6LILO?LQTNU̎5$&2D[FbBQ(%1CBBΝ;+e >KL.ؤܵy$&eR{zzn߾ȑ#_꣹$}]GbBQ(%1Y>zU|||###_, 1G[I̶Gl EY &$&h+1vXR_kHL(Eb %&w*KI EYHL%aC];AbBQ(h(1ؽG宝 1(k s>zηRg{竂ĄQ$&JbrjßΔV~X̝?~nڷF}i f-c=w=~~F}OHLd/o!weСrd%1yz2*T]9tT[7jմo*Xȑ-c=w=~~F}OHLDP'p0}SNLB.Z4oNNNEw:iƕ哔%![K}zjS{"}>ve=!1`&[NoS>&MW>'&꽨l`Cqt0Q, M%!imltL'H.]ZRLg,d Lw1743t{Ҩ%mHL$b:u4~!C*UvڡsέXb*UV\_}Ղ ΨQC''5jXBtK.-OFb2x#F8;;׭[w۶mFϜ=ML/$~MW&%4{ɒKp>ݴCJxydɄv/^LTnnMzxAAKj׮zℇcnGQ.>s)ϟѰ0$~޼*Vt=C?ztmn-,~q)_B 7}VX-Rec1@b޽ۋNrcWZ!_e.F߻9K8iz-ƕ OZrL!#Kaf rܺg駽o88aW5Hnr/ :0ߴHL ЫW/jժ,YaÆ>>>۷\2gԩ'Ol֬YVD'""N:bNHHȵkի'9sSӕ;::mذQFM65~lVbiӺb]_-p={q˷`Jxy]1g϶pah~e__7E U,fʹƍ!Ґ8… -[J,w]ořŋ'x{ϱ4.q|(9B۶MS׭[ES {晢*2}Q{._fr3VbOĺ}˖S89-Qiۈ /Wb\NDK^ ?#pYzv}++zNI )^/UѡoN{Oar eє B0Gɾc./w3tQW_Bxxb&$%%f&$$3Iyhg'G&+7o*LRpaiBe11kW^-VbzJʕ@CW-[lgӧ7#b b[͛ *0uLw)۽zSRSC ȯ,- 3NHBVZ8yNnѢABBBӸŻtϒ-'ۭ93-R|1|D95*>Xo؎ OtS!}-ƕsOd\g32.s`_&OFsށE ;;[o2?o޼ W?Nn7n}qó#.i'/9HLq`|֫WOCb`+:;;WZ5dXs<40>ٳBbRTA+Va× _ؾ}8NZE*U+CƉIRҁ'}{|E5jRjy.l/X,btǽ{YFnaaHoX8?C˗3gTz/6ӦqѵbڵӔ߱Bge;x)9/rk1L'{"kgd\ܑdp~F޼y_!.c;w.&̞Lľ4.=\_ohP$&Db[nG&&}-81ӧ,;Vyh[9?4ZXc+_^-\R+VLR Ж-_/`kLjw/޷ enǀb{ٲ%!ɋZvxMCe%11s曏,&y+Ԯ\:+:˖-g>>fbӅ5o^?~G'[ۡCs] N/e.]Vgq.Z_hΕ`n 6e[%!+%V!ߍ;-7JbbEug%Đ87[$8Dxba'pY|{-9;Pԧzg^x쯿޿爋9o0"OlAgeݻęFca舟lj֬l4 s4/,;SΰaÔ֯__6$&iii[<ɓ)Rf2>WRR%GGG' 6̓\r111̙#UCQgx*TvIIþ};e?n߾eg>p`7?ynn:thOÑ M`aV(4mg\\[Z2]gz:]'=W#5R%X֊…߈BQ,^_PɋŶG6̽(Q.ٚw ݱ5k,iezJeZYo>dMb0Gƒm]%wJ$&W (5 s4Qr׮bbrwǎQ s4u+ܵ+P9HL:CF0GfBF0GٹkBNvĄQ$&Dbr5]BbBQ(h"1IQ>-]$&e"1`&as-ws9$&e"1`VLk'Rܽ+L))?7](k׊_.ϞeV}c#MMj*H/ HLPbr?B wXyzz3~ZS?c}#.lmS7ȑ#\꣡$!"Ymk5𛵭M$~oֹsbcc_, 1GCK]{#VbG Q- u*.lmS7+:::..N@}Di宽s3O{ru9<["~͊KNN岀Pm%&6>:UyȠOKc+cP@}G#J^N#:y ~v u!1GsI⥘%ۤގy?{9|oVB]HL\b"=rv[NsWxbw:rBbGykxOVeދ)}R >ZLL}_LB. 1!"Z9ۚ ܅$En)]z˹ݝuxW] >MLS/w%7CNzqk?{O}_] >ML{>!tW7V_.r*BbvAЀK1:G.,X^Dw>ԂPM'&5ݣ}Zʍ;kZru~ 1GӉxЯjҰv%$1O6G%rjAb@?)wF]m|4YKy{܅ZCbOʭx.z.{~Oc֗S܅ZCbr_wu 2}Gyi|7.TP3irjqE %OvEP@}HL%~պ^޸S;uw$&Цr1܊wuz y\ټogrPl?^<xo[ӏ'؆6>^#1ԇĒA?^҃:|~#rvPKti;oAz'uBlɾ/ s$&$&/&Wm!&^ʎ6lvpp3vXA$&?orvP'Wh=@^u:yaLL<==ņWPPPfݻzoiiie˖]|yLLObbtNL>)rvP'u3W7/LAv|4yۧYG:u l8~ʶ!1IKKkݺ~g&O\H5kJ*9::*? oذa ʕ>g-7hY$&;.!r| _/ô7m}rqqqJS_tI8؛7o]rGіR'@}HLrFً:\@N8;gZ)7,AN]'@}HLrLRtzCO]6-9oĨIS^>Cbw,9-yW7NE߽~0Q>Cbi!4t%VC6>YM_]}K]'@}HLԔeߊ;! ۫WBQe]>$&XU^8$ .\I6w=܅"1ԇĊ>UͿy(9{i<:afJCbb]g/n~h4]j<̤MJ߿-(MC$&X]Z|#7^My ӥl34tJŝT܅"1ԇ oNQbCS)J7<*v<{Cb۫[2ag!y@ն-DސCbTŝTZE5nWjO1erP-oj71:7o{$ޖ4{}%waoHL!1zĄ߽˵*iυ[|^x< w/V,waoHL!151Cw81w`oƝCXͽk> >$&;_dg!7{vq<1ze> BbI.tͻ|k;cv^e߼p@Оccn܅]!1ԇ&l 4 +>;]\UФvu!waWHL!17t{޻vkSnZ%~Ϸ+$&Nwzrn*Ej\B@>| E.5 Ab͹vfsM}Ƨ%cv%TI堰a3>:|o#1ԇ';to=*e|LCn٤Su{:޷}_. >$&+wwǿUnܿe~|<~0qwԴM'o.O. >$&6^͠om7\t)w2< ؑ/l{m@z=)x@7zC~Cbbѳ+M5f]r3NN^C|*vw܅ 1ԇ>$\O?{Q<}M;Ng"z719Fj]]!Ov@ ;Ab,vf֪Rύ%Z Mw1QTuiwnUbINN^zt< ;AbIr{)<6xC*g7X׸{};E[=>vO&Wj܅ 1ԇFֱHd͵m&XP#_M'ězݿ}!1ԇnSn:}r;UFlٹ$Ts~~k^+{yȌ_q#. >$&.!_7ub]m7,Fnh2`/`ݒgc/,waHL!1I ^]\-׍,;:rޮ_n}o`G.3iV N[˱~6JsWـ33.waHL!1I$&_Jb5' X{G摘CbHLld鞞bY{ȑh{[{h4 GbIN"1R'Z'88822RAǷ6@摘CbHLl mlӓ=ԥm$&$/OL-u߻zJ¶CbHLll?1 ?/.l >$&9n ?I¶CbHLll?1u]6@}HLr'&T-a$&l%& ǎܔvМ;trv뱏;>y=D}] rU/@}HLrR#GּryPF%euFMzضmfαR=9g"194xٹk]qqq:tU/@}HLrRVU wr*dɄ 69f(9d}G Ϯ?ҥK/U3fnӧ6tYo]$&o.wmF&M+wFbbו$'=21~}G޼y[nd:xzΪWŒ%#II .%J8 'ǎ_jy|˗Yq & G;WzGG]{gW昞~\X׮]w^dBvM/VLv>>۷\2gԩ'Ol֬YVD'""N:bNHHȵkի'9sSų+7=1#1ԇ$'=21YCJnavTTؗh3xbO?}y')k5o@Ym4 %&!~89{\͋WJ‘h+1B rHL)-!9nay_+smU#&e]4G>}˺o~qss Hnp$JLn]Yh0EbRP%,MS~VxSf}}FUmϋxvsths0LA‘h(1mcS* $&gi7brU1Emϋxvs$.k'pgǧ$/-,qtѠz+غm]ܽE<;9NLL<z-ѣb7>F4AbRPxDDY -X q-\昉%c4@bc\mޔp:] אּr&&i "5\H`Ir ̹7\怉əkt,d޼yo`\nom0[lycUoC%&i7N8q^k K&̩UNο9S1qC|}*$$$kӧOEה?^]>;rG[_+w,ˑU8EEDD[ ˵r@>YILرS\"j?y\͋U#>tk\EY5;pO)l=z4::Z{Z( Of@吘8p@CvO"Wd5;]\G3mm7"S)`?gϞsW\w9$&ѣAAA;PD>gdG'ߍ ^^  T?ftttbb%|>Jw9$&?"",(ퟻB_z<_r ֕t_r7R6SRR俧̐yyq,Lݞ—Uu5\\x "15Cz22y9r9gZ/WcqGu"1ѵē$Wacus=RB\]oO HLtν/LֹE} WQ<0 ]2gxvDN|' 꿕kB[fJTкom}r!1ѵW^}\fRh[|BBg.yr|#1ѵ͆8zFo3)4;\ҙ Uz9"w2ddf}vܑ?ͤp-t?Yrxssӡa ePPHL+|oj6q-s֭̔]9Wr!1ѯ]ߓpnǞ**N֗}:d Is22>Cb_>\j̈́%-`[zio~HLa 媝yÞ>ͤ`%^rEZ~_/w7y앝䪝dol0`sfBW—z|>JPHL˫z/Q^{67W-!ھ!WQDEp`TZBGrvv}*$=ɤ +.ޛ N];xM{#W>i7ɦƃ:'WQ\)w NE5p'r=o*WGQt~rFbS'Xz|"ZR'NS^@#_]p@#1ѩ#g1Z0.Hn 81rE¹7<: E@ 1ѩmތ"W ̮nF"R'e} w@|Ʒ 7ãD<މ&W LҙHG. mE2Ǎ^u1VPHL(F 鹫wtk9Ybbbz݆}>\UB2n'T̔krEbGr\-`ώ9ܡsR6m:nܸ{mm,drt'ڶ69#35MPHL}?fxgUSrGg@::LmlR r9U5LܑiӦ:uJKٳg)SF,߿iʘE4?c>CƠo#$%%_U}2fĈʺ'N 6(7oN2|CvLc3gN``urH{OOag}23חho/ഄ7VH̓y)ɚ5k><(_"DYϲhu;3fL˖-~<11IOO/VԩS… k׮]z21S]9N'rww%٥ Op`)z6Dwn]\?ww񆌬OĘ899\R/[L< 2fkbDN^ǏEv-.ͺ7|cZbbڎy]yɒ%Km͈GU8$Czщ}>)#!1ѝ}!W=}&~kn51III_~ǎCCCCBBڶm+:| ܸq|#n4o޼F+Wu떍21y'9?rHql111bW@@زqr;c/aiذ8S-Z;vLթSxʻ<̅=3*5k֌5o|cƌiժ,%&O=TժU+VaN7w.TPa֬Y7nl80tUڎ4&""sbbLJU92pXQ^{{r cNwHLt!O'JjfCE\\իW`xbFFTW_vM*ZNdffZʕ+b!)))11Q)XBB;wnG"6ĶI?d]!':L^ :.pT$&sE#d}5q\O8H7U8}!^88zvzr۔hW5WM!3ӽ|wk}p{_7Db;ptd&{_)w w|ʮrΐqr Ϫ|>Ab/i7wFY}#VߞZp]bKWpeK $WLҙH:CM}\EaIOux5F* }cyysӡڐ)Aōca (>>87yEb/l ze\uHIěr1g~[1/}Kq{}D_NvdwrQ22v `g^<&WQ02ńř)r7#1їI?j\ula ݽ(w1s^\ƃt&RP$E$W^|1ؐךrng_UUrإ76|)f#)PˮbU-sO.kOni1LNғn'χzzUfZ h!1їO9S;sungUװz5t_۰ޚ,gܣb7n|/$oG>/5d&@HLtmk\ղ3[[τ1r__RQqi?߼pYyxu F({|./:8zW5׬w%ltDG\5^; {xAڍ$ΐHBc'W!gMQCY+8#wƹM؏kGa^;+][P_|\-* '->{Tzb"+/tZ_}wvQ{|݇ו8#S98zV>P$ݮ YگIENƭ;Qۚbmk+~M&qƹu\Q#Y HS..WCfyx>' S&xT꾮x]OCó6kZ[y^>nk]z?/9ҡw&+`9`ZDݎ;8z=67{uS S4Eϳr%1 zeܭKq߉{JU|iJuy!1ёϼoHOu7T*8<(?bƺוpnܧ?YqIz8o5΃ @b#~L:{Q9I_Űۭ-SQ7qWk* *{{g׺2xˆ*={̼?;9|F?pƍ]9ٹq˦)s=ۏZ[yJ"76[\!\l[J6z/t֖hsk? +ސ!W@_NJ8KETcrItOw@HL"=F(v5o<D/n]y\%1vlj!1ы67*W5Dk;v8pٳgAK$&zptGrU#HL%!66V}hM>sǎG.EO@@rU#HL" 66vɒ%3g\zÇpʕr, ]`0|ᇵjʖ-{yD^mVnnnrG^e#4M9şy^Dܴ $&֍3Xn]ԤIyĽ222^*wEllwVT~4iRTT<"/7o.oya4Sb"[nǎgϞ.yo\@+p\NHH]r<^{;F,b@ ӡCRv ;FbjHLKt\@Ӽ.VwyN:*Tҥ˩SD񫯾*WXZjM6MLLf͚գGz8psׇ RruN4<5h իW+V<.\0~&M-[QFk֬]77eʔ|)=&bw(޾}>)SF>cƌTeQݳ9ƦltoD{#(eWWW'1(bdŊrHH _߬?wØ]ƻ?}tffRX cɢV[bػᆱ< TCyP̷0OLllFi#˗/s#+?"Ĥ}III*UsDbjHLWG*W5(\u?ׯ_ߤk6}(IXX{ץK 8eCW^픝tܼyx2޽_lq6n8e{l C. :e4'--MfeDJL͛甝)LIMM-YXhqTؔ.F*U*$$Ĵ'&ʧ~lZ;`rU#HL"#%%L2bF۷oW.=s>i$={SGB3g/s :tH<\l0&&FƁ)O1vX&===ǺlٲEّ(SbbcS6LY|yݝc%99Yپq$e{N&&X6 +W?eEBbjHLbO/BbhnҤ;ShѢ);Pm۶zѣGϘ1c7|#?ܹs-O)3&M]r%|snݺ/^\Rb]R6v`-Z?~>djuKRb"VlٲSvNW_5jH,wIy3HeK.)_G2be"zJ ӽ|bSNbM 15$&z;Abh\o.]gϞJ˗l/j֬)zTT,?.--O>e„ ۷Ú4ib̾q y8؋-Z8ťV,33SEHKٲe{ǏuKRb"DFF*yq8FkG%ؔ.ϫ*x޽}ׯ|ӽI׮]ͻk׮U6@sHL@ ^lV  uΝ 6IZZMJJіo01r5BDDi9###<<=kltYeܹsǼhry[;ƦltK.Uwm}-M 15$&zFCC4ԐŖoGU 1{O#15$&zɐrU#HL  !1 ^KAbP@HL@ ^<: -!Yj @!15$&zUOz[Th @!15$&zr)rU#HL  !1 :gU 1( $&D/֕`ȔAbP@HL@ ^qn#$ Z@"15$&zƩ\|LMs!=CGbjHLD:rԑ]\mߠ* #1 =_X: 1ѳDU: 1ѳMM$?'WHL@^VfZ22SRPGb$&r  !1 ݺ~Ǿ'Ul$&D/HLtKO]$W 1ѭ!S/"W 1ѭ͆8zFd#15$&zAbOe:eF9:PCb$&t*]$&D/HL҆]{Lw 1ѧ/=6e\Ԑ> va& p !1 }bç*]$&D/HLtȐw 1ѡ(W̐ 1ѡ(=O!15$&zAbCLQ!15$&zAbC4ⷍr !1 o5r !1 1df~`Ԑ$[y p/PCb$&zr^$&D/HL!~"15$&zAb7G|W܋Ԑ?=Z p/PCb$&rQ !1 ]I>[g\@bjHLDW}3AX 15$&zAb+'[ȇjU;)Ѝ+_~ℕvQ$&D/HLteM7NIn\xbB2 $&D/HLteF]ݟlrwIhQW%#juԐ tNO%۵k7j(y@I58Hmo.Ԓ.E4Ԑ~ y4F9&M*UT6lj*Q߿ԩSZ\N:;ִ֛o޴iӷ~{ĉիWoѢە.\իWҥ֭i~í7OL6mڈ7o|۶m">Z=z)S'x饗LɈZ=7j'~%KU֙3glp($&D/HL#orq߬Y}8pʕ+TyȑJ~ڴiJ>..Θ'VW/^G֭۷o/-ZѣǡCƏ_F ({M9s渻vرk׮>Zk VZ-oݺU5o\IF\՟7##jժcƌؾ}{BBZGCbjHLD?N}Ŀoc޽{+˗.]rqqo)Sԩi\SNiiibyeʔ xv̙(NrHHiӊT͛7Ł/_ޘUu!K*]OߨՍ8TEn͚5~("Ԑ~F9y*ѯ_ƍTZu̙͛$&k֬Nݴ|Ec.\XvիaJaZ$GQ1jh̎W6l(q*Q+PHL@ ^F9cĈ.)11cƔ(Q˦M%KY2_Qmixo1%&ZǏEeO?$#juc.jQd1ʕ'8PCb$&zava 4qPJL222jժ%~/h$==e˖ 6OII 4mAaxOOOqH^^^;vtqqIJJ2tjk=a``+ʕ+gJF9۷,YrxqʗZ-hHL@ ^F9:u/^\yEfƏhqڴiwsN󢉍տe*;wqvvTi iVǛƤw]VPa֬Yb/7VVqjk)SF{I&znղP^=TR={Tc!15$&zAbY7}mw ŋ322̋&GnԨ\5c{us111rUW\IOO IIIbRh}8uKVղx Nmp$&D/HLtF99/S̼y@+HL@ ^D֍r~*;駟^~]pH:ZԐN( G$&D/HLtԐH7 !1 =ݻ Ԑo!F9FPGb$&zer@ !1 =z`nԐIgt/9=ԐI|>ʷ7yCbjHLĤȋ ӗ!15$&zAbR䝜"dr&PCb$&Eޟ?X'Wl"15$&zAbRqI ` !1 ͐}( !1 -E/U 1)ڢud|CC֐ 1)z7wsO |yJ[c6 P$&D/HL?z?w}鎞\wu׫Z/M#15$&zAbRܼpyÃ3Ʃ_Ab|} PCb$&EW5$+A"X#15$&zAbR$g][orBbjHLĤH:Oo}@bjHLDHten]Kw9ȲW[--!Y~PDXTc2fyJbjHLD'o9Z}X~PD1ҘE4SPCbkHL]kL [eQ[w/,,,&&F:B8ׯ8qB _EgV‹Ɯխ߿_/^YhxxA[Y?q(~(>`~.]rqqoŲ(֩S2Q%.]tۯV7PfJȤЦ͋F,PXݘOqZ-Y- LFMS !1 #yg~3o<%… k׮]z+-뢆 S~rHnm8@'Ç0ׯ_oZKawKc8ƍTZu̙JќZޑ#G.1wjweWDoV'KטWխfJ*c̋FDmXݻfbbcDU~KVydt< 11rj-|V/iī?~73c͊+WN&V rJl20((ȴ|]埬,Yrƨ3D/_6_רECO?)]jucըZfJKWaeuk(N:ռhTILfս#F3.?LT-oj1OnPCb$&FN %xxyy+;$%%ݾ}[\]x1>>VZ߱jbuk))) i۶?|9uPhذ)֊4c_O MtfZ>xb qdRxVWV3[~fIdWխfJvkԨrX[nI4-[`d 4qiTjuZ-Y- LFMS !1 #y׬Y+}ٲe?ݻw/ *̚5K7nP^=ggRJF 't֢Ecǎ] ;w+Um>*+N6MwiڔZnnnZN{I&9ߕLtYiuY}eukMɰgyFSfX4_թSxo!QX13Qn_%ɨ閟y*!15$&zAbbarr`H 6ed-6o^zx̵*fs>ɕ+WXHJJJLLT7n0-U\\իWMb_111r.G8zF3^VJMMzU%CZG9rl6m4+t͔v\Ua9  /*ߑX1fZ]bd'Zi6棍kv$&D/HL;5/˕?t۷/cֽ{v[ *UGZmwh9_ ʔ)3oyӲFbbs"رc~M}xCGN<!9L4tӧ5y(1lyݵe1lthcV%6cP(Q+|Up͎Ԑ1[,;rsc٥âW^|O>f9rEei#\6ME˳:]es,Z6;q:gvw/o 6_3|I'նfc(˶cѲ1+h=Jv鞾cP0QuVϱI+Zn -F4@YR<%15$&zAbbũnbGcǾdu~Q\0r([d&MWZ}qذ?Q?ux[lF+V|Zʟ}6F){垦-,{$dA˕+#]Efs>8݇ו긾̳^{oj28|ofJgbJJԩo֯_ťXZ/fw)v7eFwҤߓb{J8K;k]9ߺon6T9NFסbNX멧/48(6Mٍ匰:٭4:U8OIL@ ^sq&O?!տQQ7x^̛o+VlODq߾Z5Y꯿Aڔ$+STU Ӯ]K64]=pܸ!KɅ DqQb9,G#N4xTڝE{뭁L_^櫶m.Y8rq 5OQTkv<烣}ؽܳ]Wϣ<u⋥Q^{ s&#/Eq eý|1˗W;;z wͬCcK[P꽃_2h|:/]Fdɧ-[6rvv޿?k~ּy:+˗/oO+-gn ѩjyJbjHLĘƍ>}:=@uV11)p8ie{9Sٲ#ǍXSa&!vmw%yOou]nJWZ%K(gu6 y9xqROݼ4 3}F!/Z}$]'՚hv溳_k]h3QYIu훏/i A}f%{xTu%267`h:ܵk̛^k >LL,g4myT<%15$&zAbbub'7,YBm~0thŋ)mժ09Fzo…D[`xgϯ|7SS[X^ HY.C^^kjٲ2vj9<ͧYf%:=jpgʴiV %#rtYW0cb9&oOuk*˹OLNvijڝv$&D/HLyNQFS=ѐhjp?1;^{mҕ(^={Uk O?}줞X6Դ;U8OIL@ ^sqj(NLy5qq;t̞=^58q׏_/Kf6nQ?I[Ιll428xژ1~pum'ǍbZ}"NU-Ɗ]>K?[ٷgf~>>ϝV8ٵZؼB\mswq }&5dHݻΠؾDmJ8w{U걳kRW`:۷xGV{uÆZe˗[i[TI'Ġ2Դ;U8OIL@ ^sqjvWʕqVfձc_uOKŋ%+"bR6o=zi߾yEm##+&Ι l~v;-Ǧ޽˪W"VPnɒOK6ry'+VL7oZ-hv<#H<qzr]<SyA-[Z[+|Ymcdƌ9Y|q2:^/oݻSΝ;Ѵ6#uR͹Z{mv*.'Awdi2h3R'nv$&D/HL:j94YLFM7;SPCb$&FN LtƬ &) !1 #xM`&:~cVQ͎ԐSCGmv<&01+uɨfyJbjHLȩ6;Aߘ:dt<%15$&zAbb{jxoIqxFuMpyeˬԁԫ |}C<'5?v$&D/HLwh9OPPPDDa&j1+QqgZi?[>4m5SPCbkHLƴ݇%fc׫hϩeQmrOOOqvhCrgtk5~ǖ4ScV.jvon}ϣ2`2:l[=5:~].Gkv$&D/HL$'>>>4K>^[DDD,%-υxF"NRRR' ET3g&|QUrT0+g }7#l*ɨ) go2OIL@ ^=~N”|{ wxv|:ғnEsǃ݃_`CF<v#I"%15$&zk4}tf*j֬FbvRv(Z{!r3$!E$BBD,9msM#$|ޯ׽sνσ|zGɌvx]9]-e^~U7/Zt{.ܗByPdָUHLtAb"^]#1Jc P1^8ʌGy.V=o_;mm R3-( ^SHM .HLBԫk$&ZzL*@ؾ&zMHy}I'=UjiC ˩K - 5 c7]_ \b!1 T0)FhԮ ``ZXaQ1F1EA`~`T.$$&H4mٲ$o<2tlwӆt @v.ZA&Z e='hjy2`x9D*D177?N11N_6t @Nr|j8ǎe5gVmX?C.ȅWdd$} IHLʊ~ZȘ]y_8}]Г30B&.=}]̦ݯE!!W\hW`D*ILȿ666GƔ`ɩڎ4[K^99._jq,](ѥ}/7ҷ W\h=~8)IPސH3 W<00UȘ]pl;f..t ¹\w3fQwle](W.7a;g=]0 \b t @H3IRR<221/k3}v~}8ƾ߼{n57D~nՍٛ?zD\\K,rG_H`&1bS`RnB}I[ ?ԥ<.}@Ye3sJk``HV{:ME\zMU)tENoYͧk`HHVc R32tǿ]54 \q =.D%WxѵBL3CD*fX"v1Q@Jtp4mYH 8*lOf%%0 HLk`L> ,Yopl PlZۯ^0HLk`L 365A/2E_Ԡt =; k4_]Bb"X]c b+nY9iJ*@1@` I?]Ab"X]c b;{Ǩ*.G]CNL"]ПD@ntT`u)u{F@S?54yv֞G>=nC@HVxqZۯ@yJq#ωMk#s9G!1 ك1addVd](7*2t)&OO5ÓdaXHLk`LA^}JIj#Pn\/tܠk*?9͹;zD*fD'-xx](7WȬ 4 :!f_/kP!HVdG'6e~.Ev]v& k"/rP3yV]D*fD -ӡ԰}@Haaόl **oNs_5(gHLk`LA,TJ~?soO<T0$&R5{0 Bmb\A,,}͗i"=}ֶŸhT`u)³v&P{ν=~uAz]`“c= P>HV{*kh.-ߺ_D[ސOD 1 ك1dlt]ZKY ntE.~&v؄ѽ&%%%88T`u)2e^soB R)w09I ]`KY;ڷoL&{uرݫl=jddRJT`u)2[='{Uusw&% A顑6y|Ȃ._|zjo4]ɈѕPD*f Vد׻Pdd=uxJBI!ًkm 7]0z5k:]Ɉf?\CW2X %$&R5{0`=eFdG I}W&\dFҽG޲eʕ+tGGG?n'ƾW;4iѣG\g>}:>p@@۶m#7VZUj՚5kiٳуtvٙ۾cǎϟ3gN:u5kvٳgÆ ZYYq9**jȐ!ժUk߾_~Y'g~S֨QӧZ ,Ab"X]c \[7!w54ݤ ToM*رc*ULLL,--?޽{NBѭ[d2+++r{Jk"4h`...iii\ƍ7oްaCf͸IӧO;udjj痐@zvuʕ>} 8xzk xw233CΧK.C sҥK5j=PU*yjd'NNN;w]EGT̙3첲4 Ab"X]c FT 1 \q.繅xxG ]0EnV(rZQзo߂r{ΝժU#7|||=msr_*޽z&&:>?֬YK1cw;88իW~n?ޤs׮]vPUV;wȖǎJş_XtÇsZ Ab"X]c &xNV濺((ᇯ4z/.HJٵWoPd߿KL.^hddor/_.~ۻM64jԨD Çf-[TOLo$_WП3sܽ{۠8_:֗Bk'T`u)w_xصjxE>Ǯs_wkML<<<:̙3\']//G㲆5j]iܸuڴit%&qww7{њܿls~mە%%# _?Rf 1 ك1Ñ2ݦ騨Nt@ 6u857!.#{&&yyy-[ӧOHHHPPPϞ=?#Y("''~yRRR&M.\ׯwQ+V022ҚL>u֩䶕L&yxx#V\9##۾}w%9s&9W]\޵k6m\~9bTT=䮧SjԨQyP/N` =S0'񛷓ȃG:MKD\P*\i6:=)wSNK.n8pz?بH.]ݻKKKkѢEJV:t/4kk !C='&j޼y*U;&LRV;vh׮]aQN1p ]ծ]߿?y,٦nݺ\?GמͫUF[jjժdDW TKD*f ē>O%o*шd`!11199UNu|g4Tϟ+OBB\.'7222*Jʹ"so?JsD~~KկIZ_ $&R5{0`2žYV2u_%zc?8{OYCxg_D*fNP:wciK,vxˮtJT`u)]ϧ]/,8"jjR[7o]R@b"X]c IJGl _v%kPֹ ^"̡kT`u)EkknJEVq>36 4R`sT`u)T5QAx j`F==iki]T`u)T0>{xP_dFT+w7.HLk`L"=;,RIxl k^X 7xCb"X]c FkltCmq!xj4=]6$&R5{0PaBw򚼞!;xxO0]=Ii|̣!1 ك1j$3".Jucࢇ@_kՃGT!1 ك1qwf{^"X:ÑE\lo3;7OZZ7T$&R5{0P"KL e<ުIzS?d5bfTyQF*Uk'\P!HVB5ct/7G,~+.19p;qsrbUϙnNt *=S(oV L 2@YEs:]Y z%&XS)>~r3?h_ JHVBy-xt/@Y% v*UL}LpAܹIIIFPnHVBzkU"w{ 8i]&Tx ƍ\]]cc 1 ك1r &/@bR#6MG? s GBCCĦMfϞI*PyCySJ4iB&=zDo jڵ666^^^FPnHVBɉMZoH~ `qj `N2B)ԭ[M .~;w&;9}4]@qߧBMN.Ǧ|||QoVP)aaacz#(7HLk`L|zkaaJ Iip54yq݇.@"{!o%ē'OB*s(>N^ҟ.YN:.Ab_HLk`Lȳr# eZ/@bRJIdß[Ѕұ&?ĉ;v<ɓk׮=~xrwƍ|W{V*jz{+W,\ظo߾ddRnnM>jժ5kl٤3''g͚5m۶%:tزeK~~)իW? Q~E)D?I&5o_~OȖZ³$;QyI6lH6HOŋCԪUk>TILB:ulcǎ!C'N0!""{WOׁ@pHL T`u)LJ,<\KՓeC^CĤG4qԊ.Ą Ysu֑^AA޹s]bBOpwիosQg}ƕFeTdС&&& "l9eʔU_ԩCdovVj֬Yvmn?qIDAT[KVVmܹܷqqq\U])yƍݺu9sfvv6$OpOE\D۷o裏6mڐrL֭{zjCR;v,wZ_=]!1/$&R5{0PTJ] ^@ nCd>!R:EKɉI˖-Ȫ;..rʤG&*YϓϞ=U#Yoݺ-SRR<==gϞ-~lq'ْܵ{ǏJ{UT={4R L)Ϗ}uy'&$%#y&K^.+M6%wIgN_d3o<ҳdTvBJ+@ 8$&D*fCs^zzmb 1yS/;`JEt+919qDqL81!!jժ'OViM?Nnx[:t;nq"@>|ب7bw[ܼxWvvv䶗ݴiSf"wo-9:uʨ;\rrrHp'&,HLgZJEj֬YY31 _lـZnmHi}J8~!1 ك1pkܪ'Ǭ^2Q)ZOLp IdwV'Prb>wZ+ ___d[I7*@Jה\g:uTE);ԩWveoo_obp={%&pYg4eHL4Yj۾ɂ#wK@8ŝp?5ǀOL*UdTQ#Baffƕx&&#G$WZmUb/2ܹݻ7#|PRJ8~!1 ك1l;~g/t/@٨T&p 2SȽ1p<'&E__­ɯ^ޯɂBڵQgp,Ym۶wi_~i۶-ݷo_-''&nݹk׮Uw;!!{ƍ۷(JkVׯO-[ƌsI}j$&FE?jժf͚qG177p Bb"X]c R)Ǥ> ei* `Af%ҵqIjը.]3gΨwromhժU B=P.,DEE1J*}\ Yr &ەMn7oޜ+͙3ݱc9իz;qW:t(K/^4iR*UsiP#1)((^"R,_wޤڡCB=SV[OfнeW^Ąطei&2( <憇)/('/(+W7ިLbccXAAADDDFFzgi$$$deeq###ox| (=$&D*f7o=C W߉Sw< kD_m>&#Lwynݺ+v;v,fڵ{e;v@Bb"X]c RX& DP"OY76M  ohϞ=F?肘.[_~:u;v 6HL T`u)(ƀt/@ GCD@1&Yqa 1/$&R5{0 )#Zѽek<!1V{I]7DHVPuo\{ yh45o~!1 ك1wPyW}L ̶G/xHL T`u)%lOϧ^2w!1)'MODHVPl?(|$IKJut֝EJVXIw되=SD^2Gr@+yFWȩ߼TP=SDؾKvӽekw۵?>Ĥ)o[usrEvzj}S{DHV kK{&WK$&D*fxwwH }& f$yilŷ^E'nLs34AZAZ&=@<=SswJɓ|l{Dܥ?Â-=X HL T\9S{Svt/@y ߛvwLI~4>-׽HL~!1 ك1>u(~ ]^0Ą,}Uhhoo\Db"vHL T`u)tސTL, ON{D KLd;eYi'%%ѓ Bb"X]c |L'K\Lr_Z50{ KL.lmeeEVځ$ÆDHV/}>EIʝ׻͠{ KLάannnccIO20lHL T`u)e~kzLb=~zz D  =Scv^2qbɗ$&h6$& @b_HLk`Lf {Ĺǜt/x 1Ac!1aBb"X]c |\k=1sL:MMAl^^/4n'Ҭ~;xp=kWd9 1aBb"X]c ||0&y< P&vM {CI]uk/T TRiLm͙35706xzuȦs@b$&D*f54MH{ĺȜD@<ĞѱK6T;yukaY2V祗jžCi =Sò,L2V2@?6fTw۶%.5KXsK;K󼨇m ?ϡzTCb$&D*f>ʼ|L.Wɣ{ăoOfjek׮mիj,Ӧ[wС馤''g/6u\k~mMfkٷoeuӮ݇dO68qbk~az7/(>Co>٭[op;Hܹc:|6n[o>xժUm轵k縹~d?Gx&_wa3{dOΘ1oqt7rDrazӯ';w.Uƴi$Z3իs-eddWuΝ[ӟ39￯s^M4ǹ}FJŹϛՐ0~!1 ك1>0@@N v%&UT܎NAsqqdbt.Y߽xdas)ÇWI?YMpFXV;w^q UEܼycj{rPϋ: Z_ΰQ$+ˋUqӺAɉI|ҮgNť7==|*G:,{0 ĎĤ2;odP_c%%'p}zM45*1!7Μپx$S>FE=)OS` GQoG Drҫ76s樦MZӠp[.&&M8<ڡ JNL_؅ \TTCb"HL Tr=Sb'OR:uѣv??\rbRP߿X1u U` GQoILtWyQABe) itږۀڡ J,ZܤJ<=$&ҁDH.gك1>0@@N vIL&LTXJ*Wso%'&2~rɟT~~f rɞJ:4C,ydiL+W~+9M9tg_e֕t޾~:R.!1aBb"ea:I$1ٱÌܽvPFmrw֬dv-<=O:Us\SOL|}ϒ LշM66͔ܝ0@@N vbOLzT ңw.mnMkݺ#=~$KmLnJEœƌ_ ݚ4i[oEzWԏxb7ww͚֬=&K=/Qˢ ʀ[p;ܹ57]oӦ9l޼r?~J>ݧv *U2$;ۛ+4ĄHL Tr=Sb'?nCe -12,KPVhK=/QBzd1157 hvN<7x֚ ,{0 NR  1/$&RY`L4V 1/$&RY`L4V 1/$&RY`L4V 1/$&RY`L4V 1/$&RY`L4V 1/$&RY`L4V 1/$&RY`L4V 1/$&RY`L4V 1/$&RY`L4V 1/$&RY`L4f"_. 1/$&RY`L'&a%K_&[Yrm kqNWw>ؼ-9HL~!1 \βc |`0@ ?1AC Ž6V2//HzaCb_HL".g1>0@@N ve&p׿n?xa?dB~f,k;?u}YYb[E̹Kc}/1/ ~!1 \βc |`0@ 91QGK666wi 8']VXgůGȜ!3֝~,=Zۯ|go8jj`H,{0 N, Y&zyy͗+.l!Kں} :LXD 9dƦ{^]Wzkܪ__Bb"ea:؉%1IJJ"+ǢalOD*W !s2UXu)`n__y!ێ'(rl  ~!1 \βc |`0@Ē2/7G,Wd5= ZۯrR肄fx͹+5ޚcneқB@b_HL? L';$&@6x9]ӫ;oBJo2vWjw:=`]RM 1/$&RY`LV;UJCՇ>#Ȧ F%W$?sc Z:Ml_;{Rސ,{0 ION}Crgn~ BNP ɲ+'`mN~!1 \βc |`0@쐘܄_[]04*Ö*HY O۠Wc}Y9P&HL Tr=SbĤ&ٷ1`T u+2/?-}_yKy6wW"*;$&D*p9)tCbRrb/gS=F0oE\ٹ/|ǫܩ9dYsi" 1/$&RY`L>K 2/mm*]*Hϊ`]֍M}fnyv>7%hDH.gك1>0@@N vHL'ָu3/`nBȌ8by{jZL Z}()Bb"ea:!1ЫW0,f<]ʤA6+wsiBb"ea:!1J^b}ɡNeXV^}l9XAo'IHL Tr=SbD)]HrRMz]}lcWjp tש'FRDH.gك1>0@@N vHLgd;sg/tArbZM2]͏s"ñ/9SGo:$&D*p9)tCbYĺ_F&7+2,ן PC:on.jv z#F!1/$&RY`L>#{Z++2?n22] We~g u8ޏ$z FuŐ,{0 I٩T7_M ~b4 P~j#]];{ڦ痜Y9vDH.gك1>0@@N vHLw>-Ȧ ,J&3E@߲">̲[cV>=i2H,{0 Iل84d`YCw3'5m?.$ Bb"ea:!1)'ǬZωM KyתI}Dmygf:{}hz#@b_HL? L';$&o*˺ifX72lHL Tr=Sb䍐dkAVi#,*VPʃ-ƑFne#1/$&RY`L~otT =-\7{F>z赶_6so)a~!1 \βc |`0@쐘RAZCǩ_ Ryަ'1tD"5um[hƒG⛆,{0 IiJaK.G_Vd. \yЦhCȊ$&D*p9)tCbRw9;BI$vMȉ1)VНEXg^~!1 \βc |`0@쐘'YٷTIHϧ;LKL Ny%gEn>QEAb_HL? L';$&%Kt o ҃MGNOɠ fSvnX7og=zDH.gك1>0@@N vHLJ4VhD] A~s|NAz]8 ÑD&]keS91~!1 \βc |`0@쐘s:! :,g<+.Dǎ^P,TAb_HL? L';$&=}3 %P6xI,ߔouHLJouaK7w҂[~!1 \βc |`0@쐘h~c9ytJR*mi\_@W^f͢{_MeGO&#]zyʮ< 1/$&RY`LM>5Z*5ǯc̣G޲eʕ+tGGG?n'ƾWc7o޼dɒ{Cl֬Y-Ο?mm۶=zTZsΤgժUn͚5۴isY3iҤ ~ҙj߱cEi tǨW`'^8HL Tr=SbĄbX[tJMY UJ-IǎTbbbbiiݛt*nݺo^&YYYݻwWj<3SS?f׮]W\ ӧIӧO;uDKHH]v%ݷo_hh59:sˎNM.}E]t"Ӏ!1/$&RY`LרT7G,0oH6w6R;۷vvYZ5rLJãGr9rW`acG*7|ӴiӌW?i|q}NNNYYY?c͚5&s)B}mƦ[ΉKxHL Tr=SbD]s._S_xRdgn_B/^$0 'w/_\(c,XеkW6YN'&n֬q˖-&&ܱ츻ŴYzӀ%5y} \:HL Tr=SbĤKtJcNʃZ 2Ϝ98q*~?2ɍcǎ={'&3fnݒm֯_-Ā'2o7zQ4Db_HL? L';$&,V-o'?%z6qBD^^^˖-ԳgϏ>BVuXYY2ÃrvO޺uTr[.wܹQFgΜΦvDpv/i6:l9Ǎ.DH.gك1>0@@N vHL8^S7Y BKJuxJSN:-]}իs>c"]twׯN ,֭w81&kժcvyUT~gqz㩝(5όWҼ ~!1 \βc |`0@쐘Qɒ^O@ /E!11199}C\Jz98/_߅ toV L|flNj,{0  YɌTx"J@9c5)wDb_HL? L';$&7G,'5e>`Lkt$Lm1Ob-'u,{0 Nɓ>e(}痜H@_P=7Ab_HL? L';)'&YOc5 F#bm<HJ|vZ&^"17$&RY`LK{y(3](,TO6sk/ 1#$&RY`LdSvNR)t*D}YCDPD_eCڴ;$&zD*p9)tfb*k4Bs@Hp 54I rsrc\/Ώoq  D*p9)tfb3sKʃt/T8W? tCXgٶG鍠 1 \βc |`0@$Ļ~8VC@bd7eF? 71yzmbDB>l쭅*] B] !ێ($&RY`Lۉ@n|L}ut< JMN2@b"ea:رDsjF3>=៘%s67!AH.gك1>0@@N v,'&*CǩQLwucꝷƬT $1=gjBzh$]ݐH.gك1>0@@N v '&q׻͠{ArR*WU9l01!-oX7T=H ,{0 ĎE;ҽ f -=ˊKs&lbB]n2(]mH.gك1>0@@N v&&dEd|J rObl07۠E"}${t4 1 \βc |`0@XMLnxt" LHy*k4"ڃ l0<"vucӗ~!t^D*p9)tc21KLZwpAZ&]Q[ݏPȋSdWjsVN ktTjH.gك1>0@@N vL&&a{JhFyFoO\`.N_#w>`E%&DkmON /$&RY`L YK ؓ=}E/b}n`51!Bwr"7.@$&RY`L{Iͻҽ e^ T'\E);z;`By'&~vҽPTr=Sb^b=}ׂԌПO۵pJ/}ɚl+w U@bεo?) HL? L';yFe/A"KG]hhj* 1!R[76~O$T\,s0 Ď$t{.i11V'4 ]-P)tAڐH.gك1>0@@N v%&'yzҖ5T\bB*]44) 31!\/PH.gك1>0@@N v,%&\ڃB$&hk\b"yIII 54%ʡ D*p9)tc)1p5`HLФָVVV 5s=+aHL? L';Cv{ 45.19vWdd$CM'&9qI&/T!1 \βc |`0@XJL>t;5`HLФ֊R& Kvo=FJ,{0 ĎĤ =J`I~b ¶? ,{0 Ď$ƀtaCb&f w~-WH.gك1>0@@N v$&a,K6$&hRkHL"$$&RY`L3mG^ÆMjMI^bպUJ|0dr=SbLb${taCb&&ĄpxJ0WzH.gك1>0@@N v$&WN6ILN/^~Cko%1x^Ab"ea:ر(s/WK8͕&1ݻKn4jAA*U4mo9sfWΚ ;iU35 k"yG)^Ab"ea:رdG۵O;ѽo4ItiO'ZlL[͒!<7]U؁T&&y 5Kb_dUH.gك1>0@@N vl$&7Gou(wǏǭ46۶-! ;ۻ/ac-V,h/nKظ | ͇ڲ4=5ϦuT$=}t 1 \βc |`0@HL-^z͚5}o?mկ]WUMnwyCӧ $ԩSs;{5͚];g߾ %]җlpĖ{׮nÆ6o^P|yyӲeʕjҤѣJ8zx֭qL)SL I@9j>/-5_0(ĉkԨFJ۶-Y%H4^pS!=߮515tz Z|z$NNG}eSUE/2-9aO>iwaOG=1ٲe!9߯$$/1Ū +=HL? L';6(^S7=zK,y:tf͚h6۶mѣGժU;wLzVZE֬YM6gϞ%=111&MjР'yy}GQ-lbRDQDETbAjDEPµ  ^>@)Ћ@B #H(I(%$!m7!{!L2;<>sfg'{^2)y/ܷojժ5lp…^\mLĻӊ+ï[[r_b\ukԨ2vǑߊ{Ds6߀/.]z=G(W+KG[cɓ;R޽Lu.m}Q[eSO=>\a;|{(ޱ=B۶O1xp. ~}\izZ9C&j G=m;@~߸)b /,O ~|Sn#/5_~q&e˖ѣ?lRJZC~:dhFrM)s<]rV=) ?qөјOGիWL]Ι3oTR&M"""BBBjժe3dȐӧo۶Yf-[<{{ׯ/Yf͑#G5j$;t۷/[lvf̘qw6m x+?O,g?y@a~Ƈ ܢ#/yoTNz*ψ1Sٞ֩s<&*]{{I-I_j;믿Zq}K: !>-x}HM聼7'<)%%-²]7sEjLC#}.+tΜYUBj*;RJ:t8=ر%b\R~D8a|sJc=.zXSA~`!S1ik_{^z:tp?צcǎ+={{+V4>+W^plo޼yVV=xrI;PO@ wŋSW_}x,K/- ؤDܘ0W^삼=Pz?ϣxoޏqq)S~3؞~õV3n}\my%381u5UKE:`=;KOxSZb~=@søbsPFR]ܫa,-v6g"i|#FijtUW<@?%m4&Bc fX8iLW#޿jgϞ52n]ݘ9f͚ժU]dxswƇ6lX5& WLu~ԨmĿl;^$++E;Ļ7>rw?;([A@yI!=Ӓhc送v@:yFz,lcnz-xM]~^'QF/x͝&U*jժoHܽz{FzOhLD]=) ?qөј9:l GۘOY3fꫯzʸ#߿G6&:=JzG}ݘ ǍO| CdO>1 s?/!=r3;wRLcǖ9 tJ))+.>}m|xܲe1 tdϡW^yuUΑznjܸ8yLڴ7?ƈX/nZIFc,4& aMaN$nBy1 7–-[֬Y2e?ӽ{oɓvvvv W>a„ÇGDDIdРٳŇ=z<,7Owލ=]w.nݺtitI~ϣxoiLy\{<-~Z+#"F3K<\ժN67@;z| {JbkٲWTƌVͯ~!#Kϡ8 vz7y"$8o}'6WΜ9ѥEF]tхwxo4&Bc fX8hLL]#ӧqgϞ76n{VZۗ]v٠Aʗ/_nܼ+˖-k|ݻ4ibìQÇ>\׸]XiL1iMw0.nHxos-[F\xaYx>cҽ{^ysǎ-߿+OZ޹sҥK/1nG޼qӦbϩS4>kz\{<-~Zw7ifv:')mU|@V=K.S9C#Kajj_tfSyCoߴTRbKL4D[cn4&Bc fX8hL/XիhGHIIINN6n?cHJJ:~C1)v?z_=َ]l|3˵NL>-FrUE{-Ǖ>/ owi}7$>{#?$T>yz ј8 .zXSA~`!S19yϜz]ѠgycИ肸5"Np:5'Bj+=66g1qWk 3,Dtj4&nkE2Fc¦Fc,4& aMaNDSʮhp1am1q]w0B NLc{ Fc¦Fc,4& aMaNdv|<hLthLD]=) ?q)ӘFc¦Fc,4& aMaNsuG nИ肸5"Np:ewkf69D1am1q]w0B NLc" Fc¦Fc,4& aMaNdg?<hLthLD]=) ?qөԘ}ސGјk6̖8g`ql%@n"4&Bc fX8TjLrdάƕ-O+1acmI&EDD0И؋D]=) ?qөԘ } h:uu63?ۀ/'7/ϼ=B_;U/O1|_;VmҘq-ڰaCBB hLtAÚ  '8bGnYFz+ZNyg<^rO<9i? ^hLEc fX8kLN9ˣN nذ!&&f fSVn5`|xy֡1.zXSA~`!S1I7S(TqDT;EÞΌhzB~fИ؋D]=) ?qөטs_ڦ+3K94&1qWk 3,DtJ6&xw}`By\ڶg\pp\+:=C1.zXSA~`!S198s¦/ȣp wv}co9E#O{ј肸5"Np:Uyccpw+Ksu2en'w|1A@Qј؋D]=) ?qөژFLoQ8۽KqedS*=!iVGO'OHhLEc fX8nLSfVn<X뙓vFnV} ^4& aMaNDwfء-4y _Vӻ(${ј肸5"Np:}Bj}:]@pᨹgH'aRІ;ǵEm'^4& aMaN$LfM  yB3IahLtAÚ  '8pxj='=C@q:s[9WyBKDS x1.zXSA~`!ӳ1V>9`ӻ?ȣ(6ǓuyBc=< /4&1qWk 3,Dt6&g:$)z={6nظiL[j%n_ve *_|ݺu; wnҤyjԨqaiÇzw3ϩ՝}ق^4& aMaDy?yB!9igs_XjS8 O(^4& aMaƤ2s}:VP+#si>h N[>?CQј؋D]=) ?qјq[;o2^p8wv}cwh n_CWued1.zXSA~`!11̡c<_ rb_ޱ;;GݖhLEc fX8hLKOHӮ 'rW?3P-KZGEcb/]w0B NGcbG"okW\t_Ϝ3+3+JԸDyBQ4&1qWk 3,Dt4&VI?U֏G'+9UʳolvJ<`o %*^4& aMaBg۰~#viKyiLEc fX8hLy"e]Ϯ} ?ʻ҈]w+ꗨ36x2)z<"{ј肸5"Np:e-HwZPId%N#o{lˀQ^UhLtAÚ  '8IqIX?+{/Έ/[&Fy9&Q5Xc-Oȣ*1.zXSA~`!1)&̬.+icD|wY7<_LY^#$eswD\1yG<{ј肸5"Np:vֿ6tn'r\DeOJ)+aڼG NW?YUhLtAÚ  '8Iqokgz$e>"Z9-QM ^ê?weeڼԅVnqg{'# y^UhLtAÚ  '8I VABlvu=J_ZQm2`ԊޙsWh8/WVJw +{ј肸5"Np:k;iKzq3"k:J)8)e6nLGJ?"7\"^4& aMaƤdxţoOŴM)u4yek[cĨJ:wͼՖD"6h~#QИ؋D]=) ?qiҘdL9d=|?5Bza)e^ޔw+mOO;n8w`<Zut麅M_GBcb/]w0B NIc"ފ{Ń-KɋrL%Dcb/]w0B NUcsGL-qĄqŷŇVXm7_Ә&$(*^4& aMaND-w׳vxdZ_NGBcb/]w0B NGc$ј N+^fxc$/$ε#*^4& aMaƄ-H61m ĹNn5BhLEc fX8hL؂d3 4iRDDDLLL\\8WZ*hLEc fX8hL؂d4&%¯I?*^4& aMaƄ-H6Y%].hLtAÚ  '8 [l4&Eف{Fȣ1.zXSA~`!1a Ƥh QUИ؋D]=) ?qјFcR4'͋wyT4&1qWk 3,Dt4&lAјMC^GUAcb/]w0B NGc⻥l45.nvvZo;v+q|G[<4I}zʣ1.z5ݽ{wbbA{۰awZT -[N덩Vw)Vx߱m{5kO1mE>Bm4&EsbyxJUhLtAcizz-٪NiԨQv 8_s:thXXع.PlWh.v>[1lM +VOa/|;VE> x{SmܸOy@[ϹPIѤq`͏ɣ1.z-J R+x}{o}|#L-E%и]| EW#bɒѾSs[kb︴ڇ1G7f?|і~nЁIќ9t,yT4&1qWOE^)zh#)O~?.=ӣGy`&иG>S 8 q}J%4&vRJjuAh7RooHO_իb+*~k׾L\SuԨ<1v^h{̒w|Gu э}|O_]tm^~UV裞;n8ZUPN<֧`ƤhOOx< {ј肸_۷oj6lpBcO?뮻. ,Xl޼E]t5ڵs(߿k׮*Tػw}ڶm{%ԪU+44]z<|%߿uʕ[q@9UOAؒΝ;WRESNs#VMĸwto8`Ŋ1'Nh&8q_q|Gc%$$͚5ÇoѢq[Z8 (~?G[gD:Z3Ww~ׁ11+'aР޾S֪՝+V⊊[]{Rˇ 7`K9}c=Cÿs0cF%!mQ7[Fc?Kz2}]{envX0qXq|m4&EvO)}< {ј肸lٲڵ1cƝwٴiScvȐ!ӧO߶mxڲeK1#^3..NT=u +UO?-^WڵkSzu>7OkܣG潔/S̬Y>_|%^7nXy:!^7T>!~TnԨQJ}pqsr|;뇄Yȑ#=|*PlKay waaa⽺qw>2ԬYӸp))) 4o(*>JwP{>@s<{~'"{gh}|[_΋~s?*O%3v{n|T,VZu=.]Z8)~W{3(~?G~I>cL~"Yߣ8Cc_CAsޯ11yFa|ͻ590gR-:Avoϸo'"s5Uh׋=[>wmL= 7&yU&|+CB1>p+CP<\>6"ShLtAyy9l0kБ#GשժU]%x=ZN5CwyH˛M .{ӧK=xufddTR>3xi~zc@;UOA|~U6&, N&x.q:∓3n:cXir}.-@E%xG z=O~1>IO[=Cq =|3K<O?w WڵQĉy?UQJ}|ۘzti6&}謬-+|ȑ_՘mʔ/58n4&E{ј肸'(5ůB ˮgАϽ<G>wbgϞ^xC^7o3f0/{h<` ;OLLءҸ$В=ܼwJO=߳ @|,AaٲeP&L0f'Un޻֭w~jQ> ~ ~'7|=={>g'h~{|s}-7ׁ11Tlgκ K*Cͥ =ƍS.Һuk;Pwnzti1Ϸxo}谰a8}b,n{~m-LޟAߍƤ~Ccb/]w%44T|a o05kVLt2III\s_5?s^Nyeddlܸq:`NN81Ox/uu׉Ǐ/^"{^?}B*#VAիWx>1dow~7~9L~"9Zn3W@~?_@AИxs(UTʗ??gm6cԨی| =.S>yԼ 4ƻu uv1& 8 キQz*Lt;УsƒΞ]JʊB=/8?~]{޽~+tg,1^jӧJјovhLEc cMׯߧOcd˗{٪U+.4h[Sn2/nӦ7w qqq-Zw+ý|_xP5i$ [xo4~ST-ٽ{w&M.SF (sd_}ٲe=&8qʟq x~jذMqUVnZ]p~軚sn}Ao|}'rα-7z.7й:P4&ۢEn:c!|m{zܘjM{xGʖ-#/l6wN-ܹMҥx^8& c8oz|N8.nvuH+|އ=D<^ysǎgE|.׺[oW7=|6"ShLtAs5=rx)n$'''O'vKLLG/-r~{Lhܗ}B*$%%?~\ w8`q ߳ @|wP1@;=zرc`VVVs7J\s;xz'- w~~-1ݒZ5!!22V!+>7w+=z>ہs7\nƸ8Z|'N(AqјFcRd _hLEc fX8hL؂d1)2D4&1qWk 3,Dt4&lAјW"{ј肸5"Np: hLL+hLtAÚ  '8 [l4&E^4& aMaƄ-H6"SJDcb/]w0B NGc$I)|%1.zXSA~`!1a Ƥј؋D]=) ?qјFcRd _hLEc fX8hLvN[;FcRd _hLEc fX8jLv~x[m1U[%Ι;e~+ÚИW"{ј肸5"Np:&;hҤI111qqqBŸ^hLEc fX84iLN>tfF6ˉ 헗ޛ\7?d+4Soƌ o7lؐ /$QJDcb/]w0B NIcm͚5}WDD`5ɓS~[S|`;Cqu#H1NJJ(|%1.zXSA~`!Ӱ16l((i.;OڼQVnɷĺk,/QJDcb/]w0B Nac$ލf/GǦE3]=|'ј؋D]=) ?qјeţoa<r3 M+hLtAÚ  '8-N>V~3HzbRuN_U!D4&1qWk 3,Dt4&Xʗ[G]aU.O> _hLEc fX8hLJ^S3+>s< "v<(|%1.zXSA~`!1)y[Y yQvun٧ A+hLtAÚ  '8I 9~uH8yiּ8x}.<^4& aMaƤ9:lY7Qrg,i~# 8D4&1qWk 3,Dt4&%,.GG)DJdG9SJDcb/]w0B NGcR/Sw>qA,Dt4&8sJ.Wy<}ѫZrn-66vĈ5kּ~W> xg 15n8cO?뮻ģ4h`b䭷VXQ<ĉ݌ť套^=%g߾}m۶Kjժj /a0B NGcb!7nyq߾}UְaÅ ~_vJ]#&dȐӧo۶Yf-[#{_5k9bs9 t>uk׮ӧOՍ;/a0B NGcbީF~x Wlv͘1;lڴ U&/_>>V9g W"{јB}ʕ[~WZ%Ge|?cc~[rߡCk6%%E;v'==穧2uV1>s355ޫXm%q<'/d+qƿ)  '8yO9x ׼y,q{ʕeg~v907hј؋D"'O"##\n؈M{%gϞ52nޗ>٥K߿=rȚ5kVVv=g8Oq6})  '8yя׹_ 6hLy1kϳ?rN1BQJDcb/]ĈlƗre˖ &OCgn/97o?x!n3F ~WޗO/qǍ~| )"Np:k;ocNU>/_Sf\*A(|%1.& T^]\`>Qvf͚m۶mƍw}M7dJ^rn'%%=r*v 3q2eٽ{oɓqKq1~N:IDAT'Nu}ŗ0A~`!11)uoB5ymLy)Jk9:LE(|%1.޽I&QAo76li&.ӧq[\r7nlܖ.9?TRT/|ƌyVZ}.A/_nݺy/[{_e˖q}Zh!++Íqm% fX8hLLn{%ūAvҫDo|]( F+hLtǏ{MucǎIe.999SGq#%%%99tݾ{ N:sHġ| "Np:־Ů *ѠWWfm W"{ј袘(aŴĤ _M Vu3v<PJDcb/]Sٳgˣ(ŴČwe G旯5IE(|%1.zXSA~`!11Ջ%H"SJDcb/]w0B NGcbƖo#Q?S4 _hLEc fX8hLX? ˣ(*~0hј؋D]=) ?qјZ]z"?Eb~0hј؋D]=) ?qјYځWȣ0gUOv0MEј؋D]=) ?qјY|zC9K ҿј؋D]=) ?qјٖGm0J99igf\@y)|%1.zXSA~`!1)e<8s< Ӗ}̑GW"{ј肸5"Np:" }Q7>2wQ^4& aMaƤh>>ryV?yJ_hLEc fX8hL nӻ?l<^EI_^4& aMaƤVvh(y%%=1)(ј؋D]=) ?qјܒV^ZE Cj\<=D4&1qWk 3,Dt4&7<g.G^4& aMaƤfVjy<e-G^4& aMaƤ\S/nv(Ag.Yޱ<=D4&1qWk 3,Dt4&PuQƳ ј؋D]=) ?qјбU[<vϸ?%QJDcb/]w0B NGcR@K?OE[#KɣzSJDcb/]w0B NGcR@{~sy%n ~<7D4&1qWk 3,Dt4&p<}:vJv~ڗG^4& aMa$g.^7FLl8;zQ^ј؋D]=) ?qјcĨUO}ҼsaƇ^V՛W"{ј肸5"Np:|d%μU84og*uߡ?/]i4;s<1D4&1qWk 3,Dt4&۠kT9ig܉;Ĉ+3k^6uo7JO]A՘W"{ј肸5"Np:m8F3_dzo~ g\Rb+JJ!jL+hLtAÚ  '8ISkz7קWhVOs]QR|8j;ˣSJDcb/]w0B NGcr^sn"OஈW=rJIVtzG՘W"{ј肸5"Np:ô)+Mڗ@:~]Q)|%1.zXSA~`!19?saS1 6iyiu ])|%1.zXSA~`!1)w>#Ϩx) 9af6gGu^4& aMaIɔ#Kmz)S[~CFm2 _hLEc fX8 ۘwѓ~>Mur}O/jW"{ј肸5"Np5&;}}dd=?Txܘ{,J+hLtAÚ  '8]^Ϧvx`nLslQ])|%1.zXSA~`!1aI{5kֈIIIrҼeޔGu^4& aMaƄf4& 244TMݰaCBB$-8Sј؋D]=) ?qј݌d»&M'>Uk/J+hLtAÚ  '8 Ә;ՂpgL+3KВW"{ј肸5"Np:6[7&BĵRG^4& aMaƄ;=z<%D4&1qWk 3,Dt4&l~oL~ĨhyTK _hLEc fX8hLnߘѾQј؋D]=) ?qј݂1Y]'ˣZRJDcb/]w0B NW|7Nٷ/*'glPm11?JqΖt1cxSŷc1[G^4& aMaBr䒋.Omi֬UƍSJ*խ[ȷ߾3ag;vxSŷc־<%D4&18p!Ɛ6FY /6΀cSw--N$- oҐMrsϞܮ/׼^]g j(:Y$&ݻw5jo;w@NN#G6۷ZN- yqƢMHE$4n$楕Ct7Ą-0ܕ~)0~a8Ebbaqŋ3ME`'S"D߈j%=qb+|7)_(Z}EXDBՁy,y-R'&IVӷUVRDHLpW>§N N EM޾}m[Sii%N~}c˖͛4iZ y< cx\.]BQb«XƃZjQ~/$[;>#ؓe}tg:Ky " o$fɓ[Wq`\ExBgK{+~ pԽ{+W n,(vĄ*'*+)dIBbGҜ| *)tZt>-*Cv 7iҘD_佖-yafylb"$'&%%^&ر]+nMp0+<|x %`lG z b M<헼 ZZg (b_JdY֛p P-zmdU!Ą,_}ջG.ABUg͚ GMm4xR'&kFÇsYpذ/.87ixL8F2&t^Z\aԢ~i%9t}xĤn!1a wc✐h [m*@t0=]PHL .zܸa1 k썄,'O~p+TɫȲeܹHӍH=+I] J1]VKi-ꍄ& \ϠA}:?-V(DbbO-7"Ov-$&l|\` ۳O*@ ^IW,^<"_<m-* 3"걱EB71'2_u!ꚩ}VHiRR% Mb@vuwʊB$&_ ē$&u [`+7yso xWTwUjA&t@q(1B$&'{UQĄ-0ܕOs ץ*t3rL啖 US@!1"vQvWt}xĤn!1a wb1l9]Ui.*@K 8`(DbK]e% 1[HL]JN+䠚\ :T]Gܤ㣉IQjFNsw1|J}):ƢLB$&Nē$&u [`+W;9~,*@E=1s[;]PTboS[^nnI߮t]v88=ȇ8DŽ B$& 1IWG';HLpWJ!l@W ROw{C(=껟*BFY16ZXl 7}weK~ }l|+% ē$&u [`+ t@j7t~:BW$3G pMywEcq+733fz_35}6HLX 1Q JUl;x.]"ۉԺҼAat}Vt+Wry[Qj HL](% 1[HL]YE&tWZF7HB"Df0ԉى.g,:`6H]$q}_g[RJ@$&X.HLOv-$&ll'ir)\wTEcSץ* :\zI?56Sms5V:;8-$&X.HLOv-$&l ǮR#^s{ R 6黀ԐLzNZ IQk|Ĭ zIȥ dFK3"#VD I9% 1[HL]xC )b4 %@J<~izԔҜ$Kcwflk=lǚa7u2|CMT/1’ɁS/^022ruuGC]+OL*h*dIBb-*@UN҈CWFB Nt@jyIיsÖ붘d;q7 3r5IIVnX\#%1IKK#騨P%`M1Aggg#NؘOWG';HLpgsrP ܢboWF7@*t>鶚0cϲ#nrSK*%~LWG';HLpgC]U\$5nG& U`⌜'fK鷟nR=W< ~ Տ;qZEa>d|L';HLpg nIU-Hs7> TYҼX 7"> z8/ AY$&¸EX)[A0R-7<Ov-$&lɏM"Ѹܼ$@W3wjYUP^n\*EO' d[/*+ S*(dIBbd 7>QcEit@",:@WA sըP{}曣UW~5J=AbR;۸"]34'n/(7GS3Lf70oW9+ CŤB>J/{( $&'8{]yUWb]e%% 1[HLÝR|K7TQI?0\>W5>+;(nő6oU*ӷż)\Ɵ. S񖮲Ov-$&lZq&}9b4z|F7߮+t$,b&$/_g=_UW6tzͦD&;tIYC:Ą-0٬'ԋR3*J4u1즚lA7|̴9Tp1BR=?:-؉mw[HLC&Q.Pz*+]R.HL pg ,G*ȡ('ĸ< 嬶?]n$,>}ȑ#Xܬշͦ Notd.)۷ox5**>|$&o=Sa 4OU PKHs7wpgYo]tz4lmm9pF;'P]4b.` նmMABǚzm[O7Of#~~~,c\W4 xFKUV"\A*!!!;;>r|$&dcknR켍||Ȩ0dK˅7G跛JQZ& JKK#PqtN/UV"\A*;;>r|$&],7Fӊn:2rnT XgǮP9ƽ溭8CeW~CWAyo>vK&;Gb@ɍ7t>P-hvS7tx\K]- isvdN157'$|s^?Vۆ&;Gb $3~VP#2ޙ TzЁZZf3v_-/E'~JWAٌߐbCWY Y^#1a wW}eo6++\l:. _e9KY [neA rϴ¬J* {`qV'Ԕ$ 7>}w\KH}k{N>5Yyo>->xm:zMm50%&;Gb YNhП|) ԈҜ|-L%zmJj|LWAL :ļI=PfdUoeZ pr* {`Gl;o>dY(P->&_yn8]Kq:Be-얮aY=g鵙ɍ'UV* {`\3Y?*Ԝw_SˋN$7t|5s]`6H&;Gb l΋J%'z:2"6`Ҽ^sq[ ca~\ 1{|@WAsl'iUdGWHL*Qhd<^cn~dѭsK4񆎆gqe*z.UdGWHL 1YmqԔmz̎zbF;8t>]O餂Cpu[N­Q౻ޛUdGWHL-N苹o~TBM~ >% $=US%#W Iw{C]E,hLvd{Ą=0SgxmpO+zYm-.y{A;/g͠^܁?~TG?2aaa|n.\XXHRFFF`` ]X\~nAWA،[hBW YZk<wP$<^}#=\|jFqFU<*+ėX >3C˵ݻ7}k:lٲ-ZٳM6dkKΟ/L͋WWWԩEEg0]0>}zf h"A2RY]xk###ž}.Xk^ :" 39&;UGr"..n*,\0!A7($&lTKg +$ KbmUk(w1F_͋NreE]fR!C4jHEEEOOoԨQƍcgΜ ?~)SH|ܴiSTT̚%ˢW„Ou?22oQUULIIrӽo޵kWkׯj*P55/|ۼÇoŋ ]1b+++[[[IF*?ڼ<7k9&[Wdxi.F,Ɏ,0{U쑜pРA<9rd7($&lˍ~fn:`7t3@u%xڌ[oͲ8];ob4[J+oR&L())!O>ݬY3V2?x`V@nȧ[qS(W? `իW|uqq&Μ9Chhh :T!yz^˕\ȇi޽{L7n7k 9굞RC7"ӳ{`-LvdAo&O^]NA#Cb8@E>06u顬,l'l4NnSt!\ Ӝ|`ze[nٳK.}dٳg2Eab_U[>/XSSlyK/Xt+&*擴/ATV}.X;ބ~]e1LvdAəS"Outt}?7($&l&2c=p}1dJuQ9h+ʄ[Tlwk'r*ZAQzqyA8A[[EJ[P|rJ1Gw2O$|>yx!K\@HQOOsƍzHeu[+MsZmQ UdG$$&@}?7XHL}~d[$=juQ̠͠q>ȋL0#2֏X{" ZAQC> OǏoذaNNNAAֽ{wU*~%oA+ ,**^#!vJ>'''Wr"/ꟛ|"^7Nfz~j״p"3]71U}Q >I&;Gb EQO̬F1fYm= 5Go3v_͍AY^z]n`QƽUjvtD[* {`<{A;.) P-1I08g~AQd|cK7Xumu'*@،[*Xm*a# ث|$&(-3&}N ΅Q~*cS~&C+³}yEQ{7}lJW궚\G7{9n* {`IpY|РL2_R~*?NX{"'nCoV2NW.;8ʴB w3*a# ث|$& (5gY^~If2ߞ}b{ը5w puX`hLPe u[LwEjyi ]ps n* {`J} %\xe(|*Kƒ}ym:S\Ytj P- <~&я&;Gb OjM5ϝ$~dg刕at~Ҋ X#*@8o0\5/:&;Gb4cN7bg&=zst@3srvxsN8)jsqu-.n<ѣ`# ث|$&W߯ N~qz S2ޞ{jbyTJ:qиܼWzdBW$3Gp]qFCWYY^#1a wPV<.7cqN~zl/VKw xZݴW3whZqpky~[ϸ֝n;qS]TG?*(T7UdGWHLۻtDtٻ++(~n627}HzMˤ41_vSk #t0ّU>p(+,ӳ#&ʣ9ˣ\@U&|`%g9u.QYonZqn5kә:tGGߠɎ,`򑘰;PYAQs։m$ѝ"ɿ:N҈kPlղ62%qZƒ Ug;a#м7E% Y^#1a w`3nQq纓Ulƭ1ko5[\qp^_T8}{\N$YInt)o7T t] Ft0ّU>pW6Kƽ R}f?l Vqnzn8MFd,A1VtrɋN"G- PuEUP(CɎ,`򑘰;%uXЉcr:p_u,Vۦ4 3̿Yfc߮+FdffNLwVCBee>|m6+M-j$&;Gb)LJo4ob?}ۻ/sН;MƑIo=5RoZnth_ɥzC*T=X&/} *(<ݖ*`#ث|$&Qeʼn>/Xdw泉.et?)dø\D=Zʕ+_`8kդu{ϡP/_FbBr]őa6x ]Lvd{Ą=0$/2!/}y{8g8(\^)a7UaCtD켷4in[| ?U #U~ؖhJWAq$&;Gb-)My}刕F=f{9²(- Es̿^xnV` yr^3Hɾ[W>l:]`Fӧmmm===CCCxyrʋN8ڈUɎ,`򑘰;+LJzlNV[d["D4Ro{GINL LVIVqt9Gp8~~~ t'eW_|"ǣ@qt>]Lvd{Ą=0j#0=ۉZOxn+_nQ1rYoȱI?n{i"Ǡw ٳDpp0[:U8)))-*$''K9p/\]]N.+ tʣQM*`#ث|$& #eEݲNۂN>Hsy+-T"7<.cv,QܒRT21!rr6Yj׮?eA9趚LWINNf+11Q&6$&?ֶ eݭPl'iUdG6WHL$nzL{ W~\Jum~p_y4NwkH%9ӶRYׂpy7f11ս xirX &6@bB=U Ÿ8 * {`Բ#G_/[ _z]!e =ifD3|5s^NswG>4&+U)1!xe~9Fo&cdS?3p!C<|ɒ%mڴY`yzAŋ7¸?>}zǎ[l4eddUo߾w{E$4Qe˖^znzo߾e}7oԔ\OHL ҧ8G%t`#ث|$&P3r|7fn3q/Ƴ4 f.c3n}NlRĄckY%! K%&C%-[2yСCY7yk.Uc׍5p̘11b3op%Y &ί޴I$oWmC>]p<|73苹^&bKLhz=Qk;Mrbҷoߐ.ذaCR10(NUhU Z2=<{Xd纓d)a v/.ӻ8K1!Ə/?i{ܸqwU$,qxx8BoooD M|ͰaZ=?PTTQ")-oUV7HL,flP^{{ ] * {`(.2 q궘d;a߮+qvF: ~ BFN^MF8=inˑrU#}b¯| X닿]v{ٳ'ŋ_~q-NڨQ#E$4QrnjC*;vܸqcΝòaÆm߾ٳgK,WqiXҿBb~4]ps 0ّU>pP%y6g9kG*Nzd^K+ɏղv_E>/$JM%&[d:LbҬY3>l0R?[XXԫ HeWy&N 4hذΝ;ǍG<_F[f8DRRy*It3H]]]]tٳgd{Ȼ4iҤ^Y$f W$,br IoNwPD7:UɎ,`򑘰;ʏMӳ}vnIfW}wE3яLVbN=do>Q lbBdGY _pqFV~wuu_2.%'0q%"##޳FBX%%%99 f%tVY]!1q[{S3 Ť8 0ّU>p`qgy~i6lo!%[{dDinA& ?Wu_GsLĄ_q*_/}1xܤIv ,]IWA:HLloHu@x<&;Gb,r£H>+붚lgӏLh 2vKqn26F 8PJ~D #Ͱnrb?~7oޤ>8@5 119'?R`)N*tɎ,`򑘰;+izmvSuѿ cܒ{oZ\j}beEsv[ _n!KWA:,OLȿqye⯒ !7* Y^#1a wgzW?tRk;v&lWRޛ>0%#ʣ]f_|cIo&&&ǦUݭM$KtwtX$CWA$9:EW_* {`Ge)YȥY _tWo*7"~( ߐ3mm1zo&xjQ-$&DAB&iVt70]vKKO dGWHLWV~V{9sUSǮz.{GYaq7Fi6n栓]j'0&3R3*HItJA'U&;GbgyUdlk_v_|`\E\|Gdyo>k:`nɎw\zQk*ĄϜl"OW6q=E[TEJ,OLnt#U&;Gb o9g.9Df:/Cy E&G=2q_Q9N*djN-'& dom+䃀n ?-HHjJ<1 8NOPt偩+]a# ث|$& tE2l?]{(ܰ9tI~kG?3|$& -ݸDN%o*:M`E'鶚H剉 *(on Y^#1a w+TtQ*lz];ҽ >~ (:jO7s5򃮫Ąla򥚋oⓠ7o>5[T* qO{@%Xq@q:(LJ/Lvd{Ą=0@~伋I0v k8Tt[NvCjd Ye Ut<=Y*y~\ O:u+.p/2hn6S"(Jx{ia{d4gDsWva_T9h5s$dGWHLYqFN{`Cc}ם5D8^s_wE25M3ne^v^qɗjkOD=2RP #;8v&kDgF=qK?Q[S<ל(L~o1sUoW],OL ?W(P1I] Y^#1a wP0<^NHtcȅgNی[oYEo9/ZNw_&jMV`DM% ӑ"~ML%uuA]fziY%(^YtPgYVNsvm=f-&ߓ)tG{t`# ث|$&J$;/'EN=t_uz:N'-䬶9)>QNnX\}#]84]$?kѯ&&|4j_.tV Ü0N >$+iȰj4V8M{ѝlNLJsu[MP ^9BWA&;Gbȼ4mU+1k,ɜ\t r(?.%慥ƟCiÌy3'ML=ڔi?u r)>1/31[O's[vtw á{@%؜E%GWA Y^#1a w`c F=#qӶzn8|Q{ S)I0rwfzG1Zz̨M~惗PM&N׎KT~m9ln)noNW%;AX~SΡ{@%؜UtJ{ߠ Y^#1a wnIiλDsװ:~:L;Ly~=Wn&Y;򦬰8bW=k|[TlN:&$Y/1dRzCea2s@6黀WZF49_zm7lNL,flP|{w%]!* {`T(-G`UGOObg>vSunh6 =[od I?5vXssU&ϖL̜{-q}7ǭgN]u_yL􅟸j6pLh+/$%9UP(.Ƽ Y^#1a wʋNJ}4='m%sFc ̴E7n$8gFЯ{q~^o7¨iEYdR-d`Q%Gx$H$϶tMhFWA&&;2Gb5%?.%?yЩN;nb;`1|>[8E_^T"zb״saT|6># {ͺZM?}yw.| t{s]B~d Y^#1a wY+LJkK]W\ְ,r69fz%\ q~[A>Ĥ4;j*m9loW|6^yEXŢLK&)S Eݴt`# ث|$&PWx\n^T"E?5 >{Yٻ,-o?]x &k8zwW5t]c@I#tF,.7b}!9'Yz 1QʅIL N_ MKK6'&vKi7Ɏ,`򑘰;*+yb$2q c5d|GQMS}r|YĄ:ӬwM&:&HLoaGq8[[[??=͉ʣQO*(]fU&;Gb ˋJLsնywkn?a{vqzm~p>N9zn84 姨4by`7YYmSC-ݘVY*EuyĄWƍkP^(DzC 8oym:CWC* {`Jr#S}bmɎ,`򑘰;Ԡ ߐd[G&o? _Ut[M&w{5fYlgPQ;(ML;|pnDY$&3bn~l2]jȅgt>Ɏ,`򑘰;Ԫ{dD=1 S~ӮK˯b6xQ9wnoYZc7e}cȄW0uiu!fNm1o%&7Ab vS-HL8<oDWC* {`*ϋJLw{Üz]:'j5KNAs]d޼dwEx\hR[KnT6&It,D{P($'&j,HLlBYa?PPF=ڽU tCHLp% 61)K'鬾[h.$5ծ~Ӧmb&p. Umﴈw Eضm;\Сu}voذA9,X1 6_ki-Z4#xpԩߑG7 Gح['&&ר F͚5ڵÁktĮS o>yy Bڷo5izOx׮d-m$&Ӭ?.q ̙3رcwҥ˰alllָ p„^\ѣ[nС^ڳg>}<sĉѣG7mtС֤w^UV x)ǫwԉI_l޼Yt|$&JJLF\|^a|41!tǏO61 7jyLɓg$&Z7hЀLnC4i.pzƎIƂ)k'~*\|yǘs3AS6'[9ߨy|}_2O%VNAĤs![x$yǘ!Pg跛FWA3· ҨQ#==QF7F1h C9Rk mذe׾}HUUwL3g?~ʔ)7ߐ>)))Ç'xbpp!ywf&p#1a wPHY+{EExb_V-8yO-]PDzwܱc۩S{rZҍly/(916Ƣ df:RMl6c4o$yDFn݂t9s(aAb"=|ThyIƽU!HL&LPRRB>}Yf䁻{30=o߾Mzyy yٳy<ynݺ=zdǮwVZ1OF:ٳGЙ!v|$&JpcUf61Keу_e39,O^/=t֩k&o,ZZgY3S>1ټy1ixE5Iټ$&KNL޽3hѢmAQ‚DzвͿYFWA 1\.L0Isf///TGGߗC6m>|84*]ɭ[zCy.}0ebb<aG>p%PIY!_6j08X_Wy`U~Y&P-I}ذbզMˁ{S yU6l!|Aoeȑ:vl+$f*.1+8Z|#KHL=k H5\$,HL - tDHHL?'O0MLȯ޽{xydʕcZСCS&p#1a wP5&wѣAAzw3EdYzNÆ X򈼜/_J-Y2ދg8K뱵ӤI.ٲEjxΝ源ƍjZ>wMPIƍNF61F n1S&!LM8 oo$y׶h,$SXGp͔$&Ç@hǝtDHHL;~ 1c|Y}41p8䁁#Y[Æ XW*--:th׮]gfgLСuvMz0jnj^{\x-:vlד7ٱL2^ 6gqYz0 WWYWLz5EfO7z+ ՟Tn^ꜞEu/;.nEE1o-JZ }"$&yܸqFS39=7E[:EI&M sQ~zIm~Fb=_gG\o̙3gϞEAq#555%%E/ǏGtܹ6MWD;,ML <o$&@[;8yοO[j$&`@brS#:g&n$&@[7þ[j$&`@b¦o$&@[71gUx`έ@W5u0 1a7Ezoҁ/Ux`έ@W5u0 1a7EEwers+UD;HL{|hk[<:7Lsnq8 x/mmX\ tU#1Qp6}#1_]9b\ tU#1Qp6}#1_k#*<0V:w"1Zsnq8@q%&9y~gFb=-v.2Fsnq8%&B|۩5>}E6;nbMILcӗ/쎕[j$&`p} S9?~HHHDDD\\$&=S[j$&`P$?}GP': XF|>wXn ni7wgung \nݮ]7 1M-\ZI ̹Fb'&6eز{wJddȹU)TC!0 1MW`61V:wdK9ˏ~z+5=ok׮uεOX:XMbeRRR #1M9y ̹Fb&٭{t=[z<GޭiIIIsu\\! c/g)TlJJJff$&e;f3Kcέ@W5u0LLܮcWjʊ=|1/lpi/ߴ$&d'_[j$&``LLSL>FO||6ttj6 ֒.fUx`έ@W5u0?[I*wwRئ#K 1M-hNOAǜ[j$&`fK=ǣLN?qZޭ-G&WAbZtnV\ tU#1Q=7ѢJO>lWD K*HL`[ ˵uUx`έ@W5u0JsxSZ!zUn- tU#1Q c70S's)Koh鿌\ l&1snq2,]_d %eqBֹ|X}gJt$&,8 1q*<0V:w>ż:yfy$&[O*HL`[ ˴q_ ǜ[j$&`؂աuo|vHc>sA?U%1 snq;ioUWl!@bb.qmr$&-s+UD;׸.fzoRP'^ꓘ;u֯Uʑ[j$&`>%!$<lgYg}#11ו^ 1m-l\ tU#1Qg6=?śÑK;zjyg*HL`[n͔j<1V:wM=8yA:tFnV $&Υ,Q\irs+UD;J?S=?qyHLfe/a ֲ{/Ux`έ@W5u0؂Au97g2IL u\8 lJ˙y\ tU#1Q)SҶ֠}EGbRe:dK#1MtFB\ tU#1Q(yIzvs22}ׅĤPk>~\U lJvwts+UD;$ssdZp. }7ĤP+W<Ԋ=.ƞ[j$&`%&;]YۗNߓjMWn+WGb kbʾ8 ̹FbP2Rǭ7ʑ0B0(qm\U lǦ/_+W9]HLJ@BHx} wBm|nh| V۹}rs+UD;Znfv.2FU|HL ȣsHL`S&|\ tU#1QNNM7.:++V$&ֿc,#1MmSUx`έ@W5u0dM^Ӷ>#s]FbR\<?g% tU#1QBSa{v,@bRirUy$&^Jؓ'OՂaέ@W5u0b?.QrIL uޝ(WGb#3C6a„ vƍ t~Es+UD;x%9T)\a%B7U员v WjvE݇~>oTsnq;U}Ȭ#^.WGb2yyrĵm۶o߾rϺdέ@W5u0rK>ybO둘*aU员~df={RmV^LQrčԔv=/]y$TX9]HLCSA՞:,xӑx#]2N&U2\-Ɵq tU#1Q(*wNڇ?~!1Ɗ=Rj#1MY+VUbέ@W5u0;j|7bxc՟q@vv/,֝+q- tU#1Q(ͻ=k?Abu 8N6ײJDP tU#1Q^Υu{,HL HL`_=1\ŵs+UD;{?:rx#~X-WFbZsU\9]HLta:_J/ 1Fc~$WFbwre\ŵs+UD;+n֯\ozw/Uy+*Ŝ[j$&`_nO}#oɴΐj#1}zoҁ U\9]HL ~)FMAbύ6]( tU#1Q(T虾 _xԥQ%WFb6t+*Ŝ[j$&`沓/VÒG{\U ԏ[6t,Wq- tU#1Q03zߘ$&ވyCHL`_ɿ^%k1V:w씴*Swo$ZrUm$& K~BZ̹Fb`"^`#1ƹȘ$&%w>JM0`έ@W5u0ݡ=y\=$&H֠\U l-~O񾖫0`έ@W5u0\yUWO"1F!tj#1m4ԏ[* s+UD; Om\I$&No="_sxZ\snq+2>܅UbF3|\snqm=R*/-hN%0Zw+)Wa[j$&`Z浓+#䪯"1RP'/% #1iO0`έ@W5u0O'N;'WHL #1shr̹FbnHLcv #1ff-_s9]HL#HL`wgw{Ms+UD;藑ir瑘x)>pU`\U .^9]HL6>~|r瑘xܶ[*WFbˈ?|rW1V:wњ֯ݺW</ k\-n'O>vu>uٽrW1V:wϥI>KCjtŭqƃ >vJXrcrW1V:wQP.%>KRsCw+HLl/!7+{mrW1V:w8|Q</]9 ˵EѶm۾}k_:;'Ɯ[j$&`Fo̒>{?٭[#G8r 6*UjٲeHHH.]jժ9nܸ%Ko^T9ҤIqLddӧ].Wfc'Lo߾JbR\Ν;/]UVڵ3Bb`)M+UD;Ȧ>_7nܵkW-*{RSS3gNfdd6lذ+w?eq!C։?#رc˗//;HL, cq@b|!W߿YfIDAT./12eJ5VZN| CCCy'8q"oXJ4 tU#1Q0u{kH]hb")n̘1CǏoLLߎ >\lf'ͱ]HLlz] 1Os2lsI``_lYvz]n ..ϯZjsILL IOOHLl¦ovxb)M+UD;Ȧm6idz͛7o%&.+ @ܾ뮻ƌSBkWf͚ʕӿ$66e˖^zbb(O>i$X"1&hFb`d6Z)))zv?~\8sݵ8KiXj$&`F6.xopp*8KiXj$&`F6.xoČ)WUEb`)M+UD;Ȧw?JR9V:wM $&ދz?"1&hFb`dm}e\U 4As@W5u0#^HLwbM?"1&hFb`dl[a\U 4As@W5u0#^HLwv5_"1&hFb`dKrUU$&pͱ]HLlz] 1^S!xFR9V:wM $&:*8KiXj$&`F6.xYtK;*8KiXj$&`F6.s.eU%XJ4 tU#1Q0uĤHtUR9V:wM $&Egҏ!1&hFb`dIQ5|1%\UooM6]tIgݻW\7ͱ]HLlz] 1)5_=w?1a„ zxիWVmӦMn+M6M̃#n|)Xj$&`F6.k#zHLn?kmB={wޙ+^AHAuqK`4 tU#1Q0uĤ6>;$>gm۶o߾R>gϞUTuqdfffA'7TǎСC4h /%#Սȑ#>ihh#<"+M.4 tU#1Q0uĤԥrU=$&7.W\Ν.]ڪUvډbNNN4h(nhdc w-дi:l߾}ժU{lҥ_yP{Ǎdɒۋj֬YJ&Lo߾` :n//`bdvMKK|RbTҿk&''T,.6wd+UD;Ȧ2yyrU=$&#<-n;|֭[E[M9uTq7**ˤ<ڵ?eqqm޼Yt[܎O ˻KKK6lXŊ2dNx۷#g̘雂ڵ6'U^E-]2hFb`dIQt/yXe$&cĉzұ`і;v(qwW)1k,q7""CebҧOӧO1ILě?~sW"Ç{V{={ҥW_}'#յk_ɓw!˼xlvɠ9V:wM $&E+WCboґYN6m;|sKilf'ͱ]HLlz] 1a7=1 uEFF:t())I^H8KiXj$&`F6.雞0uڵ+!!A^H8KiXj$&`F6.雞hCBB"""4 1&hFb`d %&^& $&pͱ]HLlz] 1a7O, cq@b¦o$&޳XJ4 tU#1Q0uĄMHLg7;<&hFb`d xϦovxb)M+UD;Ȧ6}#1MR9V:wM >E޽(.n˵=8yǿypQ>u?ƷyjO, cqu!66ɓr]hѠtҥ(Wlz5]ڶ|HQ>:oyEu"m$&7;R9V:w_7nfXJ4 tU#1Q0ׅmWk~b쳏W~t]kSbu"?ƻݻoA zSt=77;KiXj$&`F7жm۾}kxbrOKhK 0fXC߷xddl0Q瞊׼~m~u_loڴΣ0jqÇKU_/w1v'oyg[oa:{vz֬QO>J#G{`t=xѣ HLw(^, cqׅnݺ5߯ZjӦM׮]=qD「G < ؑ#G8r 6|gbIH={A/xӦ͛ׯ^̙BCSԧ~ zqɒ/^|80.hذNPDqZXbA"1XJ4 tU#1Q0ү 7.W\Ν.]ڪUvډbNNN4h(nh"77W:xlٲe{r|RJ-[ ҥKZcƍdɒۋʑ#G4i"<}j֬x ,]? |&1w=w[@@rʆ}%nǯ󟗿i5eʔya~~u{E^=Ӷ_x[ow_|"J zv|_sΜ9J[ZGSZZ|m$&@`)M+UD;(/1yGcǖ/_^غuog9uTq7**cvv~=̙3yG 6bŊ]OlٲEr$L虘^&&>uvvcyS!1…n1/滑x/4As@W5u0#$<<\|3g^5kq)41ٰa1c Q?~11ӧ~[V3|pn|_3INƍ*W}<  V.]_%M< k=HnRbO}+q+&̞~kSMrfyMlf'ͱ]HLlz](Ą9?~HHHDDD\\$&pͱ]HLlz]($; ;//F|>w>G7|~ހ_PyCFulg \nݮ]4 1&hFb`dB$&FuHHȢpVK-I?Mu#СCIIIXJ4 tU#1Q0uz׮]nqS#aU-^Bb`)M+UD;Ȧ$& Io ffɻ}۽K亃XJ4 tU#1Q0u{O]Vξ*UGf/i\u8KiXj$&`F6.rb}p҂On{mtFyovv~ySXJ4 tU#1Q0uGUu?n_6q`/ NAb`)M+UD;ȦLLN}UWִ~̆>[q]G 1&hFb`dO%&)1GŸ~7-X-ﳧ=UG 1&hFb`d낏$&'"_TɃfݶy/%w 4As@W5u0#^nzb⺘-I(ﶿƋ4j$&pͱ]HLlz]?N8q}[Sؐ/!Ӏu Nenӊ6'fvR9V:wM $&%i(R딺C8Ky#XJ4 tU#1Q0u!99yUcǎ1bG鵞rq/.sQw#Æ| ﰿSΟ??$$$"""..N/XJ4 tU#1Q0ruaݺuw7?ꂻʐf͖:?8PjbZ̈ٵkWBB,*v_[E{U0f:.a\91-bf:t())I)r:ͱ]HLp]zwX rq΁zv^ \91-bf$$$#PxCR9V:w IIIso\\ܡbkQ/4~q>l,^C;LL19)))H9б&hFb`u(-@׸Vhn[U7>g , cqq]йRcʲ~Iޭ֯n\fw ͱ]HL.hn˃w2T%Ev5|1qm\fw ͱ]HL.سִw.2F9ax[V; KiXj$&`F^2l=2䁮GY^[~w>};KiXj$&`F ^r.eŌr=Jːw. \}(fw*ͱ]HLT._nyg7QZIyLffC؄jovc)M+UD;H…ݱ?=>G^Cr uR9V:w ׅQU}79nx-'hc8y@7"XJ4 tU#1Q0ru;eI}w R(12JfW KiXj$&`F.$m޽yayWvuJ; s]5, cq9p)־<}a{>&8 7XJ4 tU#1Q0ru98*wb!l3;fWKiXj$&`FN.^ce6tzjw\osқ]q, cq9㺐&k={bzy,q2iY厙g;ÜfR9V:wݯ Y}+>ɴLy7,wZ‡͎<, cqpzKx//ҏZVcyfm#ͱ]HLlz]xnKk>.CI|}/LU6}KiXj$&`F.ssc,NN%y7J_Bd'_w'͎&hFb`dVzuݣSbp3li?gU${a4As@W5u0#\Sv "Sq߆p<t_g y|](KiXj$&`F.auOG13"z}s*|-KiXj$&`F>~]H=t|}A?6}ly|CJ̑jO;e|=ͱ]HL|wu:|+G _NAER9V:wo^6E5'N{.D KY|:&hFb`kׅ씴o[|>'y|g?b _kov\7ͱ]HL|pbF|Qv.j_HnY zF&hFb`#ׅMVghyl"wO]*W3|͎R9V:wͿ.cv`ο|2Psg7; KiXj$&`F7?nk~#%戼6S(Wnň4As@W5u0u]W;ev˻aOg]^l>fQXJ4 tU#1Q0)ׅsQV6g} pSKiXj$&`F%|]=렪Oap6bm&>KiXj$&`F%y]8Kz|xyydCAGfUl%fXJ4 tU#1Q0*Bnf k}pMѡu]9T%fG `)M+UD; g ks_e[KT>`Y*nxd&hFb`du!RV}p3Bzo3)QXJ4 tU#1Q0pv/a{F8<3邼 XGfm&>ĺ7;JKiXj$&`FV\r3Lҟ}Pf fCx`)M+UD;د vǮlk_eKA1:?;͎4As@W5u0b.ssXsܜ0ytv嵟v;p37KiXj$&`Fu]H?v{l!Ÿ~Ar7Cqqӱ&hFb`T,ׅst?~;7W߾yg6_P,ovͱ]HLnu>u_lXypg(WQnR9V:wэ\NRۮ&ff|Lllɓ'ju ч.IVCYAuț>4As@W5u0.\ k>s>1a„~Fƍ t~:>:m.Z[/^@ƍ[dILLEȑ#M4<}ؼ':l߾}ժUb_Ηb ʕ+׹sKjժ]vXxrƯP^XJ4 tU#1Q0*vmp.'WFȻe˖RJ 2Xܺu(N6M;uTq7**Jqk׮!HMMgΜ)Џӧؽ{eBZZڰa*Vߕ~,BȚy_$yfV܎z&C .ls++HLT@|+@ea _L\"i?<{^FSQ0Oxx(Ι3G;k,qW#={D}ԩ6l7f̘!Ǐ7~d{c]?O~ѰDڑjOeUMLLFZ<9Wjһ.l$73lĞm|vȲ{ d_P亘*ncIzHLTkg"Wد &ktdfyG\._jĐ:uDGGi桇%Y4hsΤW^yE|>yd``8 ((H|g.[{[ HY=c>pCk~C|L]]M$yҩ%Wqc2w!1QοO8< PUáu]9yWj>Zؖ-[z⃫(5ҋM6ݽ{~p&x.?"( ` *(H  z{/BХUz{O..ɝXq1]ym|?s)dLگ_wy]vo_KuuuZSNn߾}޽zt~/s7\?gȑzey뭷,,,zY4$o N>M{{Æ kBI6[ @,ş7򙿏i1SFRW)A{=h)_dkU2&N|Z$ZVkbpWPPT3^$!999ҋҒR,7^WUU=q= Z?`.Oi{ "(-v^pڸo>&e]'{T^ڻZh,?ulADUE"PmUUSRn\נT#y7 굳K6@QkBA/uL(#qm]aPlYy{yj>qKԡNu/Ex@۠uzlADܱqVgeBٶVukko8sU0mKh[ TfBǹo}7?pFvQUNl-  ZՃ*/rc@%RD2PguLsfp":34};,ߟhMccr\j%#LUTtZ-u fkU2&8ObeSgƝU ٥AR6HCs *+2m6gDAs"<3/Mq->**!uS=KkIguq#^ΰKCUeU9Nԫ)*=qb[}f+ 9\YR;e TuIT&צY;xJ1Yw~%sJeş5(iB6=4Bۯfǟ7bdL@zl'߅mrq8 Pf<V[^tq^iQ+ړD"\[rfp%}Vj*s\bxNnsi1t"^ʹ)c >[Pζ4y?dL@7rc6o^g好!Vوk2<<02obv!P1qxLakDۯf ϥaVsȖa+;~=6)* ?Xviic~k4g $'uPȘ<ԖW}pic>vmiVNa~qy#ǁK,ߟlQh< >6SD1u5eY~QoxLjMBVZ[ћN5%2P-_6~fᔝTC'18o8a,*'^6+On&+̢f[V%Eks(f Ipi (,Fe3D5A }m_mu3i&Z䬮J{޷g@Jumg-KTi2&jJ1zw\Fސ-җ2R[Z5-ҝmhsÃ陽2QUv]bpB?,Jn64RJinG%Q8h-Yv>QKVSS͆ (ԄƞZ;xi4O3P/9L]0K{ɛ'lܲ).c?|U{Bey72=:;<ٺO/u]ݪr VV#,, ~΢f{ZNA?j6n!dL@AE|C]Fb}IW-.QV/۠b<0鬎IFՇ1Z -?p8UZ=~ ￐b$(ohmGi]3l<#S~6_ Z;}l<#dL@ Py:Nô82]MVUT8z5}Q&=Ӷ֪̼t3װkϦwF$ K\'JbndV =eOЙ!яm炌 (u>>I'\$Pm. =]L)nu2z}e3 fD ̻i]Pu6}fUK(']1cNIT2 Ra5-tY|zWF1V{n'Zl>%khsB p.gY:>&'nW㟭muK&k~I5pwyZ(ؓ-7K{fҸ6S4T˶A cmGEj6u&7GSB%x q&U2{..:*3aaiWtL9AMop-`[u^S޷gCilJfhɶ~'&vo- K}R:%|Ͳ(,A,B7sr݂ZxFE!qh L񤷲`J}c]գrNctN#]QnZ.$+w66q(uϖL0]>V^g ":PG9tY-zh:(pLaD'ZV*eZ#'nIfYS. &႑VD% nnd~ AqNqD"cϼ8B+>vԩN7s|&; #2&L]w]~EwMs};*Qgcmf48i#71uZ>CˬO7w E&o}=[7ԃ|}%#->覹.gؠD f6Y1cNA@4 2 ;QoxNAg%'n q>Iғw; _c[ RElWdєzYv>- qAҌ_PS~Sk<]FOJSXP.Bt8 Y@T<̊p(KLcNWhMu)wl\Yt zσGUV~?,zN[+ni-Ș޳tM:0b|v!|p&S6RȃWmgDeLڑa!c8E~Z   k褖r6tYI:VO5ᢾ.r];y c-`kAD59΁!:g?a8ӌq/zkKlTIĝziU.d$yNCVzLθGo% ,%UUI;=g7;of<O .9 x¢%'UKlЙg溇z1 ؇w]@&on>#\M5t* LjnXKl-( KǬ>=djXSR.2d:=G&%߰[MK7y0_R3̑nViu"Пw}&ۓi?|Sߨgֆ @+KcS-c-9R_1wZ?ګ4>}Dۯf`|{>P[I Xv!aAy7 ن&Uf䥛8ΤHϦ{M|HřbW8 ^.,P [啩F|߅ 4dEћySGfhߗ6@@}"[QSKr+ u)X5DL%lGYӂIg4*2m>2o8`Y=~.lՖVdb~9mE O҉sjj6?\pK?,22w~r 3̘n};ϦvԯܓA@)Ufg;'av}wa Şn^ٺ׭ZPuU,{__14sf+T# JbS޷xFa>)Q~>![N%mϋIW̽f4ys%{/K"hʒңtqGSӎ62&mGef^[0]v^##;4x=ẗZd$7C<(tT'׼Sze3,)sd Ӯc<=~wJDXLh6<;+Ȳ zYt]/-Ȥ_[` %@y c5Nөn::gϦS.-Nt) ~҅aM T\bqVN.OdX$]2KW?- u1OdYe;1{G:= / cԖ=̲Id.[4|uS-=)F|݊.e i"a aA uϓm6"-αaQ[Z. D'l~@؎vΣ})TD1&-KLqE䠋:Ϧ:xti4S X<. u /W%cOs 5 wf@)*Ͳ:tcy7 xL}f߳N{w~5E2FU1_ŵuyLeiC>Ai`ПCϓ08pIΙts7Ŝ!m+KHK`>a3rw8l1/L5mvc~Momu~F3OLa~1 heWzP\%!j.F)Czj!ms}y{ɍlKEZ-9.F8"Q32WoqUiM#eJJ nӳvq5M_ME(XUNaSxY}8D c2Q[^Y-rKaEyem̬Y>ropKL2, bqg2@Ӫs >B"#x /gsƍGP%Qɂ[A?ШpP|R#wn-K,N5pa@a` Dk(O04RT-~kxhaQ`ԔS)kvmdĝzP2=7]6M3UYȘՖWR> yՀG47};I x>8093\HQX{1p$#޶[|+tLnU.<"aM˿MJ7s yez7G>K_w'׉L] bÏ)ȉ{YV6~E T{ҥK*%^6_~9F zl#b+OΠ~ߒOL:{LwFrz1RW%,KHq VԡyL0` 6wݷm~F{T oP.~ϰ`@3O3ٺ!c?ђ:~߾*]I(JfkT[Zwsv'v>q 3d}:])LåNb:f=]/sZw u=;w40e!(Nnq>9 I7ō$'#W3$s,FT. Ppкh|wQ B]瀕G$ܰʲ00vf)Lc67G~5"t3 ZE&mhB; .IniȊͧR ?fIdՇ᫃479ZfA~]T6>AK~_~ئ6V5:IDp&x /޳ucOu-dWf,_md>Fg<@ cmX$*dy;ǟ3 uoAQkmhvS/tI2R r]]- @*fQJ!km9dtr mW ~XDBWsF+,܍?z3uo~@k޳u~>5M^oaplez.:@A(XITr?ֈjjf:Inr:ع"5]1K_\-Oѻ#VKnQ:F2{mM5 l>ťT2m "%ҶW;eG'(5VT-u Nkɠe[Ğ}_gn+C^LdVlIcfy}3[T; 9ѨpG׆ۛiԉҡx;{./4fCm&S'bV!,,ĞOYs?, \[ R Yv>DsoA4L&Kxt Qt<ę{_4LUcєlbixΣED{ Q4_vtWwi`g>nVş^js2^,t+?ҸU>%`Qӷvo7ШnD--/xCꪲ 2mN=_qaN#ͻi^D%|:LMpfoU $Tjr?lq5ۯf.:ΰ0(塮]%?z)/cOu^Qs`Yzt5}KݴUA?oDgWa~1ƻi*# c 5%P+]py*-i_8OJ.J*OEJ [szNPvb(宭Mom+f2&ȹĞ<^-ӹ¤(jFͰ4;kuѰ1u[ћNtR׎-O'"$w:;ѿAv6,4uɰpqJcCT-,HL3u#=֤OKwFNչ$ZC<Є FA?wv BGEQ[V)#4ʃ rBJqE6z='X1SЌJ{P]$y{v6}Ic#ְKH1M,Fsv֧?w~G4{i}{?ai˨Q|XdyL*Ih 2{Gbb/+fdL4>5+>ܮ$O]C'n |",:nc[1 ^VG|<+i:6_2|e]Ǭ\{~Gil ƹh5E~)wlw]yF[ttQ_'ԃLkm 6\rJod&"-{$<+f0oS}fC%Y>oO]ϢxZoR!ESTpۦ]eKb;wR%y'gb]fC3֛UO#K&Sw˘<أgffCBB222׶%Nkt'\4vonYw4/[r0ح4c碰 *jJ bS瞖BƤH yC}X %+d'R8j(`eqIguL])hQ/ҭXhm0ܸxQd"ɡ0}&&~& [c߷SZr݂oXE`KR^\Q4ut5,)=/fbiG};s3LQϾ@y c67(y+Jr+S9Xm,A/ܷ8zM][n[nؓ%s242a/+li| dzOUE>w>z3`1yjEM} G5@vo٫Hd>:gz''V-3S'Bۃ HX$mil X@r->rHg+ј'HxLJAӐQ^]d.c,kXXFˢjaa`L_bqg?AtI&/U:Sm[ۨp./3t뙄 F6^%1&Y-|wepK ѥ.ZttK{DWynJr6h5uUrA&u$G3ŝя%x 'I j6،^dY]2 B}Z&xÉقRa kCt|9>a#'t+E [>X#g_4u ~x>F=`Bn }.$ڋN5OdAXXZ Le#ti1 l:R-X8dL@iTfI&` ԟ>z:vk~[|Ms$wAQC57؟%,;Ds#pS2Ӫ(WUvԇ~Iguq#\ΰtm&vL7l bV,MGRi@_ba:[D%g;Y4kkR?q5IQ{PHҭ+ !ԅLE\f'+NCW0z֍igu}h5G=rҭX-K–@#>ڒYϝ.'hbNte%r!Aso$"QUi-OՠsI.zs$iǁKwNK_~k/t|h%ﱺ"\$Dn cq!c:o%GS I~jᆿ]{tȮ\Iz\"".4tElK؎^6l*fܽ-*3؍h6aAC$wHJ]Lsb!C{**|/&knݺq,A6]Q*妋'qǑ6}5}']H3qH2$"IY(97ZyA,mIFnr.?@dy;w+f2&ʧH2DK:#pw](Sr ާZ #2ys[~Z*Y[V5's+yYwgn+&&o4yKoɯ͉)|w=\_/&"XZvZָaQ*Kk~Iyi F+_h@C\3 :Μfo>A:JT_'9>ʃĝz@ʹRTUY/偌 ʩO{S>G/idâͬeҊ7QF ^cM.lӸ'_pYn,**rg6e=S$/(]{oJR:;hZ$A*)J672&,V/4<ʼx>׭D+VhKMre/Eڵ5j,^x܏v؁ugMM>>E+y͚7XzJg5k,-bxf VO]4WVozϞ~eQ?RS}. ^t ʧNmݳgŋ/x.{x~5wٰ,Mi԰(1 ;_}A<v/CR#=q:mxt5.P{k~K ͌K͛WX&@Mow޲eŋ^xJƄo}-PGC3ʚ53Ѝijp+|y7Wܑܗ!~hY2&XCӛe9cxG4( IB_KbdŊO~,+zƌo?<"ҧť RW_}~Tδ4;ZaXƍ⿟FxbϛK/终UT۝75 vM0]EKkTO+J1iޤE.IyiP;/Y cϯ%4M͡;w<{vG@=nVS\ʕZ;v04۽{kaIei}}' wWDׯ,#]miy&1ъ֭ ڪW^yIIV \WwY}6xѢIܹ$Jtō x~͌oge97]vF't([X.5=VȽON=}>~$WVQHܯ_~*4{FES0t`N{S%5,ONIrkhbK8bO+/Y cϯtӥăúGlXRSO;BIN4njXrs)0N6^;=ݾq؃[!/!e14,MGR{& x~RQ-Z^V"c@5!ct+E [v &dL!n"h{Ya܎Մ Ѳe/,r;T2&X E߷e/r8T2&jr]bw3H[-wgnc57V `4(^ZWGfnlܤ:EjBߟb6KKK6`/=޸6c?>>>//+ҦX{-֫9BBBm嬭&}gن@8}gdd{Eh/ؙn^PȘtQ&ۼ8{ 6 I>| ٽ"m bbmGr6$9g& qmXymFqm =Ș<aA3/ c_X[V6H2&ϩ,)ݢfϓmIf5*m6dL__;1l@{KS@E--zN(d UQfhF @Kş7-,,e@J2&R _mZ6@U>Jj62& {i򞳛^M?,m1dLJ4tE l=h]ȘȄ.>RlM(4m/dLd%tYUj!OQhl~ wȘȌX=K{.`l&?|6dLdHT-[<&,,jvl@+A@%_Ld6$y3l@A@ʓ3,zhfXy _bSk=xv  2&PeFa` ۠2zHX6*dL$ݢfyr۠¢]~Amiڐ1 F6%lJJn5*+mPȘUsDBAdZ{Yt,Oe2&%{ҥEA yU Wn?6TgZ{ V ,(魝pјmh몲y%_b 2&<9âfvՖV u@ cj %yG mHX2z}Z=@!!cК2<,zh'g mX3wH6($dLZYEc/f J؆6$D ʺ*US2&/ly궙P=yR@!cbٺ޳tۤ>p`rEj6ؐ1Pj!mge0w\qd1PR> ToM#-mPȘ(rAE lӌ% b) 1{G?mPb21P8V=4˓3ePWY8hY/ "JdbLaA ۠u"I: ِl8IDATuUBA<6nmP6Ș(*{.z6)W~XT[V6(!dLZ*tYA$_}US6('dLԦvw&ˢfYB1PtL2,P!4 ( ybVƿ4>բfS1PV=4˓3Vܱi1*V5ˆm2&J#3%5u\_W[^âWȘ(V܏F-Ed!kqmM+{"{K/'9.#qӯ^-}qߋȘ(Q?|U足=ʘXd ۯgs.bhC1P>‚ _n`un!Ք<~]sާZr TaCS1ya@3l%^42h']5f6%P-Xy\)VMf mk{&цoPrȘ(꼢ȃWM1ﮩŁ5e葜 %&GtV6]M@@Ɉ׭auCF+rғtŁ_nqVp &!c*4%͔_h_ Vb~ ;~iPjȘ H(\r]أdL@pSs\XI7nvq} V޸^KȘ@1eL(ZQTd;#cM@T2&(˘9#T2&B1A/\=333>11 J}2&w@ `PaȘ AgLnݺehh#T2&B1A/Ș@Ӑ1 J}A dLP 2&4dL@ cR_1!c* 4 P!Ϛ1)/ 3kk2̙V/ 1TdȘ@Ӑ1IH~}ڵk#/xE]}A}n"4{mitˑ1!c*Jǎ_ߟbi/ٰH7crƁ]5iXa? /toy[čd 2&4dL@43c2e ]\4n/"Q51aoL6>i\__Ļ;z8/ejif+5=r\353m,}iqߊ%퇊 +"b.i"*₀:OܼMg`*s/>>39s枃]/ĦL/_KX1,O1]m @R1~}-Xd[E.]|ܸ*& e[nuC:th1dH7vttPU<=c(R8^ıc;vlYpŋNP1UVΝ=JW~wwYb41OYI4+g)Wr:'ϟoڴO\]fY1:yeiqzGJ NNy*T(1$- @R1ٷoÔ)-0]׮޵ 6+ULԻ[]yGG+]5kVm׮r1{֩S|R߸q41rU|}ggkeʘLB*T(=bD-^]jGt.V㏻tJttvEgr̘yYpՉ)yکmX24fvL~Ŵ{l#F;[XNjP1hbr[*&+W~#2_~d%H+wVL^ }ǎ-7۷o(sh|l^7>>ļQc(1rJFFnQ֭[A.gg͛ۗ/GM>\kCPoڴr[ؔ) .xmKrϥ̎Q-/ʫqԠbF"TL:88̞=̲K˗O4{cq?RڭVLcfR} _̡O8HYBi1Կ?1D )ۻ.1@q\bXE\rNZ?=k5ڵk\a9yR1JwEA EloIx^m-D$'jڴ^ɒņp,+&[.ukTT^QWvPR䣏:[vI]=C11{MLIFO^evX.^Ԟ3Q1d El2"%.[6ܹ N]~ u ?q\W׆]GYpwo\ܹ_:?b3:n~3/sL2Xݴi^l^󹥥4ߜ bK JL&o^g1x-tvv5yWLDS?~+8x8Q1l=2;{ww1qbwsL$ՠbF"6VLLȨZCGG5 IIH۵k&ZzQZ1淯^ psk$G)Rhƌ/g9tժ=#ڟyō F(]ԩ.ZZ{ҎBOb2~@nl޾}/S4묾/&YVLN>2^fҥ2fBo,֘TLڨ\A/W>bׯP޼vD$_JqqzVxo %}'evCQf+\HL!Q*&gfN2; 4P19#-1Һ{;~{j[J\. A|OOO??yr1*&q;k~y^I7b#䘸;(u"sVnň(NX51WЉk3гRە^s$uށQQQbTLcr߿eSN˹679"OUd+1wi>ýU單_[%::Z%\ xLiQ~Zf!]G],ZrBXXXhhhHHH`ng5ߒ[s1lضoW~{cΧ[.*&QIe~Op8ܰ~O5@lܹs%~SlKkw%%˛P1߭çXKCFι)xgr+̤&$F,߼G>%8Ɍ܁ xhLiihwCOk;iw!š WOi}6MD[N bĤ7UQ˾LivqXX@r+2gJI`2nXp7|$vS}sO/ag.Xsxwr+lGd|J/l C<۱'z@wNaN/ȭY꽤K6nQ00*& ےcNN٧&G\a!" <3߲o>uYҭXTL@6%ħ>]a\xjR*O.3P161{/羯Ǹذr7tؗsLUnp%|cO~:8]Fנn=#AW 6KnÐ{%3ށ~I vA-mѕ/1CGnÓ|'AXw|˾cJMO{Oe܊M&ލ*w "b /1n"Wn[h~.Z w/wNl:p[7{u;Hnţ/`JZTLҒSN_S5uF3.I#n}{u{^yD%*&/鹻Єh?[\\;{;~w3TLL)''6}0G9&1JKL}2w\Sr; }0mjA|ĕo vr>P1 :Ϸ빟0>&"D,(~ -1I4*&F)w7fk!6Wrt܊sϛGlz3{C\ph7vJQr+re]~O=錴dC{r&P@BYco^q>@Nb@naJI=ѷkt;K>{|#1 qvҮ+ǎ B[MiEJ]Zf;w;M:?55!Q&M:u'F2OJw3f#GiӦyyye90f̘7]GPni&d2[ؽk=\ܸtҷ~۲e^z-\VpBBX\???q[ZJe-&Luִ~-aݝ%5!qoZ~zo}Wݻ p).rv GP1hn?[5D~J*-ZtȐ!ӧO߿rL8,,re˖)ѣTRt*|gաD*2(qO>f#)RD-DFFԮ˗bduؕ+WGS19An&{wV"}W>n8׫WOg[l{WbׯRo+VL(폍sCALiiL)?5]tiҤ$~beܞx /(Y'9 r+ڸWHOwuu9_E|_n۶Ro"䧟~nݺJAAw,>>^9ޡݻwժU[,+&]1yծ(+&ғ6lP=[< rݸqxj2VLf̘Q@Ç+R/=ck?69xέzx>Դ#3Lܐ3INNK,i^e˖-۬Y3=rv GP18R_厌? iȑrGzzxxСy!CD]3^{MOh_*&&MzW_f̀?~_|QLZjm7+GFFjժ`/BNVL6nؤI|UP?P7nwܞaÆ "ٶmqüQy?,͙3sy IF-`#9ӦM[vSDݼ_WHuݺu"!#gUN:K5kZVLRSSK*FDDT9&&Fi/WܸqۣGr[&^Hོ5k&r㸸8r"qxV>,rQ%=f>>Fzk^sثW/b2sg6NfKc(cU꽤?:oX.͛JX3f ƺQQ1 NM^d@f8XxH~YXvܹӼ1**J4N81~"QFRR҃UL2AbR|y ݻwWǏ1E+oҤIr_bԩ 7kVqWGl_>vXzȰ:" I f׿ߔ$ζrm %IVIKYLbnR~[#:t(=㜈E4?Xae9OdוPV_'3)q  ZaˊYϥ/ٌ)&OƺQQ1n9[-!*Z/00P:k׮;ӃDׄ K+qc͚5qرV1Idŋzҥ"oW \ZrUT15k* ѣGv􌜶ZjP{O'O[~N`ժUbX1SC#[ b.쥍ɋ;::FFFgaÆW=LZʦM[.44T=@Lȑ#Cb}N$/3=ι8V_P!\PYaSӿ*d.]“O>٦Mі4 n+fEײeK#+E]\\OKgggQ;]͛pV1I6K/TV-vzEܹܶF{"Sϟ`b///1Ç+-"{W/ӽ{wٻ2 P_h"]%eM< *&Z!!!f\$55U$ سgrrԇKv-F[|rwɒ%9*&/3=ιq4^P-Doi[O7J?57nx't*+OLFtXw0**&^!]3NjW^ rYӧ;::~w]"˟?~<м]ti1WL,Gų/[,>>^Gرˈ*|||8"mܸSlllz&IPbEqXppx.:XTLkxd ";*_(Q?5b|OOO1rR._+إK|)?ub]y勇I7D\B7Ơ7)'Ntdiܹ8֭ڒnb"2L‡nz7EK_ b㱯XD[>\V_J|¦;_\ w*&U>%HwYJ/̺*Sȭ@Rbui&(N"M<*&G6T~'^d.5!qM&r+”bЩ)Kbhk;Mi)^yɭ0}˸ꇣΐ[l(r+ d h7bf*&@Zbo7`'n"Wm[ah7{-PbDz|c d߁'_An1ĸV&*&7?[;waȭ0 WKN~Kd ޵[OL{O ~LnE.pdgɭQ11gV/X "M^&w2A{>|,zהf ;>rSV@&`_ǞBSi-HOƫw/yKS56r}wY?_`_XQ{j⣟4+3L-vڅ;5yylq %^Z淏)wZБdù|/\UF9GƝS罟l|'C )wtΚGb{eL{/uM)JHo.P?SC%%1÷!vצjLRÉ1b?u^z**&؝#_JHQ?S3-u|4zʖW7Tz{_%lJPXnQʗwb`w"Wn[[w[jt=g_ve] L)[_zwKSb-vUɚŎ#9c]}瘸{lNjB4P1_6XmDJt33=wɖb/myëA0[Osi&޷?XC4P1)*;<; ƧDkeS9o݋=v+Q1m{̬/Sj|M1q+&^y)%z5Y}TLGG?-2uZlUd8r7@.X[u^#!0w 8o^ :bcUz?햽v1q蜑m|.7^ncl+]vz\\@`j}DG9.Becqqq(ǙYӯ2@yeǙ lk0˲Qסߍ[+ce]~P1{߰MDğ;V2akyw㱵 ..b0r%:/00aaa:[Cewqq{x(1&444**J^'bkyw㱵 ..b0r%Y>|_HHHDDN:ֲcke]\@`9jn/xyyCl-;nwO?]fIÆĒ%-Zt}hycVwزpbI>V5&$"iɕxTnwQ4feIFmt4KL< ֭&6ޣd2%6ؠA]L*[]wo8'(SIzmꓪ3tumعskv^"9yr4% 8l=VߨQ-ݽOmY_?O< {.Sz/q\Rfv*z5@-W\`e˖YAP"ť>KN-Հv]\1sWLIdJd *PB#hRk1$X 6Z٩w-+fx[KΗ/8&k{Cce5V<y:QUm%qؗ_R4"*Oj>ï-u۽{ժ-砄~P1O_h"+W~_ ZYAӦ4%9ztzh˗'Wzq8F䇖]"x!ړ)WddnݺEvY'j501Uǎ-nylG%ULDHپ2YbYj۶Z RVWE F'K`TLace$&8{O+_B]~}Wt,QkyV'GPduLJdRxxvttOae]\@`4 l 6l-;nǿ~@&˰P޻q5 8AHa܆eߍ֘1Oe+:vlxxm6ٲee]\@`? 0jnֲcke]\@`9FmZvxl,C #è [Cewqq{x5akyw㱵 ..b0r6l-;nDHCPREQUEST processing (Kea 1.8.0)A lease was assignedAdd reserved classesClassify required classesBuild configured option listAppend requested optionsAppend requested vendor optionsAppend basic optionsSet fixed fieldsEntry pointSelect subnethook pointFind host reservationAdd either KNOWN or UNKNOWN classClassify (2nd pass)Process client nameAssign a leaseAdjust interface dataAppend server IDon success exit pointReturn no responseon error exit pointhook set DROPDHCPACKDHCPNAKon errorkea-2.0.2/doc/sphinx/uml/appendRequestedOptions.png0000644000175000017500000014233714206773363017307 00000000000000PNG  IHDRF6)tEXtcopyleftGenerated by http://plantuml.com09iTXtplantumlxo0-GxHQivD.` lf3I`0 |}u$-+j>w, 0)Ȧ2VQBfd]V!Ant*o{4M_洛-ڋsC}^PZc$2A0λNxZ:AMgU4dλd2-7`IE*{ *UBr6T!p ]̘ը!9;W$qKZi..,Aą71gSEYO ːݠO񆞡>T92]km#5lp_ft>̮װ{Uv1[\n!ɷ6G(8;|<b%&bOc/ a1vh^7Kb ^_Kbo!{N[#L#ZIDATx^] XUF}_r_3M2ܗ=S-MM34MM+q͵b."*"*(b-W D%54L=9sΝ #{{oΜwp)@ $-  bZbZbZbZbZbZbZbZbZbZعb׮]̔p`Ș_ɣGE<{^~HHH?;trrz7;5&ҥKVoooɴiӤ;4(0̜93<<ېi-55U\G^jtz7nHwtӧOKwы/MKK===׭['h++W1͓>:X3 kL R ,EMQ &L(Z(5k^|F%J8~Td58ܹs_g7n?Kp֭[ K. ev؁e˖=B ++;pڵ0 KRSNݴit%(1gLLL߾}7o,.NIF@v '@XXGż{+o6ZCQN#F<3N#|G!CHЩS'vjcccUˀŋ?uԓL2D<ܨQ"E+V ְ2:$08QFIwXMY(0Cv 'YYYjժYݚX1̇ӧOB^{Ibh5}czz:>iZ">>t"g*Um۶BeX{z5n8l_w׮]~===z]\~*+ׯ_+vU=~| իWoZ)`Ν;þPF.wYf8(> YZ(QI6lXŊqp~ ?s86ʕ+jY:&@ff&?%K!}|4<ң(L@ŋnnnXd˗Gf u KDrد#`j:kѣGѱ *cHHvBʖ-;xÇ MxsҥBX1-Z={I)2+&5l>ٳ'k (#*]V(A^)J޻w?uΜ9#Qe)iL¢}<8ҥKc@_|E W*222ի8Aǂ7G9 4idą G |pUPPf |Uw5n= B7nDɧ~*XPP;v,.XWΝC9$]`5q͆ NV>CWky4O*b0\c:_?*L:Uy9n1 ͻukPDV &O줨M90{l̘1w߱bq3PBYw}}4;nq%&]Z5'QX!ۤIqog د$GQ%'g-T@C&JP`U 4ǟ~IlY ӓQS̾})S1x){+Cb֭c @ٕﰳ8{cǎ(D>[1l2؝3gȆv+as ?Mn.=`ѣɚ|mVdt\ɂdt~gl"' 0{lزeKeט5偋ӓD k(O^VLd$LMKV4VJDC.1ts'{=i@P(.lӦo/^t2:V18 '0rHV N];'!|k7;Sjj*1O\s+gSjtXHpMט55 ;x9_~%WnrhOQ$ڑkzxl޼Q4hЊ+^~e'(Ɉ7o ŃfGAs}y}d_-aeTLv[`߾}8(ԟ֕!0Ĉ ٷJ[׮]uLAb1F?oEXxqA2h׮ '5XoyTؙov/T`caJrl*vCQb~)7c '.+/Fuذa$5&\Ekjz˃J ~YxqUxe)iX Y~L(f1iG}֯_$|_xB9kf{cȐ!_%NƠ}4; ʣ(L@IdzݪU+'H֭[Bqs2?jF);kBP"yĉe˖E 2_ae*^g5%[J7:28曠hѢbN-X}}<⯭J17 Puwww2~Qx._ u\ BtVlD5kDIN֭[YD>`qsgq2~>]BvTPfn= :gggdmM7"(^z!Xje˖6uT ~?Ϗ95E UQrr2`ve)jGQn={w[СCevDX<%>` v/T3JY򠘿p(f9 1DӔGXIII`f'Mm9p}V) 6l2ݻ3>~NvIh/c])@bdٗ6Ȍ{'C!}< rl:a_,=JF- #T"8(4dPF.w]v9=AK.eNPjUM_ ϙ3Q,_xFOxNZ.[ck W_e8cDŽj'?GK[y$سg[Ґ6\N:b )ԙ3g"fVFFFmرBK;/[f$gW8=6,NTTG^ʕ+#J0[Fد!8u _dɘ1cƎk v-P`xx$|GbDIex>:D[᎚]x)pov )mjB+uzWLU NQ.ޛ)k?W@ bZ;rpY}~z '$nu4qkծ;H1Ãgc%}4Qq~mޡUn+)/<---333C V欅\"F322H4m֋ OUGiM;~bhH199{_}~%o&G芵G$6RL+ŕ#0~FO+f/M)"q+w~x_iDP!vڕUAi;2#wXNJNNNKK0AixxJEnLXW^)̴rbZ#2願7khO3Y|c}\K`M ŴFh-Z4]΅/~{Ŷ]hHMS<쳠 v ;SL_77 5yD@˖˕+3xp_yӦ(LNֵjB2zjw~L6ٹXj4~)G6*^7(9}z[-amֶ틈( Rr1c?zt9)yvT 1ܤRf {W1)mV˗uk*Y3kVhۧ ٔ)K+(`+H$N'i.@iPXݻF~%K0Ayf'9z,_1J~9Vjf>v}ݣlڴAh ݿРA-hh\w_~_l#;tx#ȇl+cJvJCYS*mW(QWL.]rɒMrq`}V 9eɡS@W}ŽoI $Ŵ9bZ#*&4 +>%ku!O 6jT!i@eWdCI|p>9FQ2VXoB]1}6v}Ef ^V!ٶxX,,D1<8htذBa@i0>>_:^c>2Aa@FlE։/6G@2G1]1MnC1ykb0Z!cۼb Ν~FwÙ")́aV1Oކlpڵ4ڵHc%t0J ## /GJ.Bɏ?H cB>xŔRQ1m[JSHQܖ}D+Рl[< *3xSЧFIg6RLkYի/]:W(QwS:ZݻG BZ:|x/ TFٺߊ81ƍH-'L|f+t(((lPظqɓ:Ծ} F[l=}h+B r֡'ѥq`Pń(aE.!)́ #GwoY-gZÃ,G"Ej%VS[t cffB\w'˸͜9*ըQQ2mŏB>X)VLlLRKtsnoN%1vuJQ#Fۼy#^1sLt*#8(ʻukd t@ւ))T`H\~w.X0?bs'ŴbZ#dv?/A$KLzz4qL=6fTKT ]n#D0pDeǁ7rVVDpbt5)uK/hٲyN";BJ$ΩI+{7RL+Ŏo$60/f Ya1MK)&bA:a"lYst B#Qb6/f<.aJ H1 V2;k)&pʿC+x/`% $~&HD1V%BC֍ݯ?b+m@ 6H1 w;D m\Z@b %늴 .g @lҪPp_aQ"D XH1 t+u3ƏF` $2ndn+QPLl=ՂPg^s;%=5P~(9\g(&=5`Q;k) n޾rځ$b9PÃ>w*oݖ b쬇&pGMz^ox+G $H1s68^?͎=ޝv~#j@cNgٻ$mp+=G|Z+@TwF.c8b5uk(N߻4 ;Ioa9uNvt\1H e{n޼GYǏ8獏R~YƯ'A.41< cһ$pP|xݴDc7,*fG˂LH$}5GT.k{r %D"Qo̰$ ? ż~w"(0ቐ۵kWbb^tIqD|؁A$%<]u:]TTTrrrZZg銴9;HJMs[BCCccc;xpyz sD /nzv @n03#áߊpw ;ʹ`crNOsƄ_ݣ7/x7\]{m _\ ׌,_;3n6W6,#7n:99+VfͪӦ`{Ya"E^}L׭?>7B.իŷZ7%VA1\K#3lѢxEY{4gA1]O-SmWݾ}GN`\2k׺KC% /_ލHy6֐ vKh֬N?o|キr&SL_7r$bĉ-c _޴$'ZzLRz~}?J8;VR&j5kt AJ>̙^zI*Gs~PlLUT~ҤqE& fW-dlsbp!+mbۨʔ? s(xXcdZi]~6\bŅII(W+&7Q\cݨP,}vA;wn S=WȚP1?.u/?brϿ=RL:W!**YKİ&Nr/lQjU5kc~[.]%ӡX.\ATwf*cN9~I(Qv`K%&$s!5+xlW^iֻw{\`=!cEλcJӇZpaGl9\%a6Էs>2qDa$}X!S[ouZ_ %y7eg' ^0 }QС ѣ &Ӈ#u 3'tiΜ1u`W_} $ŔS~x0}|pKvޙ;fL!hԨ<7]C?yvʟ}6Nb儍'_=7qWO 4=Sdm~>$6Pl_ɲp.+W6gzzp\!+11-#Dg/ )+ĹիW\bA )9 m9ƨ0bgv8K?:>^ I)) }|? 0sP/(Dˆ]9 9!l #͛7*[X ŷ2 iˆf{*H-xlwd@m~%Zw\AmC*ll{q?Q~EBd 57%r.aN1ۼy؝FבPeMY2HFT7oƊ'&)2<}z|.9Põk?ݵk I*c"⍈tu1p>K! j3=ST*ls7"f=Wm˱qk+Y#y8ԨC*N '|e)P~'Kg]<Ǵb‰c0zȐ:*8K&TBYD,5ERL H1Y^_~~Ь%cNqw<WBbA r!x?-YDJJxHV#ʂ7#vT)$!EE?a%7n\waNoXɲn8ӄ߿ᛸϦ6,j ¥m++&b/u F2bƾ};HVLr & F(} tU*qt v-W4 ==RwOp3n: *FWRDZLXk)&qPᆏ!X3H1 V꽡a5zKwRL w<3nP̸A(uPv /NZ@M 3_`r4S}DY.H@w0<9Å3XOh>%b$Dk&#{4)f^@rI~2ь|g6 Ŵ$D[! Ŵ $D" Ŵ$D[u#aYbYKN7jٳ߻r%/,SS?G|DV)q^' Tr ѣN4))߫-N w {!m|XBN*`t*U**)xR,0FZA4TH> b۶}qÆAeO w/*=rě` TR%ds?ӭ`\ɂKuĮrI.Ḋy +ui3WCfrQŋ;WZg϶Ѵi2eJoB5xu \"nu[z54i|V2xp ja\ +9p`*V,WcKhŊY͛7BbXm0a0լYuĈX5OOPRN_3~ ~:>|x\&>5,LYI)X[E||p( u5k8}W^i[5lX;-m/<{j'? 'N+~M9fV<~j.9Mb̛7~+W Z*9b2 0n߈yU{1t ۧ{t8<W1(3~IآEݿ1 nތ4ih>Y> B/KL.u}'yyyGDD$&&ݾr ʟ|}t%8Se2kM|]ڷoS>٤I}VWD5NkTڵIr 6ejb2d<5kEq@'RRRCOQ/&2-"0GWxzUR0 )ZchZ9c0SxqWVV"_^̛\Z:j}$Da28 Ŵ'H4enV2 $Z?)y]RL}Mzd}Ӄ%wIG.s:y ^4D+b27þAG%__7g+F~#gb:Gw-!fbbbLL .桡 LG<~oO3-\V]3>DSO cB?7'ᙖev@O333iiipH t Cx#|i\j: VM)bOXh: 4@I>Qhw%CCPৃ@I>Qhus0[Pa {)O{ Z@I>Q82Eb%(V% hu)&Dad]tb/IYm\ZPa o/DZPa +Cu=lDZPa JBKFl=BS $(|+֠E=kS $(|\?ldD|:L|*V7#FoBaV)b'n޾rֈaO1q>w-%RO4ZR̼DvK{]!2"u%wyD*a Ŵ'68ޛhFw_ȿҏhSw8% WzďZ+5WI]Yy[RL |ί{_{ww+h"Ѫwwip:';[& TZRýy$'N oGW-]RLU>q9垗u9H"yn>zB5ohu8H1[wH&L4D|S̽]Rp4b9 hCDzAmM9,WӌOܾp)Xۓ3\x#mz p ru8 H17C1䝏H9f?<Rs`qvJLL2{ru8 H1Įt<"Fy+wtQQQiii|ru8 H1|,]6g\x#m76n En·jVcS';}7nG$(/nW{y 7G!}ѱ T򉌸Sݣ7/x7\]{5%ė:`c:}W*u Gb.|51A:zjx{Ww:OL7zy aff\u~'p 6(W___zYW^ RL%\1[hn*aM[l;1ڵ_I(x4s j_%ڃUd"ʫ1A*ĉ-+WfnMQk2eJ(6msj*5iR?~ ѣM em*JNֹsK|z6|ƪm""T\a̘@C!l=BB[e)O3GR*U*VwFvdIb*JńwJ,ޫ68qѣp?F?GԪUm֬ǎ=|x\h~k6m 4t@04%ݗ_~{9ƕ aA-z}d/cm&l+cJvJc 6]]?n5&+4ݻ=:mE+BK.dth]98>+X2jB&V}Žo%=aM m%5Mߐ#V?;G!FUV1eO ?Za)^ܙ]d;#;q$H1|BbB#K.]ڍBSB/~:zX4Pg$>އ}Dl"ǸLyBtVoB&]1}6v}Ef ^V!ٶxX,HNc 뙞%+&?\΋oƟbzgJ1eOA֚@9ę")f~@jK8.bdy^YD/6G@2G >S)ۥӊ)ؔv@;&++ )ܹ7n4| X1΋oϑpvQg϶^15jT4i# ef`(7oTli|gd'I1RL%POo&'ܾ}vgvC!KĄR FFEedXB /c0qȐ9r+9GN1ecTmۖc##@R%xŔ6rR"f񊩦c4+ƂXp^S䥦F*SI gSc-q)9YkVT`;BW|gd'I1RL%P`z5^~KçK(;wԩSݻGWF5|x/-TF׺ߊ*1ƍ&L|f+t +lPظqɓ:Ծ} ,0^1eΞ>}H4Stuɓ/V_< $!!7$7ۊ)ߐ#VMΜ9J6+=YkFɒ%RRQjՊgN)bJ>R1޽5R!$Gpܵk =]XMn)abqb qqa`:;21F H%Ӧ`"~+%b*v ˔)UtI7"Ll]]5bԨHy1a(֭UY )C_ŋ;Ca,Dh·4u^ 92uvg- k (Eƍ(|wFvdIb*NbIOF(!|~">,5qzV{|s@ۈb5;̂5?eq-"fe%Ry)#SC)(Xn]-l0dxNb*+oٲyN";BJ$Noڞ}%)f@*̙PvLPh%]\{n>ikM췋|o"H1>&xɺ*ݼI1RL3>AN'`uO7b zў䲄3o 4.I}.ɾgzyyѯ};m&W4Y@@~Q8 T7OVuYh̵|u8H1Ha#N]*] I$Z!tuZ d]B173å38 Ŵ'ĢK4kF%|]-\z}hst#-'ZNݪo ʈBJ%,yZRL|BMd4QQQÜ}UDP\rt s bosEpW8-\WY.s:M^+vXXXpppIǕ܇`!c:~ pH%. Gira ̋O233p "ljGFDD5q%&XC.:x B'+!pN( *enV}3>tkA:5q%!X=`:xn pB"RJ8dWbjbD֓BI~ᘎ<NhJ'bOXh: 4@I>QhwgCPৃ@I>Qhus0[Pa ۫`S E9J :L|0qx0 t(A*AH1' %늴)*KSVb%(V% hu)&D!#,a[5J Z@I>Q8bR 6J Z@I>QȺiPǿQmSa g늴1Ƙm螏5V)bO>J6K|:L|* hu) DF)}0nUqgl xJp ż{OHX3N0~h<9S:sAu,ż\~)l9)Gf= 4R̜G2|%ʱW@ridl[MRLSp,N/ܤ3~%YDҦyC"pyRzPL%\mH4I1M3x'\b\ -ͬn+xb#*Q?8tw}a/~=+Wrz͌Sq>^g))8b?ۊ{>r ѣN4))߫-N wiHv~w _<4Sh1 *-QJw,,"4GT\v˱{>fl >˟ F1'K 9͗+0M7W蘥EAelߏK>lo@*b*OQm+VҾ?|<9frQŋ;WZg϶Ѵi2eJo߂BиF,^aݺ5nV^&Mo4 ܭAZUT\Vr{UXͯ^sŊY͛7BbXnnS}N8@|W~ʕ+-*;5a`tfͪ#F6eyZq$M+I3\rte2 0n;.C+wD!:V0ƃ e팃jH1MAn;>n|][F>׏_fbfe%hty'|xY{Ǐouqkwݻٳۗ/fqD@.s>&'+?;d]߿'kʲ{&BWJqsc M ){>:PꠖW1ϸz'κzIwXDS_ 5 1&+;slDDT<X22e|+ΰ0ϒ%K,_ms7"#Θ1M{B@a b֎eSئ (9ut `KҥKٔe{B c%鿸PA9t&N18&#DN#j^[܉]JaZRLSp\żs>qmqefESPLniUX!E"07/[Ν!ŋP\2H3D^ I}Jl mb9|؋?%_LF`ɮ~)[wWLc%_\`9?8<\1[EEEʨbP^wX7,( PCSvUL }BCSMA1BA݀g;yR}ļpaG[,DwAڬYCl{{&7"ZhPeQ`{WFԞ?Kn#Ml߾.Vj[ɞ M*}eRwDDDbbbZZ+דg~N9~);)>)ѣnA1v^f"*U*֮<^Ʋ}AQ*Ab ky7۷oԁz"D[rp5dud{ѠA-7/=K_&﨨TwSП([wOD/47՜?:OA.Z)VM1}$G@8iӒcA!B"TK| ׮voضY|SS迚*)K,+D54eg '44#%%%>>>b4YX?/B)f x`يh"JmOO[.s5C MH15 M07&Q dAi,54eg '44e $6\MPCSvRL |BCS EӮ%}%h\MPCSvRL |BCSEh,(54eg '44ebܱq}]l~#gb Gw03|||\j3bjRhڵ+88-1&a`0M' TBĘD.W` }`j0A&Lӓ\MPCSvRL |BCSDSOX1=` xn}`j0A&Lӓ\MPCSvRL |BCSk/330-- 1Ŗ1<.a`0M' 4c);)>)ajh@Ohhʡ 3~ UA?y  nM2T@C?ДSДC!Fo!{ 54eg '44h8]Z MH1n3C]/ri'0݅ytkHѰopNI~iiij~=.-@CSvR̼KAEO|F/*\uB$ DAĝ{ռ#j xǥ;,GW3_>gh AEZc%JO8*quW(_cBt i:=:%a94Yv R|#=̳ vHA$25p":n;5}_rh: {>ޜr%tTG04|Gr~/d)f~}p(yX/ "Qvڕ33?VbCfP ZRkshO-fvRX[_颢a!ש7h[rcZ)>f,LtǓl28@$`A"7~n;)>{4qwhWyǼm @n03#߷qPv\bۢ3bjj﨣/ `crV%K۷/N^ v 3Bb\++@ag 'Ԙʈ;:bА*|G3GR*U*VO!=zPljܦL1١SS$!; <\ >c'ٹ,7iR?NH͛(UY;:[(od^1(骬e1kyظr%0))kQ~Ck ǘ25. 'ԘRҥK.Y2SRy+GcJrt kxժ.숍VhZey. *j}\wŋ;39{h56mb= #Kѥ jA`сݻ6uПUڬY{8`A+zn P% Ly!8/\6/Xr^޹sDֲLKP!>އ"JEzSժ+^wZPҎ RL |B)r!$L!aۼb>xp װa=ӣͧLymc^Jŋ?6 A/ H-%={ M@bDA5 ,C1,(UY;WC:,ܹKv27!ׯ8UY˒gP?(h[z~^\9ϊ+g@O1^1/ Em^1sjǒ۸q>VV1e/[6C7oTliȂ/^%i + M~ĈްZWL5*k' % Vd܌n8BWe-K*cFF _=}(.ϻb>Mɚ"bjjLA1ǎ?hPW ܶm9622*U)&xScŔ}(/,)Sȵ乒PXL!wAۺߊX)1_|X*y$qtr & y3vAAÇbqe wU΃A?8G #SL4S:qݻGeS説eIee#э NIOfD'% I1S]"֭T# Aa%ܦ"+wDB&#c *j9ǒkѢ)Yqo⾃ٹ̙";o8}k5jTq46m'-Ź@2wUF w~E>%JGW%s쪬e1˗/C\[F9M^ )SPcJ,w/8kBTJ";HWDǴYmjjI! y *)骬D9@Η3*k=;ϝpAO1uI/Iqi7˓*`q_IY 'Ԙzt?K^VV|>h'U7b.H15 v5v{b Ȱɺ*ݼI1 JS?Tz DHκI1 JS/\ba;"QL8XRB)>TG˂L!j",eoW[@///CДSPo;j٨ݴ y<`_QXz?4 MH15 Lulްj{ ;I!ՃNerm2X ДS^,艶;jHxwR0ƫu;}]BL#> Z>VeH&B>\P3bjBt*>>>b{XEۺVaAU뺍 rYD.؎m<|k*@CSvRL |"ĢkF%|]- =>_|Ġ1sCݪo ʈBJ%N,yCSДSț)A4gEEELJ- \; yp DY.sꇲДSȳ)&z>99966qDXXXpph q u5ӍIp'Q|! MH15LKKC+&&$"" Ec:^BÑƤcp8\(enP MH15b BNdaX0/b{<"Htc1P%h%CP rTOb)$pIpGRCC?ДSДÂ0p 54eg '44尠1?4C MH15 M9,va /a0oL(>ΑFSZtQ AeEX )7*$ ! @ &P)`P$?mf*Iyө~vͲ?oxn-;K;SBca<\{quigbJCy,<.#kk+e3H1%  Pv)>!1A Ŕ'$"3@]PaK;SB uigbJC. RL }Bb(0ԥA)OH Ef3H1%  Pv)>A߉&s3u7SbwҤ|E Ŕh273_Qw3H1%(&A)&)&Az!$$B/A bbRLRL BII^H1I1 )&)&Az!$$B/A bbRLRL BII^H1I1 )&)&Az!$$B/HSґF!L*&=҄F/$L*&=TɾMULT +@IRLb) $,)&a H1 K@IRLbƥgm^1^0 DE"5 xJ0 G7TRxE9!$*]^X~H0\[ L)&QNͲU/^e{F ʵU bLl!v{A1mh+9 $* C" PLb%JaH1 lj=ٯM|BIT5#az!tJKwdd^vZFJλ\W7lX/W;_F.]ժVuSS?U eSGP@41^yJo2FXRL7A\|}\FX(&,33ҥ}|ژw ?3xJRБhIڽ WX'ɥ`1żxxR[w~=v,)OaըQ=̡gdΝ֬Yϯ+Aڷo{iժix|6mݺiNm֬J}o۶!aKJv\իO)ůzk{XM}MAo0nE10cpp4Xљ̻[I]àJ5=Yx]4;1-DU仕]f/ᖷiOjǹb.HC1];ۗ/reu׮~\#Yx}Vأ߾& buڳ'7Us([+"ba C (v[8t=iI(SVP'+=̔L ccߤIÅ ''3\S1a1SC&qnb,]K:HlwwY~6ׁaU̙LeԾZ5~mAaM&.XC@bŢ1ZlwؤmV UrQTQLgGgRQ0.^a>qhu5_,ݞ3^A[_)Fiyc;[;Mքc"bl 0bEWR=g<^=V6-DŽIDڵkbSL,{b.VRGþ;lXBE1]הDG/ڿ?D)ԣL.7 hْ}{*uhFKV3)G44fpMVdgBD֭{|=|0Kv ]0J3cvhX5Dh%fÇaɷG [~3<ܡC]:ɥ} O5mutKfOfצ̓p&t+b?GT4|2mX1zuhPќ!pM6jܸ>&vZ`azA\ uxbܠAݜ8u͛W(SE46 6}غukژ1(w~4Qb*]ϧxרQ vw0.ԒWLF;z>ƊÙӭʊ}tX4Ն ډz?a 2b: a}Y$BM{*r ,ʑ b2;?j8I1݊_4\6^. &`LV`hc$NJÙӭ( 7K)Xq8b$7H.eaL醐h8#3){‹foG4v"?KMKXq8b-hH.%bLΨE3vGa5/hX\SY)l)D<& (((,,Fx^^zCf3C󁫇k+IrY8I1 S@@tRLPf=+&i,gYʬ'X@1X\S%V%,e!ż!͏n4]V%+@XʬCy55PrmURPf=R,[eYm\[YOb{3.1^m%‚Pf=R!~Nx6J BXH1o g7Vi[ئ;ec!ż+lk݄ez&7өY%ҝw2뙐bp %ƫAXʬBy90n>.w̮/нRz|k fS9Ey#(,,LII2eʽ;q'\⁍;b%(Wۑ9i6X%{$پ}w}]w͚5kGe^Wlv77ɥɟY~;)L,I)4M)L㏉'v1**JS/&k}և%=m,sһ$E=Ã/yӿ|yyyn$nl_|2jj?)S3B.OQ_~#Gr\[|+7x,,,l߾~x뭷n:::cEչs5k}WJ}B)۷c/ =`3f(QW@6m̟? Gr!L4*U[<=A֭;1K @_~ڵk6m5kfGO+Qrx:$$$X)$eE)}N޲eK:_~?X RL tU]?eJ%(W]LB1\۠MEt͛73ŴM4aUT9~XRL @gϞ7KU/gΜ9h ͖gi&ɥǚF4/_\S̋/֪UkfN6W^c9H1%p=cÆ ZT/(oNt*LryZmT*x|R<:4Ue5~Gm3E8Avo'!!UV\z 6lѢ_:!Ŕ&ʟ~z*^Qcƍami&c+^"Q.auڳ'mgf_ʳ/oV(m&1~y(EEɓ'քSժU+((P*jժZT/(/P̐4rs5kVʦWjorWzpGqY5k4j Vh+Wzk֭FE-VNoiӦYu|>,u6ENڬY3َJۜ5@]ӪUSvq48١K=#1ьbFH\Ncn۴+{SG3ǜ5kVsLQcߛn ,sss 9NQە+1׮~\#Yx}q:hߡC|0zuccǎ؅U `[niw6}.YY~K]|MRJٝ|>6MwTj=zope|^Rg|@:vCmWMh9õbJ@>ڵkuV,NuطK.AAAX(^s9s㏃؆RxyU=sf"%iخQ[*lD}Dbzձc배yV9XV)GYRs թSkQL)<,89<4?|!ѱsW(834y.yʦI,{ <[ە)4ʏ9o Ë:J}#XK1tͶIlӟ*UmSoU ݻ )foXnsY&`4}XguUHȫWOUB%$9pGb9g5 pxIC9#g2ЁyYJ'+N1cB}Y^4QrxՕ㎙3gZN1JM0m[δi/խ^W`Ҹq}6j޼ s Ղ1L?H_WuK*ٱ^ֽ8_pG7%uxFnژ\چ  OOO'4SLO_{5,`%(W{r-lENS1iذ^7}vm6o9/c8nVݩp;b٪US/DAm4b3)cu4c[0rHX7mjɝ:kR 7%A-TLK^RL3c:,r~ѢEӦM1c^uԿ曧O>g;?j:z@41I9qb2͹paM~.̌ĻNJ X[Gya˂]3>K3RZ.!%ЪܜQ1J~hРAe"Gv{XX>]0$hM޼53rL0MNV#1bB AM??peQcO3Բ#|9f%\S:YqĤYT[FСC:Çgrަ^[[c$YT.)8Fb^j3 tҥk׮9??\xeA9.VyH uHɊc$UL sssfΜ9bĈoYf3;׿{J-ƙ\ZފH4=,-ENV#1b vu\KKt;h$]%y`›"yJ'+W1T #VQQQl. e R?Q*<ȑhYY.)8Fbj,.X_gee`eP?Q*͓ъ*? 0yAS纙|5tng7yY QAesJɊc$%sbv˂$>Rt )f`r'wc6^yY F,HH1]e$L22 MYJ'+b%Ssrr?]Ɋc$@rIf~cESɊc$e*vDSɊc$eZv#DSɊc$e@,?s<WƳǏoo|jz̰&}]r<.ham"d)8FB\߿Ͷ ##ʵk2*Uvޥx2m:e2#OM)M:xpr7¶o_ݰa=\)_nqAu?6aR:YqS%Uo~>lԭ[kϞ@BSi&)'{#.SnAqIeˣG(Ud)8FBY:zrʔ57`/\Թsۚ5uUz۷|j4<|z nݴS6kLc%>ڷmKJv\իO ŋ_7*j: ;c>j͚55j VmɒQҲe͛WpCzM}jY8;R'%rL&dQ>tnw%ϧk"/ocb C> >sƍY;}wёU_u;9MTy1m4kРݾ@s,MkEW-9g,H30pZ1 &X3/rd7y,uwABf֬ O>Uӣ_-"j脍Mn74«G!c æYJ'+xbV%12P<i^ի[!7JΎZ]T_`MץK;lcc7lĬdO`n޼ sH:'V^ 'Uj4b?1c&XtSx⅘~ӦM3_&*@+JÇ(KS U_9`w4)^&TfʱЇ^ lIIIin1͍{o8"?>GՏ8FB+[L4^'d+VL@Hk?__x;\VЎMaAS0\۵k݇ աC!C/,a5cusrѰS&D9s^^ƍi{3՜TgI}fA7;ujjNwf&LxL\% m3Ώvٿm;cj*_}PgL*M\4jͱ*6d|HHH@@@hhh|||zzz^^ޅ㧳'tJbW? )lmUbT'N1FflVԲd_~a 0aۚI1Vh_D)Ԟ{ntS)gj &(v2-]guMwkvT Ur%'bS3`rgc )leee X&$3ذڵ3&Xn)ɻ*r ,ѫՊ(8kc )l䤥E{DӴW_E<ۑ#|y/4;C<:^Ȋc$zEIf-sM.Jb~d1RLB"$,EW? )fEIf+\6:^Ȋc$e&rYT{#+b9[hH?2ٱo,gI)EW? )+EDV>,*mtHH1]̖Z4c6ma[΁%+t»focB *EW? )RfbbbDDeojK7OgU.Jb~d1RreKd3 ʎ}b#bbp CFI̲eQiCՏ8FBYl1;SRRuzcuZeˢFثYqSB/AstdiK7OgU.Jb~d1RL+eˍt q:^Ȋc$VʖC0tHH1-7360>j^Ȋc$Vʖkҁ xJxtHH1-7l~T~ePrx;F!b~d1RL+e;jjek"b~d1RL+e8e=» `%(V% D<:^Ȋc$Vʖ[ᶒe+(V"E<:^Ȋc$Vʖ[x`%JGثYqJrK GzPQLl= G<:^Ȋc$VʖwԴ=3 b~d1RL+e]9YwNocģCՏ8FBil1- q-hDE b~d1Rf܅ 9?"YylHo.:=,W? )+ٺVph}Lm6?erH s'v_GثYqlٺvKئ=冮GV&;)YR ۥFUnϜܾ_ەGثYq cy{mDʮjsi5mvxtHH1f nX\ 5Lk8Xt짟*xtHH1ueÐ˭ۏ|$#3}l:{#+b/6v*v`›)tI{#+bڽ|e]By\!;=gX4ţCՏ8FBYJ.dW͜?22Yn*tw DS<:^Ȋc$d3B1/|ddkWFwLbbbzzznnn~=W? )f)J{EmclIIIYYYyyy4S<:^Ȋc$lqV7sݎ̢!EEE`mO3ţCՏ8FB)֯:nGFfQ;:p01v{؉ѫޚGثYqS-Vc֭䓙M4ә|v{/w٤ٵ.DX~Z_xWsbN湹{!GV#!eKbv%P cgCOJ ͮO>9{b)fܠ䜜={#+bS1 ڵk>hߞ=Zf l{{͚q(/ԩ?C_ٿuk;w+(7 1;vlj;Q4s ֏8vDCQ!2r&uNYx\uA;6~:UYWڧϽwfg4h$÷D p_9;/(ES4;9rxv޻;SLE2婿SÆF|>V7a)fy eKbw6vj>l]ƍwo0LFw7oמ޿?/mӦYm#mv}:{K*Uڲ’ 4{abŶyA9MիmИZ1}##M50G|1{5lڴ#{1Oa4…p)n}Z5bq8gFYH1EңFt֬:.n&tQȖKjR`-)LHXXa)%X|䫯"؟X<ѯH.tTX7~3gvV+fcMB,ٟ񊩧??l>l[}X=Ԫz' yǎ%t*i)gScֻ=]vfי7)8aErԕ)&sfS-= kݺ]wݲ`Diol랖-`qy^+c5 PK X^=9Vmc>z\Jtb(@xt$vj„ǾWL^~Pĉ#ϟO G0bׁ5@AsʨrcZTԻ ΋ߑ2)r0QONN7jT,ta&ΙbRLQt~}Ϟ~R#tܕ+?7wo0G @zi-rk]v}AAUìdրvGɫb!F2[تSIxYz}`oi>s\!N @} 9`Ai)C_^P-Qvtv^ws{*Sp T3}1'؃ߞGl]ɷ$CJkltbWbN؎}Z0/o&>ʟD9kFK{N`|1쏯6l#:-(HjzKm NiNAMAtv%\a55}K2hRRL eKWc<۩[[|#F3;f&grNn4W~H1KVBGSvj(,3v۲#cb&iRBʣGarNn4{{.@YJa_=|##]ˈisAYJ<)A+grẘ){ aAYzڽzmįO8;>o{fЯ)l_dֵ~Lja&I(zث~_~vwy+\@Ou RLMk"=}~)ܹYyX|KĻj̙'mUC/? RRY3K̢FثYqSo̙3X¤LJY:t!#3d$۲JH${mt`tctfti~YT{#+b![jĿhtu. ,"degsSde7ۊ5;TB(ټwN+ˢFثYqlRD+(;((uJ{؊qA7sӢ岨!GV#!,shfee?vLLLDD/Ƀ2<#⟹%:!":$%:'(:*+:-X.Jb~d1RLWޖX$''Gǣk?yQFגX \]]]W,EW? )b׊tbDٺd=ұu:p tBtEtHE(Qh%C<:^Ȋc$rVOzr2tdiKU%ՈGثYqJrc(BW? )f\:יtP#GV#!ŴR܌(_lgUXģCՏ8FBilg;@_Ӂë݁0 {#+bZ)[GcoGxA"t`%(V% D<:^Ȋc$Vʖq:5Vjg/ G:c%(V% D<:^Ȋc$Vʖ[??n,yEa,!GV#!ŴRܒez}mq(V"E<:^Ȋc$Vʖ[Rp6c6pģCՏ8FBil+imt{#+bZ)[Ԭڽ=3 b~d1RL+eˍm6AT!GV#!ŴRř]_"ç"ɻkIHptHH1- 9ilQ&d b~d1RL+e*0xM1ئ^+ӳ$<{… y={h}Ϙ1C[Z>x'NhKkxt0ohr#tH1 e\fN-pYX߿v=33Sr>aÆrfWT/:kxt0zh"H1 5KfL4SӾPnݴ4Mի5D::) KfL4áhBJڷoz뭭[fQQQ;wYW_}cBt5k6j(e)Sx{{7jh_ڵkwql8Pp 6jo7n|pg֬YfW}Ѷm"ȲeXM.aaahCN֮]p"aMDSm0_c۲ׄ%l51r(d&M&%r{PXPPРAu֝?~իիW߹s'(ڵU^ΝCۡwqu >y$8=z8tХK/\qƍ_vM]l6qpvh-/06:kQYP-55U9tLOyMQ51!XdL4@ݷo7mԲeKQ~}2^^^?3kԨO>^utؽ}R~08᠗={611qҤIpeb*`WZJ,FFF c*qRnWyߜ]jOL>X|J*l )VL{!((Hb"'۝WqԠvX,:TLe5kԪU mH,ѣ|pܬ)[U1YxDY7sٺ]ԇVbNH1I1˅X.af[{]W7lX//t$@ŋ#[ar4mڴ^zi޼yXXQ^PvQݦMg  X>\;SEu{(G~ߗo1sV`_AygԊfiG9nsvML)&)*0_;Wz// M^ TvG"!!AL\CE)))pܹXbbcQb~l^'Oi֬ل tu!ƌë\G 4]v}F) ,*ɷ~'PZ=&&^Ԧ(oժUSEL3M{Wn(ti_>(ۘ6m ȑDB+>X1\>B``fKJJsyp&URYc}B== 䓙l{ŊG]7}zV8y>>͚501b[C7Pӧ[V:ƌyDL.mCƇǧbY3{_qv?Y~hɫͥCdzS|υ[F/M]zĉBگre?_"Zb% 4x 6T<'-s EǠg)f}j7f23\B(KU Q J:D4Ϳ8Ŵ{(&$'''---r.\*LPL *#a>Qv.Z~/H>@ !ET߇h%*R Pe#7u pD{bJG(Th\9)JYH#wyɓϟ_RE DF|brzmEn߲)+bZ)[fnn_ -KIIIuzcun rɶ׬YeȈXlv6mEn߲)+bZ)[╟˃+qJ?U~}ʳ[j6&NجYe˖K/ԲeKT G;v,f5J +PR7e5RV#!ŴR>W^ڵU6zqСK.}%ŋlիܹ*ύ_oYHH1-7a:j3# ;wرcxypq:@{]CV#!ŴRpw?~u֝>}:+!t2}ۡd1RL+eˍqR_~9rHQXa®ǰ!GV#!ŴRb~E󫯾R6XHg ڴimXܹsԩ3n8%pxG W? )Ɣ'O֔\pŋBKP q3^Ȋc$VʖqeO9סB7CՏ8FBil?Q&؀ϪIb~d1RL+e(8hK:xE9<tKq3^Ȋc$VʖS6VYPC ʵU=tKq3^Ȋc$Vʖq:5V#9ñkz&fHH1-$j=F:mh+y &fHH1-м W{"x6J<tKq3^Ȋc$Vʖ[Rp6?§X.a30L-{#+bZ)[JʠWĶI[!GV#!ŴRܕөYvo?{χan)nثYqJrcG:ux&fHH1-\+p:ԇ%We-C.]qnjO[!GV#!ŴR\nO#3m;xX0I7CՏ8FBil1ppB{U_ξ}3g:fi;)ʻe7s Sls)tIb~d1RL+e `lݫG椷.0̜Vp~F7S z?ajn)nثYqJB#|sY%3]yמ'^BOK|o_rE똤[!GV#!ŴL~m8M--g=^{C㳲窳; b~d1RLkdZm<ݨo?&sLKOOQDLq4 j^Ȋc$VJ;Z{,.dV]MUݼ&8--M-n!GV#!Ŵ@0o=dGAHf!rfƺx||Z4ť+2TwKLvxtHH1-[AHf-f2|βD,sss ݠM[2k9^mGثYq۱M^G w CldZYl_+C<:^Ȋc$fV*wp~o0oޫ]&M~LLÐ) \$k[uT=xtHH1͞-Γ;loΔYyk~϶COr3PjHeb HNNaVثT=xtHH1͞-=9eS[ =}0+ۭ֭uzѾZ5UYߍzu]fǎWz޹b>}ž<}z _}uWƍwwNdd["];ݠAՄrAmd={5p`’k֬?Y`!!CQUϢ, c:<3(&S+C<:^Ȋc$fϖ|>޵Sooe w`d[QFgAs۶m޿w=J*moAϟũ_\(\zu*D&Nd@{(ܹsPahs|w7n޽ى@-{#;;6%e/,7aL(&k>|xtHH1͞-= n~˓U\\Yڶnꐖҥ%t.v->l) /? !˗ɶSB96D{1l-t߾`mPx~ {l{gp|-0bfLZ 9@<:^Ȋc$fϖ\pukժ3,lQY+\mXB:F Bj ZlWL=n@1EP:b9 KuVirTZ{i^Y8;kb:;3+q@*{#+b=[*&0D99q57q’)/0Сh`H3etVg?sGϝK^\j󩙙[7Y},lxb +$F)rAmhe&Xŋ{~! mǎpH"ۋ? gtxhb*Ȋc$fVk>njy86v)yJ O=5.E MaUWG)jأj֬}6ۂ`(tAiMWLai>wo0Ũvrּycoo/cY5ggp|-0RLYq vFۯ]L0i+;+W`ɉ9R&\.?qyB>Sr!&D+(HWib2ДObʊc$fϖoIJ1պVgɇHh夘 )ٳu|/q,_HHXyZVgqp,[>!ŔHH1-=2+ZBѶI1e1RL d+}{ z_Af9ts[I11RL dxn457d?ɬe_ Ŵ/[MY$/bZ![yrs!U\V}6- @ko tKq3^Ȋc$ڌ&]v~AaaaPL/ 3L-{#+bZ&[_N]aWX~19G5[ &yj$R W? )eUxڶAg/As ٱ}5X U/ٓє,[!GV#!ŴRS ٛ; ^uwzĶ ;<|Etf[e` *)6I7CՏ8FBila2apmFEOYڎ~dѱMAZ\|lѾg^m9l:=,\Ke=l'EfHH1-LFΜ9u\ZZZ쌥1_lR<,f>},0h%$w v||<2<" fib~d1RL+e询yJm/ :'n7 lBۻ3[ɞG:MՖ[lV? f*I%+{(G֜eib~d1RL+e&uIIIQQQl F&_f @uXu"d9B/d͙\[!GV#!ŴRhfee`k0 !oC:B*H7Y@.A) Ys&EfHH1-5ryyy`baL(^V/bbÚ# 2 ;2|!k4R W? )tsE:t&Rl]غdaMpBJH tKq3^Ȋc$Vʖzbc)2B␎8ÚWI5&fHH1-70u7CՏ8FBilOzu{#+bZ)[nkcI6`n)nثYqJr3 G7RN^Qv$R W? )~ǔ5DtPmU$R W? )~F`n)nثYqJrK GxٮN=$R W? )ֺ= tKq3^Ȋc$Vʖr W{χan)nثYqJrc?a[!GV#!ŴRܘF:{a[!GV#!ŴRܘa#ux&fHH1-w%s"CxŶI[!GV#!ŴR&),īIb~d1RL+eP%Ͻ+~bn)nثYqJr3xdrxIb~d1RL+e˝p&Es* iin)nثYqJrrɢin)nثYqJrȥkYp61~܅Ib~d1RL+e /e3 ݭIb~d1RL+eU.KML*'Jy񩪣YtKq3^Ȋc$VʖqM.&Q&u{6CU}ڡ=1I7CՏ8FBilY%3&qI~4yݻ' U} u^jOحrJ8:^Ȋc$VʖE9Kb ]Mڋh,!GV#!ŴRKE9?: "BD'"~oGU$ts(n]\.xR{E<:^Ȋc$Vʖ)h2 y100f%%%]irw?LQJDģCՏ8FBilYDe !!!yyyG==scQ=\{H%ģCՏ8FBilIO/\bj+I&u{&!4 b~d1RL+e ʊ9IhBX[#Z1n b~d1RL+e IKK}REKe GثYqJr szDbW? )]4$pģCՏ8FBilb$4!GV#!ŴRgIriģCՏ8FBilhn0^#FrYGثYqJr3ԢsHw4Ti%e"b~d1RL+ePDsLJHGAAAxL䲢W? )0C:6-8***""$xtHH1-wH]!%%%99$xtHH1-7ض}HW\V!GV#!ŴR܄|$xtHH1-7a*{#+bZ)[n T!GV#!ŴRJCՏ8FBil1yv!l+oGZ4vd1RL+eˍaCT쑖Ǝ8FBil1Fa&d)8FBIT0g#l tR:Yqxv{IQLlk݄ɐt )&Q<tR:Yq0QaU ̇,HH1 S<=[ ̇,HH1 S:u ]^:!Kd1RLɜ#'CxŶM YJ'+b IfDR:YqHrods9sʕ+}FɊc$D%QeԜM"Kd1RLbp&Es* AiBd)8FBITb$49NV#!$F\&gXCR:Yq0rY&<닽OP~ѝR:Yq0eIeW3/>Uu4F!Kd1RL \KfhIeTIݞiPDUߟvhId)8FBIAy͸$?NlcJ<_ZGfe/OحMs9EIJ˥2D $RMKO J:&ɥ@I#MKρ tL4I.= RL /;>׈LJ\zQԢs<6PI$n)&A E4w|>}apppPP^1$toH1 0ٴ㨨&&&\7 Ĝ<7$''5==ҽ!$9mW\7"l& toH1 E8xrbŤb('VL:)&a 8xrbŤb o. eSܓ d=HH1 SZIi$)IL7NsO.$sWLJ#!$ )&A^H1 BIRL bA B/Az!$ )&A^H1 BIRL bA B/Az!$ )&A^H1 BIRL ?q }"uIENDB`kea-2.0.2/doc/sphinx/uml/tkey.svg0000644000175000017500000000663314206773363013567 00000000000000TKEY Exchange (GSS-TSIG hook)Kea D2 serverKea D2 serverDNS serverDNS serverTKEY requestTKEY response (signed)kea-2.0.2/doc/sphinx/uml/appendRequestedVendorOptions.png0000644000175000017500000026713314206773363020467 00000000000000PNG  IHDR5Hp)tEXtcopyleftGenerated by http://plantuml.com09ZiTXtplantumlxTMo@9$U jҪDH=6]w?]"Y;3o޼7;cnwXm4hXL@V(i= a?qT&H# `Ҟ"NݡH*j0hYƪ0'舆T$ҚWd*2A NxuXVtk}^s찿;AE)"w ,%V^lc cGJ3;c HF[t }UMzP~ǎ/>~0[YYYIOOWXvZ^yn_t;u̙{U^]QS%Dܹsk׮|nݺ+XGGGɸKw}ѣϮd>J5jTL4",,LXBbLGR)-0f |0y(,v$%Z>z{dZ]Nڵks҇}2e37R&6mڠ$^^|/;ulš@=|%*TйsgqOv_`aH\X"**o߾UTiѢżyrrrL,]'O:;;oxqK.\pʕ?FC aϐpvE/^'Θ1lnLff&)ZVBotvBi|mX|,MK???<+n:.x~K/(9WӦM HXKLОcbɭ[12Hv^Ν;VZUVڵkBB+V-lNxx^Zb]sǣaժU@dn+W6l"M/^̥?w\,[|7H31](erΝx`gK=rG5kp)w.ݻRK9qW'}BT݃-w=x+VD삀;Z ##AH x_FXYfN>ͭްaC`ⴄV~b7s)5TT 7wcn޼ꫯ"C_5q]6KTo8 `W,>zo|mc7Gƍ2m4#T*3f &\PCjj*qad.bB[+ľ54 S Vbd(6qǶx E-VLx\~`DQ0av~%#prr/e˖|͚57n-[za.lिu2e4o֭[51>ҌqL~hh( yxx9r}ުU+)8nhAO?/c>vvljYc}ɶDDD%  `IM9s1 N\yYWWWˬoFa'r1R6lUhưtp:~^r@:wJڶBoѣGo^{CxmV}kh(m@o 1QbΟ?]=|Q 6~x|X0e_p b5W+bo"%Y v:+_ůDxvnˤ߿p׿G6k׮ed'O';Hܵk{ɮ{^ _d >}:{ɺs!%ug)왪B $cǎe&Ջdcrqm(%.S_u\fڵ+[ >))K&0_[x m[7^ҥK}V&VhkŘطFv gV #U(ús)1aJ+_,ty׬&VQ|^aÆ!{FE 0_Oxw(`| qL>6puE ?oq  ӧ5 {20U2 (hֿ{ae#/P7x2߿ABa999YQ0S+kS1XŬ@E:ME\x m[^a7^^v?4h&fJkocUμg*V$hG2eJϞ=V -E2Lw-.fܫVRC҇"s3μ'u! Hca(}(0>g3f'?3a'y(;uo۹s 20U{39˼'5^8ѿYO0īSZZܼW𩎝N_5 B n (=ۄ^Ō7ж&}3 kɸfo5a6SZ+}v=VѶ"_yfNi泋 WXDGq8gݻwc8?`B'[p={/cS?bTwc޼y-NNNHDL). xu`z!,#+F///xkF=,܍a*0v?NBl%ބ^3xSƤD~[? ?%qR&3Z)SEUw ZժU( H?^bs%X~ⅇ^<ѻ(tROb}OlFFZ=lϜ}`9gZXKC '+XarIҿ~:NDիW*ԅɞ߽۷/wv=}4#"B ;x{ Ãc'|#v+xJLQpR\rnre^T+ 'xz[x m{/wӰ .ؓvPb -/̵ҭ[kb/:hȐ!Ս &R(`&jժ;]_8م-܍aR* SZ[[۔V@ -f j>Ix}5'xQlҤI, C]׾l֊1ov ĭ$F:J ;~by￯cwvvF 6˞={2ˋW{,|HHxSuĊm۶qa?>&I=%l}%OD' Zޓm@^#fu* c~gH5jU!W`(xCL0Ap1ǎk׮:7CŌ4>OԶHų]x15j>ڴi+!#O>yYQ k\uG6D}kPSƷ"./$;JΝ;`g=f JKK:u*F5t3+כhJ/ ޵Dx.e.E "(^ _rJEAEGG##xe˖->ϲ{$c0pBӻ _gr7ųcMT-' p cDB޹sGQT\ߡHm(F&3Z?+VC_HmC}kPNb";JSG! #.\(H3LbUN9Φ`dM'77[L+66V\{ .݈K/iӆ)Kz)QZ蕾cC.LNN~ѵk0 PB|#B݀~+/i ߻O|8QPH?r$;P"22211Qәr KV9F|,QPXJ}EREEEdt!KGUe:psHV?qKPPPllV~Bҗ.wEA| QPXJ ،a?22VHx!KGq\H )wo9 Uxnͳ?]7\ I!!!qqqz(1Hd-W4u&M5kVDC U vEGq58_|||N'D Bҗ..6m]*N/0hS"1Uz quޮ(jRh4111ZV8։/]L~RT4lK׮m7mĔU+UЧӵk{2y[[GGf^{{njC7xuDίc=dHjG~(m[Z2^~-Ǐo޽=Zմi ~jX4r ٳǢaժU9y2XPb_UǏQ]1wxAphU^ zz;kRϞ,/^܉6vn[z;u,_߅26,z `WOwvn;B=b`$}Aҗ.&JY0/[~f%۵k9nx7htwH9s&NuէM7[Y֭/)|){ 'h\9;^yuoP رmo EB~to{7_*tH]s挳uk~C )mJkXOSs…P-ϺBw#Oo5p?sa6Qby)vZ #5 K t1E0 9HdCΟ߁D*!aA.sU>q"(''5:?b˰êUӮ\]L8u,ܹ5)Y:#Gc5pC1*ĠAݾj"WBX:lͺȑ~F _( ݋5^w>[]!B1ޗ/G _. &V%ȳea0R`0eqI_"}rILK&)6E:]۲\GZc%!J*df0:[nܸsEP`[5bD_5ePsu݇P "b /\W@F )[p/χ`.\B7cu>uqVx*VRiN/ [7 ,)5Aҗ$}bߊcB,߾Gݺ/\Ĥ$᳞8==Wp~:pԨxihuq3 @POf{ j[ aHnPiuR,dd셾!}S: >V2fi8 AWgI>ƙ)--P~ nju~,_-,<{XV@P6jAKlᚡAo򙹈/HΙa-k8Y`Ɵ0o11?ū Lxŷs 8x(v P*wm~Q۷xh/;;R`t`;B5kV%I_'&n P9 }\o/n<]UZa>[DuH/i" G.NU9wX'xeAvrVtsD¨I[kx{{%I_VSKc"'y[^A=?'KKeK%h `$}@җ:N v葕!|ކBy֚տI_dԩmGSXXzcR諕%I8CHaA߅[ 4@#*o8I]_ !BqҾUWzGz=O?(H'~~ȉY>tfJXxo,vKe+bLBd=ubzBj ~W Qzjf|1%D Bҷ 09tᛔ7? ή˾pwGE"-ԊeiJqx}wO6Te;J3>wccI2(##qqqaaan~tn_ߪ<7B<c#cIb{&Z>>>[~^o?-D2gzta:ɡZIp=ݳd|IAҷ$8J9***((MJ%;.*a 8_MA<ш1QJƗ$} y_ӥbhyWJd|g,}gQd000111&121>1J1VoyLOO cbbp`JCL EG y8hĘ(X%K ԏ9g\G`*0+~w" :=@{iBҷ'\Mk I 3Do) Y~}ھdaAX)$}Bvܻ_A'd!r%OȎA=O \r+A'ޞ4a|mQsE º r|Vq3 'uRtm(AX$}B*1;߽cyk#[~(,DVI)S{x}IddO{ 2%FƮ 3>"3}K>!_[~.!H|/Ym;p @'dM}OH?`aAX)$}}m텫{((i,b n 5QzHN>vF ˍztqP8։/-r<Ԯф5d[^ N>L9"-\yx+΢(=rY܈ A=0z%p%I_B9Oq`quyﹹG((&r&|6lo?9.7'Gx%I_*\ݛT9KZx}8yw']H?R%ͤT#>N((,Ny.L?^Knoo{w ص>Z_K KԶN7Sƣ6w y$!2OWt>LQAAaݡ݀~+%I9>g3>!AAaݑ )Ⱦ{:.3KVxC}􍺆JJIIIOOɾ!>:H|0PP!Nw㖠XVK}sC/M}#^A|0PP!l!?22 qB+$$QA1eM" ލ߼yG6QKa5qywo+gť X!&W<=* hݺɢED͚m P0~pn,(f`Kã QKb~6׮uWr)w U?l8Bo7LGWq5I]Tj4V+ ^`/w q۶ >I mԨTA NA998DuqN[jմ+Wv)S9XعsjS-_9᧟40T  Wø:lBV!``uG*W+P ~hZ+WQ\NxDZэ,qƅzi5H ϖA/yH)W*”dd(\hڵ-+}k5|0&f33Zlqz8Vт|oՈ}17 eBAYv>LB5Xp!ߪzUh+WX*6n̿&X mƴ:ׄ>Iq6,om\*꓾)5po_oK~ibߊ 3;,߾Gݺ/8\Ĥ$᳞8,q/d 8>tQc(K{sth'S|=5P0$B7=i8EE 3fСι&'Y4i.;_8KPqawbeL/5H)G4hPmOi׮ervXs`z5 x{7?sg .>VU\/<jcy368xMYb7T I>v ݳg36q\$[kȐnG#G^}ғ:nZǯY%P۸0T@;}vtf/YHuwϞq5X&xfbNӀJ޾*.>?PW$zW?|c]˻M,['oժhLTkժlmm&O\`m.PK_P2 /^ܩxrA⮀1qfˊAdȭTJt>6ޞ'=N!qYz6~-`G 5߾`\򙹈_bK;g ax̼cr4&w'xu9)rt4,-- wq0TQ qWlz5=:Y7]_hSK\Ah%I &eڢBp}Vݺ>'&_ P@seeQnM$_D4ۿV>q"WָFޱc d""b;%NFM tM/1H̱k5d:(>((I$!>N v葕!|ކºcZտK~yRuI_O!H ΟR諕K $8p]|lPPX_w!&s`!H~D K'vkwi7j*^ |ҧK,1HRa}|?~Ĭt:fJXxJo,vKTU-9_Rtv葺t1=IaM o]նE rTT?,A+$} NKLL ߤԴS~M.>?rÕ=* sDZ•,b n{Gm:vR7%g|F>?MKT'##qqqaaan~ƫk SPX|[Fg!},cccc4 K1%|\fzbYKT?/SYNQ<+=]NQLZIp=ݳd/-8j7***((MJ%;TN(l>w7aQ|.F23F56F89!K}NPHHFJ")bw"HwFxf#c#c#㜌_tLp`7&&IXXL4D1AQ3w0(>0b1n1z1111111% I_0c8`0E!+~w" X[^դ҂oO"Do(/TA$z@'d mka qowKKIg7pH }Z >!;od9bK꽐+\ >!Gp='}_c%aaQ.HszgN/uUtm(AX$}B;Vt=l4vN->"OȔ?gm5TtӞ*a!:HLɾ=3~=#\B m3~p.!H|sXmӹ@#\B& Y `0 >ayd8zuo#/zO:MU@Äm%A',#߮>}`v!%H%!u,,x ~qXVZ\\ٳg+TpͿ[P=zݻDA'$!_x&11^Cʶmʗ/?Ufgg7o޼iӦA$}BXYƍץKUVM*W}Ο?[n-XWgw A$}BϜ 3g8a*ήbŊ0;^" /܇s (2$}BWInx+T(w>vEWu[oYYVT.|935m<ׯk8NNo8pfGVe<7}]Юv@'3ncSu)!!+ʗ/7z`\uRfx䆅W&vڀrydG dde'% 6I_oUFUFns_w3{ңKiO]C5Q [զ1L3tܸqVWEDA;HgGk8STk֬O .[lz.Ve(3[REEEdQ, )¼4٘/TZwӧn3a$>NHoތ:ԙpY޽}.Orz}}}IDC'$ʎӍ{Z_.ZV GG{Lޙ٧۷fgũ~{W^B9[}~5h ]oUxb1'I()))!C8`]u@V!A"._6zo|޻C"$}Bhڸ i>ޚ$}¬ !Zf<>afHD-E6~I03$}BX_y$}̐ IcqA 3C'UDZ•"Di/h<>afH dG' I >EߑSaXSYER^ 3C',###5 >>>gw0@04|^ 3C', 111Gi^0fra &ah#fOX:Ucccc^+~aw"x$4 C#y$}̐ ̄O!V   e/O>]~9s4iҤN:֭ck׮}W+Vخ]۷?]_h桑k<>afH677Wc[-\]]z-$fgg;88lٲ֭[Ǐׯ3U7$}¬ YWի;v:>aVH+}۷ogW5kdRSS˖-{ҥ'?$}¬ Yc޽[rhLg͚ս{w^I0+$}B刈5kTPzu֍V7$}¬ Yca>|xa0IQHw)[[*DIg7pJ }Z4 fOȎ5z3 HGpO>!G|X;#f 8$}¬ 9r^KUc㻧=`Xj.2O-qHY!2%JWۻv uPi@' I),[# _?* HY!2pVz?e B'ˎ_m%RO>!_?e*ݥ.O>!kV|Vۼ_\ B';gM#0C ~ pqO>a LJ=n{a授5wmaS^>aVHe?;XRSklN/G<<{gN-ڦ3"%n6 fOX0'=j[`ԥKo=RqҾ#BK a I0+$}"~\1~䤬F.jMnɾ陙YYY>$}¬ $au-t[St:]FFƋxO>ayPB'-BLPuCj/}>aVHyRu9QlҍPozs^$}¬ cYyPDr>I0+$}’ !.["r?0indddbbN|/q#fOXǦՔROg9jRuTREEE?dO>aID4Kj &ij B',GTe:XHZ)ePڍgdd߉QHY!Ý3aCj6+|}}CB?MOO B',GabJ'.u wN B',f+{TbJ'<4]Tj4V+|'F!fOX (6m]*N OX &JsWk׶}8eJC֕*Uk e6͚5~}o˖޽۾}POq%xy!Cz88T=zGI OX &Jt7ڵ7nx|o4[;9Vߏ:t66em[ֵP\ {wP(". >!qH` |8NOH }wXzFVΖ I8#ƪUӸ,A C',ӥos27k֐E׮mY)*lܸI\HPGFOyB?eH֥X[B9>aݐ ҿs`z5 x̙w9wc47=ҥ $}º!Ufu}5kXLr윜`*b{UT ~~%n sE$}B E۷ܿpj [KU`_à\OH>a1XyD' I$,⫕p&҄OX#*[ǐ B', D$}B C?ˋZ&$}-J/IH0}t&l:=uo*{g*f|Nbb"ZX4O4YXFp(*$}"9##Cl}kdvN\>;:o? WL0S++Yo)sD:T~KǶ=㇅šmh)|('}N{!WA>apއaU5db@ϔH?l5lvT*?ob51l|ߕ¢ ~bbbTTTPPwZݸqu7#B~# rj-0Ri>&:ڀym:xN2#\x! NKIID;$$D~~~1&J{Ac+bhZR3nkw Nt/,D/Ix`tLݸX8,, :\cpfraQP'jF -b:ڀyO>}K;$}J`,bb"t xavnݹb0(5~>Zd?ء'}K;$} t:0ɾPaQPgq^7%I5R> (HOyI'ݼQ Y#A_IB9, ․O J_M" >!k$hX 6&Ha%$š F`kO VM" >!k$hX 6&Hw/ -M"bOȎC8 >-Z B'dG꽘Ut W(YHY!rdOq[+r5v]F-qHY!rJL"i,^)U]ӵ)¢%I0+$}B譱s ł)BI0+$}B^U%9* HY!2%FoTQ2HY!%=.O>!_?-m`^RA' I5՜aذI0+$}Bg6y2aFA' ISNСCf0lǦXF ґ+\d!fOȋ7|m۶?tйS/t䢌pO>!=z4eʔM x ePy%I0+$}Bׯ_ffL^u떚YYYBfO>! 0sM￟222J$}¬ '::iӦ/?7o?9s.\~oРGbbV-1 B'vx{{O6} Ν\JjܸqDDD\\\yO>aƶiӆ믿~gʕS< R~۷ 7ot:S>xAHY!VԩS,X(̿ڷo_LRRR='fOX9oVBBrɒ%۷ʞǻȕoԨOPP.Z'$}¬ +nݺs/g͚K/ M/cի{yyGFFb>aVHS|l?lcc#4=Λ7+u }__ߐ8) B'swxt:kHY!Vݻ7>mҤRh4111Z5$}¬ +Gmޙ1cI@'sq>nѢܹsI@'C?#ssGZnܸJ"VI~ }ƍ͛Ǿ{;f͂\j `ʔ)}}otvv߿?&&a5 Y*7iQ۷oi>tOEX $}B.1q9+00aÆ30I 阧{xxh_g{Ed/̙k5id֬Yj߻5j r3tN4e˖5kT(3Ν*ߟaƧV&>!0Cjqqq8T6gp(O?BX $}BFpǴ=***((!w%D"P kO }NY|HHၗHD VIwfffzz:pz\\/,@1.1 3C'd S?:ZxDd$}¬ BZ B'iA' I I0+$}$}¬ BZ B'iA' I I0+$}$}¬ BZ B'iA' I I0+$}$}¬ BZ B'iA' I I0+$}B=wJU?Z_CV(Ha`hWJI53^ۗ,{ኰQ Ysn2O>!7H!rO>!7H!rO>!7Hw)[KKIg7pH }Z >!;odWDϗ#WAX$}B>nkwzN0ƮK¨9¢a] 9r%&zgN/Ӕ.aQ.HL [cT;SD 2،Lo uiOAX$}Bd>6~=#\B Xf`.!H|8l|;p @'dM`5gH? aAX)$}9ʾs6M{{M:Me}@>Q87Rd('#r[{A; yC'?[DSKlOpej"-\yx+β>:SMgD7Kܺ-ܵ\!9᭶u v葺tqG(,+]w[WmmrM YB' 9Ǐu&7ST%l zHl Ba)7n-1|N >!䤇snbwPXnw!&sHaiZl!ϐyRu9Ql KPoη}=7./_H3]C oyT`a!}B'r3)3eKIJ]0indddbbN̤$})ǦՔBOgZs$o+eJJIIIOOɾ Oh6zͭ$/{&qzic?W ،7$$th >GbMH'` jy{׹5kV2[7Yh8m~esywo+gswxt:p4 Ixսp=*&m4_U"?l[NLz5F\Xl3'}#>JRhZh >߾}*U* ҵkMf!1%EաCJ*tL<ѾYׯ_zuV2r'!ݻGM6ذ'Vs12Cѣ?z4{Xl+Γ'Q 0p&l*wg+~@SoY"WZկ_K 5xn~]fC5޲`z|}x hxsYeIx҇bz숙3˗/ǜخ]qFKCʙ3au8~C|<8̭~n|Æ7%(h9*Ox:8 8ۂ Ŏr }!q-:]oQ' I66em[6jo_rtsxzx͖}vApz(WN,}# X[DVX x˂Mso~m7'XI_ǘ"}h2%KvHa|0?0ۨQ=OP~~Om8%{ȑr 6qG,"[j WX"&O?}6ʕeʔ>,{{HW%BL2X ~H?+ >\!1EJ L9MK5kȢk׶,W}6oE R:G|.Opƍ&>Wf/ŵÔc-ٲXe~UXRƍ7$}C'c [䗹}u_"#"1)I 1"b -RBC=7[3׀ۤoݺ {+T('fݻg΄oJnjV5``Hwq~o\ jggScś΂/sHcL笜d,˗1?0^ڴ(~aI޽{ qq9$? folQjN쟳|f.">;| O!_о}n SZ̯J>$$)墕$}yB'~vk'NTWc:#7K2$Ҥ5K-N5M$}yB'ý=髕<>Fۛ/OHS٬|>)(#r$P/[HSn>: *EGI_gHDmt3'QXS<>x`U@}͏0T~~~{;rOw Soyh@I`i>uVVp I~:P\j^el3ɧ<6^٣H >bYZRe}tj岸_?o!S{VT5{(.i{Bau 2]UaT A'dJLMgf|WNrOȗNc}K>!_?- wp @'d ~h R";gӮ.==:ME!1rUbXo&s]0: #r[{A(j?;3M.>?rÕ=* ~+Y2ㄛ^6I,ypP4uJRm#u[ssPPPP-]wPʶn& uciXs$|:? "͔F5%ZNZFQB7>xCVr_ުC9 7wޢxBLPuCj}~I)~D~(H rs,#;ʠ=Σb޷LJŹ7u(޸L23-DǦՔBOgRPPD$oKcJJIIIOOɾH?_w y"aԤ5\|||bccZLAet:hxPPPP#Ny.S):z~FFMEwxP#f{萯8I~~[o'.i(ī$${7~}?%.<8X9'Na-qy#YSv$N8v_nzzMq(zrxH$j֬os/Nה)#7n۷ˡvv*_[oVj L2ڵ\l*Uhd0~s7UjR…8Ŀ?}dթXv ,|{HL_ӈ5X<3VSVVs_? P/0r DU(<,ICؕU1A;*ᆢKfY.Y[eijU½WvEY\E૗NNsr}~>sy̙|˽F>eѢ$ZtIwl=P6ݱB%.!Hl+=sE[JM}~ǎ E ƮZME}KB|ĶT`LR }P YA&<|گX1kΜU=rO|֭DL) VKNGlJ0JZ9^^r- Sݪfg:zua,a-yb H㿗= qƍChH倀%8 v#4{P '4lXsb7ٷ ݽ".ȈBa_qOZ9@ &M@Oj׮7&͙3dɨ/ٝxc@ 3=0;L}6>??dW,<{8j<>}${ʹg`+yF)5)DRܹX] 6m!i$v\ >%rG6eɚ5<3+LyDv];- F ?T\Yx:*jG196o9P ~0ˠ~MUTA}s}4CQ wgEtB ٮMqa:/.e[.NT`Z?ev;IǏz o]~,QT+=Ê޼8~(|E1U'HpY94Ib[1+B=@KqlcocU$+U.F`FLyAX5y<یƒز>ȵž-Y}b$34t5 {1c^1 P&gF[}'=_r{w~kk&?Q5kVz5^; vm3Pn+}y؏=xs]fN5R{.$VS`LRކsIs?r":JED%"IV{\|~&ꨉg m#%-bl۶ {`B13zfԷ1,33c}޸qΌ`4g#b6ֲ{lٲɟ~M ֓lŞbIKcqrrexJ,;Z^+WY4WW`<>xys "j2{;vBNR{.$VS`LR}E ~tٲ]t^ `Pfq5,=,d- ~e3 -ggG` #J߰a/D.$AX&Y`=ۜwu„'OL>,Чm}cƞ=;c=m Z pmF!(G8.bt IF6hP=paw>]4,>CM0w{^ݙ-CQ,&.D>qYag5o4۷9d2-<帧j߿W=+oV94ؖzs!`J:ŵ'Flt/.H1?@O2\c!0qqiӦr:{^*"|4X13]' T).;uj#ժUc*_2 uӦa$,X0mkmttv \8dDW`nƠ%n]GZ{,miϗ}1/I맃Au+E&gKH3 GW#A+RϘ ;`\b& q9Ş0+IZ5Y mǬ `LRYBb LR13DU\CHüE2PAJgy1]`ǟhhh1 ;36\e+%7Y3Fk벟w>Ij,8'S)F\l{hBI\s!_s%} c ׼#GD@~Zj[}.y)1""{XvJ*gk}Y0EF.s0aYtV~+*k;vQQΝ!_+ s&ݎ,ݵ ْs0 |D0O L&p[&?pjned{;9E 31L&pCFˍz\Mcjd2wF>2n!0ˋZaMGy%7뾯u$d 2ev4@~DAjmuxr_K?K&e7D)RM}f{OL&wLm ۡF`֑]ǹs{a}_M&%dn|dH!_ʈ]$cbb0y" bfe¨_,}׌$0L xƥVBBBDHhEְL&o?}|@ hTi~A~}뛍K=}<-ٲcҸpY~ 8nt=Y6xn4|_wLz6gA"._ >bbb[;mLI~x!NEЯ>pOK=-/"FilRd-9)MS퉠_!ň2e8,Ό9:cK)sB'Fnfs*l;N1o"W0ɶ# ?w^14\"W$ɶďf#Ko"W}[AbOX&۬@D|rEtI%͡sEзuY']l;z𠗼Tsr>45.&9p$rvOJg80sH\EзiY'>ހ e))Rm}nJJ3ʋ4eX^D.CSiI[ݻ76hPO^Z8փ{pSkiT"~o*pOn|=[>xf"W(TZ$`y)>V9r6Ǿa1T.^k"~Egϟ^c~P%*} b?kj 8;^СU5{,̸۴ix͛.ouG.۷ܴi>[3bDV5k|ݻ_W6xqUwd:u*d֤IQwĪ^ 4kx'Lp B4ɏґV~PV[&>RypXtz<k]ߺYY{1G!?(}&CR;=<Oca/n%)X8ȼy[ld4.KZ{gM9.;x[^=C@S_6R E"d'K8;??s~;֭ļ#7/z5<qۣS'N6D}6AȚ5_+=srL5N2; V_@'裭s=&nlqq$X<==|׫Tdɴwc={6)(Mr&`1S666gaܹX 8b G!n]G//\-vɐ+f""_h͋=™3XHK3WŸ`}"KC#eeq@`#P}.%(.ZKCo67Ӈ1XX[~]ժUˆkr85jT7n( l۶\2*j݌ X#XÇ}0ЀZjK~X@];N9&CQphrņh#4 CBVlsឌ G!]6%woXi's_\E/T0џڵX"Teds`1%@Lu1CAc ϞFX/kq"bT~8νz=6/Q{Vh#7)0p).XiL̷r{{!p4ysV*~}"9 W_|k &jjψooos' E?W`@h2H}N*A往!\q"-b67}5@#0j[| oqЊ3;vle@ `V"憋}\rӦnPܑJ:{Vh֡~…0=R_B[l}yπÇJok|V`̢vjesa2 ȈB/\K dF|+yyyGDD$%%eee]?9ANTgZ)A4L]c _ĖzXX: ocڵed_m _}Y'YcJ"s ͙V/6f|.o f\㳹!{ hS\€b#flY#M\ꆙQo qC~a={j-/g0PTNcKKb%ke`wdL#W1sHMOOOHH9M%rܹ.z윔/Er |cJ"s AܷecO&ض}lt~~|/tL\}.0A }O8AMǔE"mO(DǔE"O}#~1[Gʹ2e8,ΌeHyDF6#~1OSʹO&ېۘҡ\/A[>`rK|þno,)ѳEyz' @CSzA&& S*___RɅL)R 1N*RqRåR ylDN"35$~Ay)=EEt*Li%>QVљ(3\D jyLYj_҃LJ'>Qڲ~*əJJјJ}.e_~I|Q*݀d1\D j;(p`S^G/V%نhLi%>Qڸ"a^3 k*w?MZd1\D j vr i0 +TPކ]!DјJ}.}%O^sh"| DјJ}.}JwFɾczkˢ1\D Z!e,#fozk1\D Z!T8ri+=µqјJ}.(g*iDcJ+HoE=Ǚ:8|w]B]bL TE[L&ۇɧNeS$\T}zGˍU{;>.st+/"ke M{b\23T4SI.>"A1 k2XGꌹ73Sd;sCV, qvE\#VU1ERAJT폼z5-B>Td{rFM~u'VIɒ\T;|jM܍UAZZ-ј"YA'ۻ{'a~ 2َ}$|tW?HE" ΄!A&۽FM6T}oBBuS$"s4?ūqc僁Lփ[ֹrLAT&]vaw|0:1 =222rrN"+>Lw>s`" dNѡ! ,d_"+>Lа&/'*d{.}}}}cT\&A^G_IМSs6p| ؈sso޼୷^rw"/KN߳g|yhPgmXY/444!!!++K2^Ԍ)HM^ޟVwO| ؈A-\E7E:.Y2Mּ͚,e֖% ?_;op'##C2^Ԍ)HM: 1P wokƖ/]cHYź,}ڲgByLtb}LԈEjT }S:G Oo4+ ?x5_~qX3mڨjժ6jT}W999ã?oYl׮ņ jݻ?ya/:;;7Ν &b1O AMرp,՟کluD]yd_=yl;x R!cWx@#yK F_?-w U<|8Q;Yl-?pgS47z[Jy)%S}.RjqؿWLپv^pҥI#[b&֜9Ѵi?{Gssl ^ 5FiVM1>/'WTiǎC̙Z{nȈi1ؽ&UZ%,l iuA!G*6~晎D@Ib%Fp͆+oߒT7/@g%kF t,#,A_Ifb*6; ` x$&c+LTٲj"ԩqcm+Зo+69~lڵkP<|8DQH̊,X~zxuNmk1Z2Aܕ-QA nԩo1l`X;;lX5GR@%xo@v K 4tL/OHM }E ~tٲ`ի;`ƍC͚52qnaa\_\O8Uɡ$l۶Gy옱gr+6;/f& ?y2dP#+ 6kI~;kCF͠`_\fTO]C"+=V<|8DQH̊,[{kԨ C@RY:^K&藧\&A@!E~a_\2ci0,_..sr#8F,zjUgc.4X3m(GgAvD }bv횵jfۻω+6}J"JNm<2[h0 )<*Ahb!vC5uȷWFZjz+)bK}RQH,Y (X{ڹsp[OfkA$ٖ0mdpa7fʷn%?͜ 3>%^F-5 8yV,o6fk-o0bCVyKƶIX~'BbKŖ?[,XM5+sAD"5 k}S>]F  oidM\=[5 A}.R}k_vD W;vܯP&EE;wn|=Y+gnbn['藃\2AÛ Nx# u} &藃\2A&8x/7/3Tfxc:A|D" z>em2Y'N=7G}.Rfvȝ0L֏Ev7`V.H}^}5SHL{flO@@O?R"sm9uIDATQ9~~7'{'k6.|ЧK,D j{7Ʌɟ}۸p/C?U}.*ifd{!r$vmsT0&F|"a9&&<)"YAJddd0 &feƋq2?gFxzefƿxL|gZˀn24~1 P pc')) A>/("YAJdgg6!!!NPD A<4 g},GDD 10)% *><6n^3ҭ- JUdΔqzv6xn4@=Xf>>#Tc(>.A"7&&&88͆|||ab7@Io/u8Hl7NҎ)\}.*u2gddcj2)fF>$7w Δor'qR 4F2#Ho$9R oeS$\TEdeeaX ̌L$TxRWӢUB"Hf4$G#lc$Aʞ %0 fF$sw8S;WoR 4XV{)AMP.I)/g*?\Z@ZHݒ^,mǔE"JЊ":SEtA(AmY7.3%.%َgT:ԖPHPOU͈ƔV"s%-+JNHÁb=Jl@4A(Am\{]? 'H8S𶚽/J јJ}.qn5TӞ8Sk\ ]ޟ&J јJ}.}ՐptlfDcJ+4+VCXkH= J$)D"JPWޥ/ݛڲhLi%>QV|r#ɡg۟II$SZEBs4΋EcJ+h[8SaMK H6&SZEvƻǙ:8|'HzBwL(◊ *S:c5Be4I\ŗ"B}m{B,O%ہ}DX{}4C}*#\"ݸϿP[HV"k,8WXe˦ΫzK5{nzJ~c,~V̦DJ}U8w}cXnn `X /wTT1HCI^Jaz+.&b [oz[>F?JwC (Q:؊Zǹ*ݻ?ag<\>ЇSSoL򖯷Rl"xs+ +gSzvwOD!~, A_+8XGgϟ^c~{Whb?kj 8;^СU5{&t6/ysmnh¥}6gkFתU Y [w~^:= V}ܩSlJc*>D;awɛ4o-8;;˄ Nȋ&N4ipԨA"[&ndJ~Y\w+f5>ߟx5Ob:g5myVtV%DH}.yf,tOH vI!q~~Rvޭ[yyG͛\o ܼGN=,ll믑5kVreV{07jTΝdW2+kGYGm}V7{1qcXA=O Yb&bItL^+?qp=!/B$.n3RdIěMW7fesK谇_w6P\t zKZ0g @[{&'<b!"bmuk,LժUH,F uܶmpTԺ3Fc7 v/]f1&v;4 Ŕ]K$qEpZ5K6l$o.T|SctPqڽO>*KR?uP\t16 z)@ q{}aY JUTdbo8ѱVdZP>{6uԩx^^WׯGöV^zw$oW#L,(#)2l=yrWBś \93`0vk3cU{q[kkZkr ^+ܵkfOY0j[| oqЊ3;vleo/ fn%b>h qx5Vf'y7G$ߑI׮Ȉ޳ggqEpӦnP<K6l$o.ķ|r3⇍uYQa[^K-i;~D" 58ӎQy-rEl`~ڠA~h߾H)C?t)n06:o۶_y uѶoysq@VZjK͛~ -ّI$l٤]11 qPA^OA{&Mi=yM[+y-m.߻Č#`/͋1'~Rd_?"s)---lt 8gKLI}NNSsI S*AR$ޑܗ.WZ*B0fFo"XrhVگfs5.->S#wZkr Δ j'~_?"s\.96'~_?"s\Qb-]0%>sa_7 mѳЙgɋ*-a|||ʁӒw|E\kpKs?::d2aSw/t\iAN NNNV9Zkr nEbcc1 6UpBgTp4d %A&(Ÿqq$>>>kЙ;WoTp4d %A&( G ptf ' '_?-y׏\5Aכ3_?"s\oJ{w|E\kp=H,W =x׏\5A׃~*3TwZkr _ i8u3Q*݀BӒw|E\kphA΄ux|iU:NK#>qMPuM]Ct360PiҪ$u❖G}.⚠\GAuF45~?tfccnm^V"_?"s\?J1P5V`iOIx%A&(Qޥ}qP,❖G}.⚠\J;ňorgbRI;-y׏\5Aו i~n;-y׏\m~QR1I k:XZ@*RCH=w!mR#Ty7~D4={w̃gˋted).R[H?dflYQOt5ԏn뷷lɂE[.M<%_?"s (nSx{@}S'X_  zvfI㯼HWjw8巯]&UiH5߹+BpFQ*@K}z8Z,!9L}3>C5BiY9/Ax$ϟo2]| SV_wo'58'%c,5?#_?"s=w蟉"c]}8vJ_6lɘG?:K^MFN^;*-H<Ɣ>E"N L\wKe:N{G5l5#ӭs2@#qS:A8%({+V)d$"dg{0>\P ww/nI 41CS krLPuVk^6'qcM  {iiR҉Әҡ\/A V{oSsjr0&$REIII99oCZ+=m˒RMoLM}.◠S{9ߘOg)a. }1 111iiiYYY~牛 m8E\Y-)E\<0RZpڏet%wة|}}œ}d5oҕ\5A$N|@;7 XD>ϕH9o6DGGc-$!3 7T?x(1+k@3:~L.nF:y{ -|2~c)`cn=B\ǔD" &xcsaw|k &9'_uqsҥ$_oJmWYM.)x i?Gf/5&IjD" &8s1 a| ]ͭq"[㏷]d|}} ;8TkܥF=O?XQݺ*U\r.WfE#Nn>>>&)666==#B֏#e{A?U$5"sU/wƍ-'%_+%_ؾ}_~ rn1c^iԨ:uj[;pc^_&}Лsciɸ\5iOR#>qMP5UB{'zql}ǎ{>)< ~~_Fk1+16Eggq޹, _~9Y֬1O=վAzGu+<~uff$>(ַ@[v-6lTB &J";wn^ l+ŝi oBd@J+%obT\Yx-9wn*{}Ѽ!Cz3 MU,aTvmx%߻3" иqwM fRE\TMpnI˳e97fW| L-;ujXA ٳђq)PvM=aШQmS9Dq0pC-_xp MU,pj+{1c^1AIEKvިfE+B_1嬧eY;~D" &x){Cweˀm+f O}L6n[ "{D]tdЏZlP"2H'Opx'6>v3G϶\DRLrȨ&MUwpOР-[6¯޳ $>qMP5Cذqآ ܶm>z1cϞ@ʕ}AA+ZؤFذaGc6mԵGn=wnGR_B4[/ݽ"&9;;M8UhkN}}SYJ+%ob#0aɓ!8Ft#>6o֬!~{XBS#K*O4D/\ݧO],>?ÊD}{A&*9 tڵkbݯ}uܰVSׯʃt pDzHܹ ةS_n%8f.. L6JA p,  DVPqɛ}J" Vx{o-Qzu4U|S#k CP[QϚ5T&f;o AD" &ʯa`Sڛ7Zn^q,}b|S]#CY2̌ob;Jy4U1˷5MU,1LjaYo"sU\c؍yDзk 櫕֝;CB.:qm y{{@}.⚠*ӏqᏨA>D" 28\"YK4No"sU~\vǾ8V///W+ RA&9'3z$o:3fx- З iY:k(ɥ>|C>dƶC1}şKd*QZBG}.⚠% Y*ݏuw}_7eX_|oGL%M˒w|E\3Ϟ~u69?>x_Mk9P w CLL YYYyyy4+iZTG}.⚠% IYFFƮ)ݷ9=|}lɘp6RPౖ_\O J%A&hIcRKmύƶF;_|'mїz}{1ΠggFxzefHW>_Fueҭ/]c[#""E%4iYRk"}_o6%@!lr~+ֱ=o00ǷNRe;~D" ZqK| uЕFBg%/ҕ}V|;z|L'u6-Ջw|E\3221} 5LLxCg-x g9L@> + 9ceHK_?"s-Kpۜ,L0qAiIg2.\4-Z%-Пp A [3։_PT##>qMвgM?11ՙvsi@&G=SҺx׏\5A .~U|ΔLO8%Xڦ\G}.⚠\MԙZwOkr 7Qgj%=;~D" 5Dx$A&(zSVtiOz\^c@O:(bt^#kr 71Tr@ik$J{D" 5ޔEp S',F'џ.HZE\kp6Ei\5A'lSZE\kp6Ei\5A'lSZE\kp6Ei\5A'lSZE\kp6Ei\5A'lSZE\kp6Ei\5Ab:4я\t'bȺ5я\u1dwG}. }D*\D'H)>I$mE}d"sAD"٦\D'H)>I$mE}d"sAD"٦\D'H)>I$mE}d"sAD"٦\D'H)>I$mE}d"sAD"٦\D'H)>I$mEYӞПlƙ:@Jҏ\SH"٩\#8r>})d[6}))$T}ut&>I"&>I"&>I"&>I"&>I"&>Iwy>[XC_\J"ٟ$͡%JJ"ٝ$)JN=IC7P*݀D#Izԁ 4U8v*d_" jeV@?e Cn*ݰ^ZD/I:Ux!ɡ'+w i0 H$ASZT-pL= J$݉Oҩl}G$=Oү A{Ii0AIVHVä$OR֖觀E6ktI$"4S‡a\:cI$u"蓴#~ʔ ș>G'}"5:O*$ Ĉ:lN%}RD'UDb=IOO˓II}RDf=v"qF_̙3 -Zh߾͛YѼyZll2mIv,>"?p[>@Ob+5j~,899%$$o5kּz꯿ZRcǎ?$B}R)Eķ[UZ5))Ú5j+Rݺu;tЮ].~E'FD|%2dҤI={\v-^nڴqE:wV^v/ ħd"J,;#ݻ)*UG_-VZ ;^/;ﯿ?Ϟ=" MKI%955Dzvrr3o-8;;˄M<<Oc[AA+;thUv͞=;0n[Nm38xb@ŝ2MNԩybdyĖ*ĉ#0oҤQX\~ȊpNNNⓈi޽+A5Q9E'ݬ'ssߺȦYf+W9 iiJ&zt{=Vw*4oI=~)),cFyd|MY`A/aY X̑TK\1J*T0Zsj%EȊ"+*Y n.>dKN^8::cgv'^ޗR>>i@G&6D'@Y#aնlo>zơkgdDٳɚ5fl]6`y }>+ .PPSq }|W8֋cJ[%XT谾iF[z`esgi`IKK*/i7m$AT218 R'~~_կ_ť!x,ԲevZ|+lZjZFuas0/nР?ow۷ݼN$|u:~߷1%E[^jݧ`_&7CV4##""$kǨ9&~"%\YY ҥXq)^~֭> dtG*I9뛫@|L $+"JӋ>NL|޻Ht{(>JOOOHH:EC=$_oztr |&AE'XKa[#~AE'Xql/ ۼ҈m/ ۼRm/ ۼKͼg@ΌeHy:yl%~AE'IröJ͋O*[>`rK|þn6nѳ@(ѳEyK!!~AE'i 1M&枾>6/?P~s=6/t/:]m^}6gppe\ 2-Z%-y{jto"4~FFAX߁P;Woؼнdt5:v_@зyIZ tW_8^>}y .l۶mӦMׯ_7[UV.]oN^t2n;/ ۼ$]KB(\ MNNvss{2??y˖-׮]|_"۸$]K###5kׯ~IV!AO}Ak)BdFcƍYSNUR"۸$]K ssswލ+ @o"t-EOXj޼9[nݺ5k6h6'EзqIzBxt-I&]Jz "t糅e9ĥRHޥ$A;9T@PX@?UI%AE'NWrB `TDGtRIDзqIzT\IjB&cJJ(Oң.I?ӫM_0Vvy*"۸$*@C:}A(,z i%REзqI:Տse7Uda= Jo"t+9jVO&"۸$*DFV"۸$qnw@pAE'ZAN TDפҊo"Hwo1IwT"=yr74A߶Eз]M>m -d5z_m4o"۳rNe2#TgφqH>_y<9;ƪ䏖߾v]^DзqV'q~ԊeͩdrI}njÚ %4ɔDзqPwo')yd2Dc𬬬֣THQA߮ՙ!/e}|2/ǹ r*~f~>ucL%=8GWOaZ\>Pd|aw,p褤͆0 gi0d%=8G?T'}:wS\g0bbbҲ0M:8 Qizi#_tZ;qm ǧc{j`zkk"ۉ;d|ϕ`71.(zV/F|4ߗf*Ao'q026ȇ(sso޼୷^rw`Zt=Kb ܌dXZD7++ IMpmP};Qccvȇmq?H]f-Zx{RF?x%K׫ž_!@ǹj=qOpmP};8|rrM^/]/[^f_}5tb/6 c2bccY&cn 6EзAtsrrlԨTի?yᅧ_~^ڵkqlY_/IZΚa%Æ4n;w;]`"D'C,Ew`WF6vq7w]6"v* rWzk"ۉ@?7p˖MU%' z!g@}ѣ[K&<|_b&ߏ{b͙3M6Gݾ}Tq+g3ExS==?=}:,>{ǚVh_NTҎ-U.jժtt~(2ZΚa%:g{y-ʽ{ի1B]J{lۀm 3Wm(m`Eb@K}hw(hQzlH};}_Ćags;P!,lMb/&CS{ŋ?d/۴ih 'N߽* 7򢼼#Ҿ vw,lvB{ٱck\p[]Y [a ̖:hSVblYkyfmWl[Fy.^SreӰݘ-d?ғ^d'ɖDз g+۷d~ᅧg`J۩SGZr[Jl[`>v99!W^u޸q^m%xԨAh8 CpWGYKR7%0m`EZu_MF饸$ِv"5ЏZ(!:T`} ҥKG1==?_}5zWǎׯ:m733&<Ċyȑ-U{ >ܕ- -ꨉ`j"H,9pK(o޽[P̙3zRY h֡/MMo'R75mڨk'z܎$D@f?70JkԨazӧf}_s'Yq+-R ];6WZ%X4q∫WCBVaa*`Æx-\v/\h*&Ēԍ6%ׄ O @RY h -H ~0U:kڇoSzx krrttfׁ5Xo0,_n:QAXߪUӻwSK~E<ޙ_ Ld0 vqihi$TDCڵkb}~σB3# 7 WAnI(vbݧT)Ҙ1.i"KMЯ"ۉJYYY0|n̵%kHݺ(|Rak+͖W`؛7k$"e`Vݍ2^mPnY+v} /} ?r[\aY_Eзݹ_kk8*jݹs;e/k pM((ӛ{W+;᭏L{{{+n6V~>(%_4@Я"ۏ>d2gmӍnK T}Rj=_6d4!ƶC ^^^Vf"۸v׮5ܬK&ɓgN/%?DзqMWOm'.:\_I4b0{@_sL}Anr53[>tN> ]m a?.AEзCZHGcŹK-dJ_J6C Oe.111##۸v(L222$pa.ߙ  d޿^#/ҳ'Y0jrP 'fgn$%%!͋o´+;; !Szpɪmlom)pʳ>#""]1dx_ \AP>╕yyfiVf?+,R$oD-ovSPD@<W[WP\9M,;| ;~ygvE+c>`І~=hu@_ا~&n.۠x˻ns=er=s|%(k/E+xz=L gU Ԕ}J/Br2r 2ʚKQL t:f`ud*tIG#l 7 C O [ g s  xP̢" S3pW 7>>1LtQF~r2r2r 2ʚKQ ava-U :࿋[z 7 C OC란W8(}Fu V gW8(}Dՠ$OA#vŰk*@RDQFIC<(}GTG#؆trW@*J_5:dfd &P`|7>6E* J_5R)cd~@,"" H)RIP 3Q.Cne"YN}]_6B*J_ x%ڎp!pPJ)^;_•A#%e+(}GK˹MFKRW8(}DD5.{!7nRUP / ʙ[TA#OPG'~­@ J_;ϿNg',s*W8(}DXrs=AKJ_56>쏖@IυA#6>SzC)F#zʠJQ֌O0g1&_eP a%pPZcyz;c{ (}GTxWG=ʧFP 8>5~އ}ƈkj4 J_j'A{^^pp0uڍJ_G:'A?n1:e@(G5`GF c܋(>\xEZ啊V,Q0n^DG&wzMf.kQfh]he[Q݆ڮ`r{1eGx?w4MrrrΈ~ӵʞD i:*>F >TqBCC lUp%[=l8(}DZzx3Ƈ >-}WN?3ߧ(}DŽ8OC@?l?r;ј3 OC?j!}GTZÃGTA?V}4>PZ>Q (}DEX>Q(}D]9~y/<<8<(}Du<@Dh|ıA#j~W! !!!h|āA#*x?'4f^t3)) 80(}DӍi #fddOG>j.I4>d2+gJ6P`2-}dıA#19҇)8(}Du\/~ݶl_Mώn: j+ GHꐙmu΃A >F G{c@Dd6E3Q.Cn@iEn#q8PJ[j<~Bĵ6BEں%\D 2 _ETJQ/e/7/"*e/Ƶ@(wK~72҆?6i]%w/="3(}&}Lf^PU[$vn>R33?s3~~rq1!̏']%wKkݼ=M"5(}8:X[o`ǰs֔b N/Jsb[ hf3NI@# fpՆa=0و^a4qHJnf*Zm⇀ZBEʃGdBjT^=СyRyΝ>/;||=y2.T '-?444&]=RyPu)%V[W.V}$7*՘7۷ Zgr⡇";"%'o=z'zqqOcxegZO9'BnK9q|ɔӏ?'CO3> h= `a~~lFRŻ{GdȎHW>VxL|ծ]yQy2c…jՊݘ2} 4L_zi$' 0s y>[Hݓn2ҥRλ{J_vL{Ґ4<|ylZy` j׮H @cN9}$$lƍm߾beJ >";b~5 O<у4=}XI=:_[+OJ ʳ6!k91bS ݒ>>[B[>5hvb2Y >"(}DvH,YY&sN»wO w&= %`Lkk~m zָHw-a(駥[Ig-PL#{w#yƌwNrxcT1V~DH׮`f4pcsMA`֭;9py˻{GdȎb^,(H?,ƍ rKy?0ٸL\ZRbW䟳B>]G? CHu?AUA>!xF>"-(}DvSRk-6}Z(}DZP-HJ3ojð5&((H J[S[wKiaq^v0wP[p+H?wZptBijzF#GlDk0_%3^9QH Jnފm56bs:m: &r,*/]/QA؎"?~Ieay-h4=H.>bSrBt.CM_ T ˌuMDDXX~1:"(}Ħ '҆y^ZjB%g|_?'''4ݻwgA*J)0]5L'3Oľ!`)l4b|/&UrgN@>L]&j3v 1#RGl LW Fcfff|tLćuފl6Έ|3hW9>H,3|D*Pa{f  tքÜed鋹&`fOaj'{ 4>"(}`o0z=smY?3O0V±g 4>"(}f 7Lyyy0t&dH?tI8F8R8^8j8v8pl93G`" SZ\fffjj*X/>>^=fO|&1‘Qñg 4>"(}!Y-c o ?࿋[aHxy@#Gd2ONhH?7,[aQA#|9 qPP]χg"GT;cs POSq8P(^l=[ʡ8(}D hX>DdOL6EqG4>? o2TS#yܦX6<(} o.@#*%{Z}CϒOm1:?@m JQ)%~O_6w/"j'$Əl/"js&Qn%\D%U2f<A>" G^.xǡAMZDW)0rO4T>"=H1 8Ӎ H 1~ΜE4ʏ f~ ?*FΜ%})!ϝmbG$o8b8,67#Hߖ5{WʂG$o@#UT4~Mz(}Zk6*E?T3(}ڵ5kN6ر@VX𭂂j˖ͤ˫pBΞw]"ۋ~oO9ajA#UDo_YNJԪUotU"%e{ӦMrv>8q|С}4iܭ[Gk΃uPn'zF އ}ƈ}]_sGPHUy*>#}fMMIٳuԁ鶵۷/ynB&ѣ,U`NLɗ/uɫWπvBN@pO0ip"}znEJ6_xa8ybfMr˿O6KvEEG[ b|͸Yaaal6*zj(a_˭5To}5[U7/n4ڦC14Kpt˖͚7w)9S6ĉCA= mL]]]<<܌8vomf&Lpϻnn.lƌr9l­LMw:eH#[o='О7>͋ gL/]S8 yN~ >,$¤RK `.~gppPJg\TyW-}G0jJ:F1333ŏDzCڠLTG8_SƗ5TDDeB A#o@ˇG zߖƗ5H9.i~_1!R`YEˊG>,Ɨ5H_6tɪž!~ʌЩ a¦."Ң)a{?))IT4$$$X%H%~ *[8h|iQ@P07 0:]F|B 0_Z >"=&1edd*!aV(8pVFKGdLTTTJ2&R V(8pVFKGT & F GT & F gN,}Sd05|p<A?a.A0a탒Er宀L(}!mL5>ܦ Xo7\;ip͌"\jjz$A0a+(1F kp0 }]&s!0a=={-\؇Քaln#y& Bҷ'Jӟ ಹ|EAOIpd|ELJ(ul6_C*ҷ?"]5k3[ |` lPjʾu=BC  zؤEtHrPŒ`zjtpF7e(r宠rH'n9"(}; %%O>?ʕ+OHVZݻw~'c׮]0g͚sRVy$O<~pa! ɦN ~߷o2-CBB`&I]ǰYX>~VV,'$$,$ջv+sJ_dddݛ]ٹֿiذxnCRRM&}"}0;,ø#j-Z zܹ:uWYȂ .$m|x<|ɘ1c4Mrrr^^lɾT_𭂂7hJ%%%&K.:t('J_33s!!!zn _z/ρsZ}BZe.m*^U6oU^!aw܁nKڵ#Ye˖ 4mڴM60 D o`zӤI乸[iB'%%d/2E黹?Dss#n΢ۈK{*+VyߪbuB+zMr˗/sKJ_ԯ_?[.,vŊL{X ;B?tF<<ܴ5͛G:u?a9&聖_lfmy3"b5ݺulԨWo2Uua-?dUfS! ΝX}=;oR{]΁p=b{];QvyWd9?-lhŊ*ޝtWc.bnmœp4]W g}&<ӇիW3af͚'<&.f@jx%zr$i#ߎB뇡…%H@cx=n Oнݽ{dsύf4v k떔`!-m'F޽{Sz{ȬK mYΝ,2)hgwEfxw S:bS5''Yw@୕> zlMܡRIPJLʕ gPPPpp0m)SkG<{#0a><|)>yCoӽh&n4>~鯿4Wnaa:ﺰܰa}0i !P ڶm8iw[bnw>}"Kd:9ҷv֭?W>Z-;cej&`ڨѬiѢ)~S|w] b*\=!v-@x0=nnۗ4@?Ļ"G081SW蝱v $xke" M c'G؟Z7ޠ%z|rǐ~߼ydJ0WoX}; ,p~С[7~r)6oNfL! K3th_u[nw$"50m=nWL=:9۵sߏ(^_4ia߷o_-ZSFEEyzzBH?eazx==''o%ԪUٳ_aKرu̬͛ߌMiûu_zi$g]_9.3gH Njܮ]KZ@oW=~ڲe30&LyWdkmº2gEwgx V+c|P(};_jw ` dɒe˖-]tƍϟg7;v*Q{! ML+}+WR9%ׯw~޺uΝ K7.fbH[Tt&z>^=qw<+Z\A._ZO5jԄ ZmxxeT(&|(¨g5VRZ~JH\Cg}_~̭sƽ|RI.Ǩ(Z~JÛz-1ߜչs{l?c!iRd-U?JƎo6aq^z1ߑ믿B$ߑcuG*1l5~t%c҇d2 ڣ>ںuZjOX~WX)'<<&&cB!TAb{?vK׆.Y"S '>]1, O2~t%c/-\QQlC.SYPUAc%|p|=q~EAď[P@ A"A:)T%'Ca.odB!T98w?Al r R IHT(Ǒ>y( aŭ@( T*YKՏA;2w?!'4ynXu+W$s}`߿u֝:u/IլYڴiӤIM65;fd-U?J ?裥/7o~Xpss4hpƍ~m-..vwT(>j8_[` {ᇡ$66~quu*))֭gDDu d-U?Jz?~̙^^^6m_wo߾rȷݼy/;fEcՐ%GT =aj԰aC0{i'WïwX,ojfEcՐ%GT =Z>}:S޲e͛kӧO{xxtL3cՐ%GT =ȋܲXʾ՛W^e#5 }T(>jAaÆ޽{ v}T(>:n]i 71^~ X5GɠfTs9,@?M;A*YKՏA#z2كmJtӑP]Q^fj&1͟ŗpd-U?JH'r+B*YKՏA1GŨNd_ؤEtF6D%ҷWr#ϣ10ij+T(]B3g4 FubBO RXޗJRdP1~@cWRZ~ J0kk̝6m±ctp[)taC6ۥ_l&]n宬K3eJRdPJGo_YNJԪUotS{,Cm*<4Rv^`刔M6Es\nNH$O:+٨*V*YKՏA+aC mr!rs#n΂77ǃ [`srD5>UOk-}~Ad4j4:JRdPʥB/XSf͚5~Z߭[Fxyffܝ;]vZFDfw_-vcRR2y[k6nzM4/_>D,a={vu ɶΝ~ЬUfS7D%m۶طo;Lq?޽SLoCڑV-}Pm>Rz`<$KhӣGsJn<ҩS ,=}% ^̜"-}k!|O-d>O?ѡC+7v g[e}'? GTcOJRdPP| Ƽ#JDƱ7Z5>DIwn[~zNݻ'a(sFƬY/;`ؙ3̊ОL~)ACa\\@QQnm wtPu޺wt^o﷟|a0ϴKHe4͘B:uVoKCv(TLs.qF:Չ0…%9 qR -}3uC?33)zYxXGo%{]:㞞q" )gn"! sQSſ\rH$kQ2(}Y$9]HONred 0>ի[XNkv68L?w>}"&2rU-MUe7:G0kN! 5OF ܰa}rOo4͍MPlsh8 J7H9733GA/>N?OrΉ:L$AKӳ}x* #(:+MVT%җZGLE}0Յ,[ ؾ})L6!ΟO60c]ʥ7`[1k{u9!v3O?NVPQTxUao#w)"b5䭴V}f`ޮ]KRKK#ۊhWe7+WΆ{wI3Ϛ9gG>4Jx ȆO)+ q[9RZ~ J_$IyzqD`K: NNvܻwߏ[7GN U`N /6z5 t,n|^? ϰ#> {͛G- S;=B_b:th+}̀:ipsOW ,lT`u0I05 K1?w4Mrrr7/|hHLrZ`Sµ⑪%җRBYN7o^ Yiû--BCl LZtV8qh.ƍ{R>K鿫m߾̴w5s΃!J}G*-*}tЎcڵi9NZ5=G93r$mƌrK/ر5iɜFN& YI?cᶆ-8Yaaal6*1b_ɚu΃NjT(,H:f|.opOW['EEe/Y*!;]eΆ$*o]D|0O>ϔW L^'1֭cpn>VZ\) 0ϼ}跙S|ߧ/ΟO*)15i>3Z"}/hL ׊G~ J_$L} ; 9_ZјT(,H:} { _ZјT(,H:} |/hL ׊G~ J_H9s]N`T'.bB0]QȚP>V4k#U?J/ 2} l/hL ׊G~ J_KcWtmU}!B0*S ¨\|)ZHՏA˂~RRN)UHHH0RyO&\%~ J@B6BNBfJhҊƔpxGɠeAao0RSSaJuH.WJ|1%\+Q2(}YALXFFF*Ry+u1%\+Q2(}YM***e6aʓW*7,[T CFIL _ZјT(,!uRT(,!uRT(,!uWG"5 }׊G~ J_Ԑ:;cKùR4ET(,!u엒Ezga__)MGB-wD)ZHՏA˂RǮ9ञp!AxL ׊G~ J_Ԑ:@mn#D1)ZHՏA˂R)^s[݆KJFxL ׊G~ J_Ԑ:Q'A\ Sµ⑪%җ5p5#WT6ُi,pǔpxGɠeA =F•8[( 1%\+Q2(}Y$uݼu t!_d/cURqɍ[$WǔpxGɠe:Sxo\q;H106b[ΚKq^cJVT(,L *쏼5un T(,Lû~˦GJ ?JJJ2 &ǔpxGɠeAdĵlz``*fFINN3d_xL ׊G~ J_ĤνM;j3~C HOǔpxGɠeAL3A;10l& <<<)) &"˫V9'{[ 0'<&wXSµ⑪%җ1#{WtyuGsd`r%n#aH#y|%¨9t(_xtT;^)ZHՏA˂#}{6nh=cR(񯿲}|ޅMٳ *5g]ѣjH@O&zk׮%͋ ;vy`ZC/$'L2'믗e1[ _9vfouy0Ăo'nѽCZ0Q%˴rB&(oܸe8>.;@0OMѨQۏs"(8Au k{ҐHN3kk-P/ bRB0r`˷nk!{II_A!fn9[1t3%p?%}#/>c᳏Od)~d:X(,Lo>n'~퉓& '@ڈ-n8?| Dyb2)aB(6m›oX9|~1G۹8 Ѐ<zDtY;^kҷ%(}Y:J}?}͚0`ߏma`}h+иu=r^6®_? 7;ƍ k%(ҥٯ|˫7vZ}I-ΤgΝ +>vإ[?%@CQQ}ii;{JB9V9m9s U:da~}g17k:𥙵(}[җ1#柳z~pw 0l){3!p .pwwW=/*:o`PWoXga-[6ա䣏? !OHglԨAÆ> w9m_9uyq={v}dà$6 #F<n"Cف^}|ޥY^n ױcK9(8TY =pV{̘XN3?gmJ_Ĥa`-f8S3v;we0Œ[xvK۷~p,nìftKv;LEy݃uKJ t$A>oX;'⏂:d0zoAA *HK1k -5P6/ bRF ~Kb߾= y ̯c I–g̪UvF,LV,Okq^Ywy^_S]JLr~CN蠠 @˂ɚru(y\}~J6eAd\8.T s(Юڌҷ (}Y:Kŷ6Mz$``!ջ}MբmJ_ħqZ^ ljs4uhi4@heҗOlpQ-i~ p>~ @BR~l_hPP ӹ N0755G||<({Pnn"5Ɛ̐Ґؐސ/hL ׊G~ J_:D0a0N`fHpV R i̸[ cJV$xxxڵ͛f;v,޻wAP{۶m!Z7n܀X {G6reZ~x۷oߺu+22ygK 6}`ouyw D裏E(4;risI2='d=\˵D #/ jH%P'}⚬,XNHHh۶-Ypww2^z׮] ҏwgkk HєXRsONO^~=))i޼yΝ;3D~FEEدZn] ZE)n. }D #/ jHG 'A{!}kZm-Ο;wN:YybK?88-8Fa:ܮ];:-}ƍd<((W*;vpqq}H*… vl0ڴi)!O٨lWaeA Sڵ5kN6ر@j˖ͤIpR]S\\ *i&LQ.]:tPr,[g\yׂ4h:t Am7oޜ4iG[{RܭINݻv `]Clҧw>ܬD #/ jHDo_YNV-RR7mڄ.gWV뢉̼r˖- kL6m222H+Lo,-7|Cyך?sVfϞQ777Č38/peNĉt2n8%JXX{˖-7oszR+;ࠦOtE c*xWne/D #/ jHBC mՉ o]|Sx-r u ^`~o`^Z~̭7и7tl>11Gs:wnewϞ۷oo kײW.3gX-?8clŃYWOq0'ߨ)E_a6lлwoniEcJV7nd̚ر…#xZʥ_}NO> Kp.>DkQC7EpYv! D>=v^VM ~WnaaO!kmCES}2OD~[ҿ 1.͜.H<ƽ`!#?tqiv/]С} b:th% 瓠tFƤIÅO7+444000,,,>>`0%Axµ⑪%җ5NͲ |c~YN7o^ _iûI;;;jlW'۷2c g|oBV;;uR a}=I36>>|#-›T(,!ujјCޗ/h3|C7;6HxM׊G~ J_Ԑ:5 T Gl›T(,!uj0i{`ބpxGɠeA SԬll›T(,!u@My / ZHՏA˂RG!ϙrFd\ń`Z_`ބpxGɠeA hhcބpxGɠeA (ޏ]uҵKV/Щ JM]@WYOW*7~M^xµ⑪%җ5`{?))I<$$$XW*tSuW_}uРAK.e5=Cؖʍ_jބpxGɠeA @ Tz eJo`J^xG}tWUYza+-cWeݹsC / ZHՏA˂RGLt*pbJ~a0>Yޱc*63[m2/7W6HxM׊G~ J_Ԑ:_TT"6`d 9qprȯovz ߮]d0wVZ@YfiӦI&{m{2e woԁ ^xµ⑪%җ5cRݻm/NӧO={?S0[\\ 'k5h ==U`ބpxGɠeA WO>0/-"osrnf֭gD?ذaC|#k oBV͛+mtQT%\8(}ɐrkF"}m4* f~ ?*U$Uc9{A蜉Cֱ+m(aD:c[W%\8(}ɐRsVcva%7Jn?nMpȖdR`˹U /%@ a*?nӃ׎fN43gfsQQݻwR`\M3jr&/%ӯz|_j {vV^^d*,,}rj6lJ)nE9^C˭$68/1Ҿ/wiFK*=10*3eV7c`0ּoW M V=8(}ĸEWA`S>oGHff5) R%FTǷ6Mzc`G j||5 >(Rv@~ J_z$|/AuqRJ8=10*֯\ HJJ2 &_N'+a1/)v@~ J_z$|/AuȚ,Hz c`T*Jn: ;~FINN3ɾ{D*ȸgv@~ J_z/Aum5:QH==娐^a4ٓ}Y]I푄N[ɤQ2(}YU-؁ӣ qf*M;v'%%d7Y]1fz;Q2(}YU v|Nzի:C|yPCa fShhhLLLfflfM>W2>vmRT(,x\=;pP=zG^]VH|x!oYNK-Z4VEn wRJ8dTȧ'<&7%r\ig_9/JRdPPCvrÚܭ ߻w&Cؕ+U4vWUjn:!I`Ru:]jjh ϼf_Y sQ2(}Y# x챮M6:u;Y0/OӷoOWWɓGkגrv~ iԨ!%=ztbqqrÇ?;4+&ȡC6nӳmݼy1>R^ݻvpZ:*{IZ;w<0'W6{x{`b_,qݺuϒVgIk > ɵm(&2_LsQ2(}Y#?/9yѣ89#:ӧǘ1^8;;1rV,.}fioֿMLSYfYZiRKnRTAvA@%B1C p!9\gygs/Il;!a |WY#f͚.[6*2gϞA0H8A۶*I;wxI_|q>%&ܵk_e4s .`лI{\PW'}TqU3*X UAU/='F|}S///UViWǕqa|)t%U^%a ;Pq2.\پ}Qvv9~o_J\eի+&d> r,z\r[9 r@p_^ngH&U5p~购UAU/=_b֠Aw۫EfDQQKAS=]WErjH˖ׯ_@>䁝/3v[eö~'Lx*C3p]Sʠ  ͛V6R] |z*ϠM᪗N3 DkWvDk63fi5ҏyniO;v6`/;iܸ(wsmr N}'rA"$yꩇ'M Gڦʿ^Z: A zV3(}SSa7mHܲeemfΜXY L7qpLA2N R226M7?p;K#lV~~t?um۶~ΜɌ{I0d]Ǐ$/HQ2}'rAG$7A~Zίj^Z: A(}ˀ7^:J|2۷3<*?8ݻ' M]չsƍ_.|\v~>͚5]do׌1Vwذ{@[THM]ױc{8Q/4Avـi3Hre?QwA;9fPgΗx#e~eӫTk~'"(oPpKG?g/'{aK=qe<^Q<pʳ$@O.탃:V\mGi_urPAO(S/^ZkOpEC,7^:LކP>/!l"oÀtPpKoرmB/yõЍ=)P: #}d[瑡(}Oo <~  b{d:JMK`mKנ-Jx(`t &A>]7^7 z~X)pR:vC+Cg԰-è(?DEU? J8)_10ׁK6tw%][*ϠMcZIeϴK_h ƏT>F'лUAW͖{:Sp00q{xzƏ 쟖/5憂wAo<<7Jvd_%Ǵ.5=mn1(Qqv7+ǴB!Ɨ-**RnR.agPU͖ koڸD$x'~89L$Tom)[P~㥠y|>NJJv8PNm~[ U? Jx+}XΕ<PGc^v߇=+W][*ϠMґQF,ⵥaD8f*rlmP>&'a eUn)xzV3(}St 233aoaaGFF =]`<ת&2b€"Rqe*< z~X)p[:hKJJ`+~aOOO+tv-~f{!2b€"Rqe*< z~X)p^:Dq =]`lfj{&u@@@1Ȯ"q:}j9v!Co<<7*!y0S!^c fj Z[hPBo<<7JL՛g۪ǡSY@)z~X)P:g'fUT}e̛T6Y;>SV3(}StTr\e$O;>SV3(}StΔRuꯏ%wU? JD(k3eǎK^~R?5%\}agP BX)d? .ͻ?~LKU? JD(k3er2KU? JD(k3e\->x @)z~X)P:g8˹6oS^%gުVyxo "52/x @)z~X)P:g x.n`}agP BX)&|u;3Eo<<7JL1D~'3A)z~X)P:g n.agP BX)&2һUA雂c pHުVyxo "5b.# z~X)P:˅2vN9.agP Bx.G6S) OުVyxo "Rq|K'vTu VpCӻUA雂~U[}y}7?gbP.agP Bx4 ${M{~gS][*ϠMAt6{;H I>P.agP Bx:߮tG`I}7< z~X)P:N+ҷ5; _­7n(xzV3(}St,G^%or%\#][*ϠMAұ 5N%\#][*ϠMAұFL%|D݀7< z~X)P:fPq9ṕ3vɼx9dN_.agP Brťcsz-S[HP7KSↂwAo<<7J8/X+%u *^6xeQۢ89lW૶ɼ^{]< (P.agP B#;W)c;6WV + p &vp˗՗CpCӻUA雂c>k?81TB}y/V6,//x U? JD(zsf!0CրƷ+fv>̳wAo<<7J~\:{.C[8_K+>#o\(uówAo<<7J~佸֨$0~طfYwCӻUA雂S5?kw$">ʉ_AwCӻUA雂Sx#Hϴ qۆOIMM-***/sCӻUA雂SR>Za(#$)--; U? JD(ׁKPƷ'bLf OުVyxo "N]2Fo $(***556eeen(xzV3(}StJpMjNlO?ݠӨnFÅ {mIDDD|||vvvIIjr U? JD(Bɉ I:-ܰtK;:KT-y" 7< z~X)P:ubҿ׭3oO/ C}59 OުVyxo "N])ٳ>ڵ8qԯ搃]wmժc ֭k{ys#:A?K5jءo>=~iW:ptusGcbbp!9 S][*ϠMAҩ+:hsiiIƍ.!߿? ڤIc׮wԩ><~~=nذ@GN;|8s2/".]w5mg@`}.aCUL_VT@;U? JD(S$`׿<񩗗4̸:~ض vsr'OpdU,\>\3{f}z/ M406 IZ۪e0kmڵ7_,DFV RzV3(}Stχkކ!a8~|'QZ~zV3(}St0wk BgQ[lk66tǏKTBުVyxo " &qp'x'џ?Sslm{`Hs;zV3(}StPRRgH oi3л;NNAj[?W ŋW3P.agP BEEE¤;kek<ᓿ \}؇'2$EqRA+ôM<ƃon(yULjlƗ؁Wm~[ U? JD(#ֵpdgg'%%EGLAhEl%݃ }  \JoR.agP BD}ՂC7}bu%/Q VHVgbk>̔m ч U? JD(Oti6zf([oP.9اn(}S`UdE|ݣP.:0|n(}S`U*<H| n(xzV{| 7V%M\J n(xzV?v'<Vo LJq[37} n(xzVwɋs/V7[)0)A dyfjW H]pCӻ j^`b8>Mx "n  . R7#qCӻ81z_pD}%@雂D̆?|7 UfJO蓈m34+eIo K1IgL^Q{qCӻJK KO5oɗsQ`S_m9~pCӻJK5ؼ#Z\),A<\_' nތr=ͅ|9o FJ1 OJ ƯkxM˹(}S0RI1~_qx?g OJ'+e5|U8nsQ`3o:yhĶĵPvCӻR8x`[mh˹(}SЖrk>ls(`-D][)[yTx:(}SpZ:U~'>lSnz\yΣ;YrKӻUA雂1> āN߇HfXap45F* Ii$uM[*ϠMALwӪNߩ}0j hagP Bx$c;is Ct/tfhq1M:a1`7oUA雂)W?rĔPIҲ?77ɽ i6Ԯ.)z~X)P:~̺y_=-"""$$$222)))77\U)d z U? JD(bۄT}J_Ͳ>r1)z~X)P:EAAAؙ:4>>QJkުVyxo "gp8^ƗAZ{)z~X)P:ZB_SV3(}St< 0uG }M[*ϠMA8Gs}M[*ϠMAD\ygkުVyxo "~bsQQQh|n)z~X)P:1|x==OkުVyxo "G#{?c]8Taaah|)z~X)P:~^D"Tbccv;|MMMEs}M[*ϠMAұtGBT͙577!5Eo<<7JȁRJqAQQCkުVyxo "5 3UU!5Eo<<7JLy UA雂c p<L[*ϠMAұ8S}agP BX0SLQJ*M٣N2Zq JD(k@T)XX8Vyxo "58 a0x#)/jű3(}StX8Vyxo "*ϠMAA~`X)P:V<<7JAՊcgP B ?Zq JD(V+UA雂 jű3(}StX8Vyxo .0S$Wx 'ɚUAgwtA<VfgP)I߻XɚUA#B#"V*ϠaydgPЈE<3(}DhDX"jY>"4",r`5 JGVyxDž2v+[OA;UA#qdcUn@럧"+Y3(}D8*NǶvj.O Va%kVyxHYF}yQəX}* 5<"( Gi2ڼb[M IJ֬ JCH&KY>"(M;}o6C%\υY>".g'_(Ѱ5<"~s+Y3(}D G{.d*ϠQcyt9WVfgP7~4bS?hmM+Y3(}z69\|u惭Y>bqg|Z>%/?8yT|J֬ J2FOx?qSm!ĵV QJI)M٣:r KqB&,wӪNߩ}0hu>x6$c;is Ct/tfhD11M:AW=`? u>xXmae|b;}ˀGPPP?vN+<y'Z>XÑeZ5͇ BJA,ZGh| ց}4>RG?JAkCEEE D"|x==_dPbMdg EpExxxXX|>_XPbY"a'/ 66nT4ʀ ȳoLOOh|aA#)ޑK)EEEh|aA#!(%\_XPbqDX~D>"4",rD?"J{ k9Chz81^^ժE7S@#8]fC"/$9phU>Xѳ"83iTD$PbMNeH^W*9k$6o?8> >XΣay4_m^~$D0PbY-7+&K@#e8]noZͯ{Ŷ/"(}2g'_(Tڜ*<H| B#k ,򤞏!A#Q["3vU@^p D qLY+}# 1~'2$Qo+U* UA#g@?k~ee:B[>xn6> Z >U1> Ou^z[o߾ݻw#ccco͛ʻ W>pU4> A 4}pm۶6m:{iFA8 rՍO•srrvrrr׮] __˗/6j觟~R<&*GNNZxpuS֭[m6۵^K¡CKKK ʠGm|;׷kFAmPNzߩϟ?ߢE;wfC Q=4G0bJvzPT_Aڔnݺ]ǧ]v]tcC6(} a={vAZ*9ح[[naqܼyIaiNCW?~'N"Gi|v|}ŋ_j",wvz_sZnwow%49}3Qx?" ;rۃA$yw*;GN(NȄոk=~ڕv֬ 4p!gǎ}}[]WxML<\豵Òz0X JA8⏝g]\Ł-!!ooܸ[UӃ/hѢYrjqh(7z}Ą%kwx81dgxC` Ri_mPi7tݥKBCî 7@gd;„ƍmڴ:=UG*IRZZZAAAII'[r;0xjK4}u]||3=.\l-Zj3vz0"_V;W:Mi!!!III(}-ܺہ1x&̃=mּ$`|qBNynr/~:A{IɎO?MaүnG<3wg(ޡ[CPp8΃Ɨ0H nv` A#hnv` A#h nv` A#U>ܺہ1x*Wh|pVn>hw(N 6heqrUq. ܺہ1x6Z#֭!(}Qz?aQ VD4l^ D%8y&Rz ,,,** _Wu+cJJ罹v}xEuCm@r:u[r;0 {?777==v߱ڰ-~ ɡ CnG~QQW_}.LA"IP;4~]֭!(}R|%%%cGmE$"ϏL$"gΜsΝ;_wui6mZ.]ڴiCwh­[CPШ9[oѡC,Ѻu#G9skРΟ?K:Dܺہ1j 6͍HBBBӦM'ЪU-[TTTx㍽{<nGFNzjvÆ -Zغukj ǎgϞ}7 wDGFak߸qf͚٫j裏*++_~ѣG ?;"^vNv` A#B]`N:M۱c:lm۶G{ޱc|en>"4E^^^޺u뜜YU/--||ԩSvnh/;'p;0PZ&.U߹o3^vNv` A#qdcUn|*nv` A#Qq8; ֭!(}DDOivmylPla4ym3N}bܺہ1Jk`yoi~TaXI. '![CPT.5O_mC%\[CP|Wj(Nu+cJs`>/nGfK V7 f­[CPH)}GG`;WYs9ɫ[CPH=ɟD$`Fԓ+ܺہ1bsOdHa_My{u+cJ3WVc^sVn>R7<Ǿm9/u+cJh|cߌy0G)Ozذaׯ'zyݻgƍ_ѣG۶mv|_pVn>4+oVG:t萕7Zn}3g|w 48penGtpaÆ7|sUͧ7mtr ZڲeKEEō7ػwhu[r;0A{b8~@@ԩSW^ nذE[nMرcpٳo6pBU%[r;0Z@{nh[ƍ7k  ۏ>~_=zA] ֭!(}F]r^ kL ܨܗPyީSɓ'S۱c:lm۶G{ޱcX­[CPKj|.h[ػ762#6[nh旖^|Y>r)E;{u+cJqg-'_7|6˽zu=t(NDnѩmrӖ-+og>kVɒ>;NO<׮߷o;ƾNu֑ `%pZ>=6lX@Q=RîN)%>G_w[񎜓w%ɻ WI8.ݜխ!(} PՇ2/:l=p}}[EgHiN_t`99mGL!o{ݗ}|@\"r}6ϟw߬Q c_!g|]2 ϴPP۩rN#Iv=F±í[CPsg'ն zא7J7M8=*j %n]5jXVKNUn]5kJޑOI]SWiGoペ6m2yVZļ}NڄN#Fԍwb’ϏLlaS^رVn&Ҿ E]YV_{m;[`oorfB=!Τ_ݓ\~oׯ_ТE38-%e-ѣGtu*N]1~)$Mxv` A#4j~XmڴʿMtlVQQ m7~%̵cLJ]`ʐ!w)3;m%Wzҥ};wy(w*D_XиqMqH\J{ܴiwԩpuwҺun:*[u&x׷Uǎ>RW9BmBm!6(}=p;0Z(((H7:ɓ#Q=~|LFܹ=,/ ?fw7/``%%;ȯ$2l#^:mP\Z>Cہ1ԂΎ{t+ 6fSu¸g0 n>R `hjEE8aj*f4$CPH-x>%6~ 3`!(}vn0~ 3`!(}D}=ƯbaFLہ1EsOdHI,N6yVq{_Œ3cJZcpn1~ 3`!(}n(+"^[6/"|aGNmx})q=eXxv` A#uFT0~b"xm3Yy1U,hRO+PLfff=#Xۃ6< _Œ3cJ?rKII XE$"ϏL$vm7pCΝ}r|ݺu]w]fu?0 00#0/&g0 n>"4E^YY٠}pm۶6m:{iFa܌3cJ'ܵkWrח|n5jO?)rq3`!(}DhJnJoٮZr¡CKKKH "h"4N_픔nݺk׮i׮].]233廋q3`!(}Dh/~ĉBxv` A#B#"gfphU+h qq3`!(}DP76>;Y,OBa܌3cJ1Mm3 _Չq3`!(}D\\K1nFLہ1˩{f?C~xv` A#BAXI=Ǩ7 &Gps腊{sM3h2̩U0=8@ʼn_Tծy %{{ '3!{*ҚZ[-3J_$*+wY Wq Ij? x9V#8U J){">.u 9fΗ> Qz81 X[iƜހЪ6 3>b)r k{KC ?G{)55577׌ ֖FbhUWJ]Ge?vc`b'$)--6]A=_?{pK3 JΞjvFu/LǎۼIؼl xA#֡PTZBGEEf (sK@VVXLXOmpO+\,[6O7?q|gTKK#""㳳KJJx1J Gv[naҗƵ׶7a TKؼp OQQQXxaxxSA#ցF<=׭OnD󗄅tA X GϞmi׮ĉ~5,(oV-{lXne:.~p$xcOenٲiSD;} !ic e{w:XfСFxT| 5O? aƅyC?cs 23?pg/6h`۶]]4ktٲM_VNܹ+L⋈KaP2O_gHțpp׮M~GL8!0p}~n:u矇w}< '>uS5%(v3Z?oE} LX='ʸ|S///%iqu\}uy`?bIХ/`?}0M7](:XUrv5&{ %0(OWߪH)Z!%$ KViהKh&0#3JzbŬA۷W͈zd:  ͛TR۔Z[l~vƅ) ):AF{^r[+}=Sy&ىӃWꁫ%?:VU׮.d>E} LXZgH׮M Ç9$rwu\"h詧4) m8wnόaiŗ_Iӝw4nNPJnj;!9z2ULuQs*UB]GIiХo܌3cJ_j>i&G-+۷o3sʚ'`?eo# "wWUqHr j:}3%QtU_/]*yno=PidΤnHIY{6q2F>m<244o=PԢ+xiTkk4zKQ|V5@qky|>NJJ"RQnXxv` A 5IHa/|},ZBZҶ=+ǿ"M>nlEn&=pJ•Xxv` A鋋}>---66'w 8>P YI2bpe*f4$CPBC_TTTPP x.oafi"^[ ""aok[^ǢLZ>`܌3cJSYc. ]է"xv` A#iT}WS>> qq3`!(}DPYjk_-'20XR8øg0 n>"(MH/P| W'h". l7׌pc܌3cJva'n@\c܌3cJg?D%oJuf4$CPd |W kxv` A#?]X]͈ xv` A#B?k~ee>|EǸg0 n>""J ϘH 3`!(}D8WzۄyOg0 n>"~sxv` A#A7>z_'h pV_qpVn>begIv9/̅ s̓14L3-`'ԗ[7ܺہ1X#'Ə1%44TObΉi1$Ku½nGOO/[RRrT}I^~&3֭!(}DMW'Ƈ Vo>=qhonG ~LW\Zyz(O?rí[CP8-c}m>dRu+cJ0nv` A#Bh|[CP(нw'ܺہ1+ nv` A#b~5碢[r;0Pz?_73DOtwܺہ1uȣ G nv` A#B"4dd+@ nv` A#B#"n/;cJ9p{ٹCPp(Y݀scJ#UZpZ<1nGty0XTZ\ЪnGD䳑3amˋXv{#Iէ"&[CP*|pap466o?8>1nG%Өj --}\}bܺہ17KBm '$u+cJ&Wf(6u+cJF O;֭!(}D\NeWo/nG&pXI=Ǩ3֭!(}Dhr]8+gh Q׀=>\?PO G*IRZZZ'1cĴ%@wK |R i!!!III%%%JOggo2ڎ >XmaedO\oPb }5w R\ˀGp8^ƗAZ@#ES>_pPbԵz:нGPb1\yPb5O>F=\TT_pPbAޏ,ݣE D~ƺpX WW x?/"ybccv;|MMME JA 8ݑX9ȿRPTT>XȋR GEGz@#B#"G#B=a#PД$E^,j(\%A=Q@#BC92Յb!PМpoP PPB(}A@#JAD P G> @A "(}A@#JAD P G> @A "(}A@#JAD P GO15IENDB`kea-2.0.2/doc/sphinx/uml/select4.uml0000644000175000017500000000536714206773363014157 00000000000000@startuml title DHCPv4 subnet selection (Kea 1.8.0) agent "Entry point" as entry agent "Try RAI link select" as rai_link_select agent "Try subnet selection option" as subnet_select rectangle "Relayed" as relayed { agent "Relay address matches subnet" as relay_subnet agent "Relay address matches shared network" as relay_network agent "Check client class" as relay_class } rectangle "Set address for lookup" as set_address { agent "Set relay address" as relay_address agent "Set client address" as client_address agent "Set source address" as source_address } rectangle "Try incoming interface" as interface { agent "Interface matches subnet" as interface_subnet agent "Interface matches shared network" as interface_network agent "Check client class" as interface_class agent "Set interface address" as interface_address } rectangle "Try address" as address { agent "Check subnet prefix" as inRange agent "Check client class" as address_class } agent "Found a subnet" as found agent "Found no subnet" as not_found agent "Callout subnet4_select" as subnet4_select agent "Return a subnet" as success agent "Return no subnet" as no_subnet agent "Drop query" as drop entry --> rai_link_select rai_link_select --> subnet_select subnet_select --> relayed : relayed subnet_select --> set_address : not relayed relayed --> relay_subnet relay_subnet --> relay_network : no match relay_subnet --> relay_class : match relay_network --> set_address : no match relay_network --> relay_class : match relay_class ---> found : compatible relay_class --> set_address : not compatible set_address --> relay_address : try the relay address relay_address -r-> client_address : has no relay address client_address -r-> source_address : has no client address source_address --> interface_subnet : no suitable address relay_address --> inRange : has a relay address client_address --> inRange : has a client address source_address --> inRange : has source address and use unicast interface_subnet ---> not_found : no interface interface_subnet --> interface_class : match interface_subnet --> interface_network : no match interface_network --> interface_class : march interface_network ---> not_found : no match interface_class ---> found : compatible interface_class ---> interface_address : not compatible interface_address --> inRange : has an interface address interface_address ---> not_found : no interface address inRange ---> address_class : match inRange ---> not_found : no match address_class ---> found : compatible address_class ---> not_found : not compatible found --> subnet4_select not_found --> subnet4_select found -[hidden]> not_found subnet4_select --> success : CONTINUE and subnet set subnet4_select --> no_subnet : SKIP or subnet not set subnet4_select --> drop : DROP @enduml kea-2.0.2/doc/sphinx/uml/request4-lease.png0000644000175000017500000104000114206773363015427 00000000000000PNG  IHDR ^[Sa)tEXtcopyleftGenerated by http://plantuml.com09iTXtplantumlxWKo8  zJ4]ds:iM^ Fm2(AáCpz]Qf-P:V0J+ڰ/?oNpvrt~t|P|)eo>IU7Wl m) \,`@6lDd%̈kQYQW%̖[Eɢ W@xC0M۫k#1Јr' !eS襰sSVV%^nف3DPe֕Mbco.$z1wQ;ABxZ |[4%^<楷'M2D8<e[ O[ 㝘£Kj`Eyt73/,˹*$&yZKCv(B&jd&%$o(6m/~oPnw6D Cx@AHP+fOS6Zu); ѬLE D 4~Vԕ6t*! ioI԰Dѱ iK1FB\N<]SԮ JY۹T:)^YS èA^Br$S++NAu)ik t:{:2(CE: be} މ`ݰ4H\uS5X'(G !HWte/8U%MVg< k n.\Vx:  tԇNw~5+-/h$%AtҘ5a2Q%ȼsqhm |y.͉|yS{gh_4H.ٸbH}u q$,:NV@Fk{(wMChIQFbF{ 9MKP\;xsvg4w =tmr #F>.[VV%kF5+3u6Փa+uij;}gװD==ei%/~IDATx^TGKJ55_%nbh%[4E1XF "a."7,HMכu˵ ߙ;]44T_~]U,Zh˖-2-AAA|ɓoVe;T;la!!!v)XVΝ;bwϟ?/ƕ)%jSN_vy t)6{aD7|u֤g;IN믿RݦEċy1cƬYou}}wk֬ 4ϝ;W9@@dkk&Nj"4h@w?^ڂ i3w2d96֫W/ŦX|ܹF/Z(wܪnc&iXp3f͍[VZe8@b~lٲ)[g.64t"7NO0AY)TZUˤ.^XT)Uӽ{DS`rW/_0ɓ+>v:NѣGj2ȑc׮]MԈ-h#k.sU˼˗tRuCjJ{!z>{666O>U2A_ڵkWX1wܾ}Pٲe/^ $%bDM֬Y// oo lR$=aÆb̙ӢE y999)[aÆۋUkk'N;d8o/ͻx .?~ >oF[n'Orʕ+[n-vڰa!xС"RJƕ\]]ED[^Կۼn˖-+QD׮]),%.9`7n={Վ;5j$.\8g ~9::fɒh LJח*UFm۶M[b7@=zP74;WuL\]pA&5jd&,(uAjkk|dW_~\_EM޽[ŁS*77S9r?|Ea,oiK~,L:繸HGGGubŊ5&TթS'_?fβ{ڵk?/3?P~zQӷo_z51u102{t͛b]v[CXbl>nsKFVZ9 .\f[n͘1C ggg'Or+V(S`ɦݻ(Q"wM413Ch2>:3å;w/_>qUVUTL*nnn9!9Qϼ;wkƍfͪđhڿ0U~K>cNiReɓ'mjW|rԞH9sT<+淞z5}7x+fPpnݺuСL2...+1ذJ*#Ɗu4]dOJ~hjՒʓ'ldfM3?ΝӧSN/]dI*M41YdSqԩ$ f^Kճf͚+W.1uL GO?d:L*PX5i( VvtϾj*[Gm0I~5J3#\} ZΝ[LMP[7n*WǏ[d,5֭[riqVPA~Lcǎ)++U$* /"V'M`2,Yʼnʕ+t3f緆.Vw(k S,V?㇇n߾m/\N p['N=ˋh2?1ͣ35ο rJd[cŊSV8XQox&Ƙ|_~IŠ)y=o=x@h[+2XzԩS|.{]gΜ1ϟ?iӦu꒓C"Xa4AAAѣGI/KO W\yg,oUPA)wUV9s̗/fd,5h"9BrwL)Y3|Mܻw\O'`2YӍ7Neʔ1t.Y9sdu lRxG3UhQQy٘d͊M6UƘ˜^h8Ug5k</mV3/qƯS2ߒ/"YX޼y+>O~K蒉R *X8"={+V([LbrիW;|Rttlfy~Ks,)<<|E( /S<;wnѕ2 RkJ/t:x (Y 裏f*Glbmm*5q"۷o++7o.*ݹst *c#LG˗9;;Jo>y3]~"+(\ %JAɟ? + Ξ=_;88TX1{:-Co*~!Ƙ!)Βt~}Q)jڶm'-V8'pouQa>Sa c+'4iݻwU5jԘ>}#s)+k׮ܷ[nS0F~ ğӭ[7c&K~PHQ4_Xl2++o~Μ9ժUәo&?Q>Yj-a3_ abP1_ 6m*6Q""*K, qپ}!@Cɓ֭[+V٫خ]4%!iPs8;عsgԡCYc[M$ f[k׮%pK(L[jj̼VҳI(Q='S{"GiFݐ$*u3JՔ1z>W\|\5?uɟS[ߪW}j)޴f[MgU[...2 K2o]t)K,Tu떧gQKE*:5x{"ݹs'gΜVVVk׮U֋UQikk#ko.*۶m+W͌z*QLnٲ2E[UVj-ԩSr=ZE3Sgy-70w]rbϰ?Γ'75*T06m.[kr5YjP@-d.22buCR̙3ESժU$k׮eϞZԈ#Gڞ9sF.\XASU&M}k͚54iЩS'D]V|^F-Z`nݺ}r,c0::ÙyM6?^fM4|pK5Jq_(UlA 8u֥K%'~LbX[Œ3GǍbŊ^t+vUdQKŸW~I&۔;t!tRߣGe˖U+5O3gΦMO7o^&3cyzz oժռy/13ؽ{roݼyS!dn*F⼒5O'g&1ᓓn+WV-k5ޤIdɒuU2D꒟suug8ŋ߻wO  sDEE62*Ut .ݻ'3gΜ?u4jU8{l5kAt{ICpƍ˜I޽[|ҥ|믿ٲrʠv ,FJCw>n869ںwʛf5)b/7?ǢiѢE|թSo߾s/BY)XBԋWV>|bŊn 8::g]Of)SDeܹT;lѣG gȑ#U*>|P2@ǧ̌,YpV buÆ ̦MDk- Zw.#W(j͛K>'k̜NRM2gVgggYL///e/IL؁$11Q2WK߸q*fX B~ xy?>{lXXᙈs΅/_,'O.\tӥK -1q8D gsG-r)"kΝ[ni,$$dϞ=&Lȑ#N˕+WWZeH$% Mϟ?JH'Nصk/N옝]ٲe_tO̞NB׋Ql>|888Xl8{wuejWj2[ܹ3t|_(>P%q}*z=q%SK `a~r2Jb[gDXVjM*U8L5kVjoŅ= j~_~n4ރ=wA3u܊6]ȧ|0͵_?4(Lũk4K:%z7.5[jm ).-[@ëk߽{+岫j>,X0ߘ1>h%Z kgWhԁYjbbNNe%llK(td'pQQ^uϟgĈn\E۷ ELm<==Iq>^%L ڸ/wa?Ldڵ ɚ5q~+(hur%U-7=d}*e7i e'4ӳ\Et;jԨFd!NdΖ~c&ozoҟo=fyر∦Oo|H?˾}ⓓI ϣ`?8G[zk޼\bccMquJ+sT"{$ϞR qcه%K~'[+[ELUݽ xso }r>Y|%!N/;vlX;w7oyx,a#GvWk!+\JYQrYT xn-o9*k͚ ܪUL$YCjE#F4߉bx~k\~ǀ=&l=\4uZo[{w<ڭ穱_^Z4 `_;\T ˲ag'q0LRҺ~X(2rt"9j,U|tkk }B{yݰp,/bW^mx-CCC xso ,y?fbiϞby [i]MOU9 ڵҥeYV㑲o9.Χq㚅 OTDٰaVj tbVxxx``=hۿj薏zlzK~ܽg]xuqIߗ|~kz3$X0Q\];%;)Ϝ+baժ}=kŊVbx w"Oj*Booo19oED˚5Mw춵1oݽʪjrz!֭UVz*Ş%g_?{Zb[X?xpT'fX>s挧Nú>ֵ԰SϜ:uhKgJ++?4(OtU-[ݽsdh_>7n 7L޽R2BCו*3D|_}7А-Q4(ι?|ݘ>}XݹsaD1)|ZwXмy]*rN?s6\7qy_Ԩ{⸬t d߲/2thM0wCΜYѣ=JkkklYKU,']xUvEc˗ťSba]*U>W+p,/b66lؠ|EyN,oիWJMM24m zJ;B&o\˗= E ;ڹs3kkk3g~jfc}~jkk#dm֬qj7wV^^88R mI=z ҹ]ƢuѢ񉉾*,Htbիeː2"R3%Ç#q7ok$J~ʕ+h\/|+6vfAAx]2UoΑ,ozIH8u掸8&Uy丿Nod\bbN^M׸T i\*=~%&ûwX؉fy֞={ݷmyfg֭[v5k^ԗk)}#9Mgf -9[@C(择VLLLxxxhhh``Uϟ9s) o>w!!_7ɲpmH!@:G~ h R_^Wj2V!@:G~ h KL12o) -9[@P^c~ ",Ho CID~KYoΑoB~M[s[顤q~,Ho CI$ĈWސ "4J%[۶msrrZի<s"fF=Y!4J%[gΜ:99 fC̉d7 [rKWf/?[booo1ֶm\271bl93,d~JZ(gΜ y!ĎzC~ h {rW Oy)߭s췾sv4u@f*$/ &d]~kFldY}*&&&<<<44400Pu5s3 Ă13o- ƴ_ m͸7 3tfSWTo: 'osrYZ7l\͆\0@)5=8mm?bCޏ]kb0c׬]uح=Kת([ ʯ1]9k5{lk~xO|EԅGFR]O\qyu3| @ȓkwFZ_u=OPGw;vԖBN5'Mul>pq[~c>qW࿧,ZGڝ33.^716Nl▃[|Z_##@~ A;mޤțg-r+`a=-?.\O~?%DŨ#cn;=d;-M\=u@D~ $2is~3^V7M|yyw}B: 3!@(|gwwhc\x:IBt;rۻm"*0T9 3zx>opH;-k?Iz:F~ L$>2R={åbB#q$K݌E~7w2)C~ YbLuzqaO.teս|+>PG-ovzr_]4?n B=zfo}|-9w+o'7#9><篝:MO-QwClkV vړ@tNp8f ^gcj;7@RRg^Z}n-!*Fq yt;JS׍e OA;m7jKf~: # @zD);C f %O=eoUGc0#.teսB+:=PG/BpgCͿr-ô'#%[d|/ 9gI}yr؅nEr~g}|: =!@sslӋ3WG^ظz4xsp_> = z\ŽGڎ qLRMP3N?> Ip`wZh`v@ ]xz޹iy!=eA?m)pN-$}bbcG|Zyo#!>śvW\˿}H+x3?8cssXu.=8W6w8cjfG~ |x[ 8 eu3>2o{λ*whc\x: Ր =2ٽB]>eg<:uI oz_8mJ1?uѣGϟWZԆSCTTkׂm/NV~t?l-i:ux]?DFҧ Mէ#kG~ Ԓ42=5{(kcB#2Kt:^nH Mտv.\hѢX6l9)iΜ9nnnZӾ+WWWuDy1[ 56;Wc ^7׎o;-lL.ϤԿN&SךҷMmh)[YLߗ_رcյSNב#Gg~ڵCBQwCO[DZ+1&VRLב%I~G[ۥQwCܧu[Uif×njCSf,7k֬?&&&}yKC~o5}B]C[|ZQF\ PGW!,ZNK::z!:h"ky3EnEr~g}|:H۶mM6j(;;U߿_߹s}u100!4iR׮]su[n9::fϞ7M6͑#G W\9yA .1oܸ\dǎ 6̚5k%\"jƌ-[wIJ~B#QJÍbŊM:U3y{O,!;;SH߿?,,LT޼yrʭZJ3gܴiӅ 4hФI3O`*4BRIB4rUǒQ4cT%jݻ'z^n*SG>Xs? IN:R,8y̜x\G*/#*(l{;_Zoذa\\X1cF9ĂN[tYdXQnm-'D̙3?"N:%VX!c/"Ӽ/ى/^x͚5e|QfF79a„>>buƍ$rҥKWzfӦMӧeL5;IJ]\9ԦM}NLyYsC3+-^Xngg'6Mmh>ɂH2qGQcMŨˋM?&J_c_k{ǕQA#&\JJ'N1sq`J#@!Eb_rvW~}'QyW^f_rX4l%)[,_\p9`-2.Vĉe}ݕϝ:m\r?^ԭ[gϞrYsC3;$gϖMmh>ɂT rUR՛QNp]ѳũ< Jw}'5,Ut ˖-ۣG* D]}˞=wpi͘$kK)5bj1cߗwppy>}8˛T}Sy*...EyaKYo*F9]b6l8rHMO?NtzO٤9ɚ%?q'Oב٣ЬT:[=>{ԐlROHHȃԵf)kbcc'o73ǢRYFC QLmhޘhfԩS }qZzu˖-SG\^J`C\u4ϥbc'בQhV*qR-'1&ֺ]n߯ UGdXٳU`ɒ%q\Ӟ9?>nNC\G/ڑ ɵ;~u-pwܩS[u-qĄ<4k͎y}k|d:"q~d^;[rV؅7-zg7Fıcl;g@@~ E {겭[y4piObl:@:zmkcF~ $?q1[ 5K>!#[p-xo#[ S }||f\F#dX;-~2$eb\:]orGNt9ɾ?n9$DZ뾿^m9?myT`:dXo+g^:CuL)uߡ?o)gcvy&ѐdxO=ǵ㑶v'.2j Oo,sSR]GqdTqxbMZ4:<ݟWd<]C6z$ū#'Wս|+> WGt '$n;rHŽgq:^P'zLݜ״'V7t  mW;c b]rvW~O =!H;y-N 諸^>A0eu3Ho#חlT}řޏbkBT:Qs^=t@r?~[ 56; Oqwn;?\؇xC=ukݮ>F~ =:}g->ᾇ}B]Ç[p-7#VGB~ f$D\}_># ]:;77OWGTF~  9>1Q^b\|CZ[:[Wrsv JףZi}ud@Wnfk[zH0!f Xn?7Wuțg-r+Paw6]^>1_`uN{_Qu--Kzsѵ wnkgFuu˖B͎WƅGn)T-U7ț[ 81Wݐ,ni˷h~[v%Ī#ୖsm{k^݅LGZR)/*E؇[ސ^~gcj;lvןk>~RXqTZWUM [^֩?*?8sR7%KyrjOV~5 V6U vzo,qaO?%J4;QNmGO ٵHs Wj]̘ƛP2\I23[2;)! ?J4; LI?% Nxo\~ >LIry/rڵkAAAZJjW;`9N`Jz(iv@fF~ @"o}tOSҦ?* d̙榮M\#(wrP̌̅[i_m> =<<^aL˜ ;v=qPRqSCI23[2n}}O̙3ƴ*,4nݺ{Vצ JjW;`9N`Jz(iv@fF~ @­ϴ/6ߚӝmaL&F^z \]]B͚52Fs^%5+'0%=4; 3# sgڗWgoo߰aø8MkjFnid˖u^W3iJՒf,9k֬`#okkS\IPn*ƛhy]ΨR24[2Kn}:e͚%w+WNuQNeHeY껉4OES;qثn7!|jvW!Gm]#eʼ'jqbɝ we%3cLS4;` ,ɓKGV-{R4+o\M?|6! s租~Xfd((kڷoRƑ*UǤlfUKnͷ ҡCr6w\Ud5by~K$1TD53UaҬLX y4S4.iwX?xpqљU^'HX[j`lCUfMTVƕJx3$䀕C-&Y T`M~oΝ3k,+ѣj~wjqg7~|S緡cG\r~jfDn[?dHgàv&v& `ʕZ/_E N:PMĜ3VԨQ~Ϟņnej>$ M ҧC6RQ:r7>=bnbLl׈>1ى(uVX<֭͌hرeaھ}5+: eӦ_tqv떻+o֦w˗[ڇVVV^^k ɓ+8Ir^D 0q/S+K?wvUNed3njrP˯83SJv{EUS_￙=QY2&'LU*iw ?O?fʙ~ڋ=[yX`ݿ~ի2e-K>ׯIMrJהfz$o>>NyPF5"#= ۚ Vu{e˦`,Yl 8etiirUÆ|3bǎVVV"fkkH,߽Gt=Aeum ^#z [Ŋ}{\Q|]hAqZvBd͚θ,^qTTozٻƏe(SPD(/*۲E("TTEEDD){tJ--P(@i)t@n<yд6Dgu| @ON͎NPJŸ$0]ISnͩKȘ9R?`oqRuVCfuӦ/}/LկG|AY&Z6mءCQ֭}͕>-_a11Ȝ9ES_:&a?Ƃkּ5m<]# xV tL|~~EktnW*I9 *_QX{뢋k~;95gE~ӯ9nժUMK 8p`8`~-_1@5r/fLohZC_yozKdbqMZwC3XiEgdo֯;vXժUō}~oٲϕn7J-yH`s|J*;{AeݻwjO>y>32ߞ=KJyvZ5+R.Z֚m̎|娕VǎmQ_Ӭ=~r[:2?qWrիtW-ܪU'l'Q99-ի|5k7y{9藲/ cKIۣMkWN-[6usE}@h^fn=_%h2sG|h+M9'6ms*%vZd,gp8bD_eo_$a÷b-Ç6̧@[yǪlpX-Qo7P~b_XG`j6>VaUQlsSvw?JR|*rX<-@ u-UWf^Tի~PK~p]+_;;;PAqq{lfINUFSk2s~|bkD!Gl8~|.RHNK GTTgaZr0%!޲(h^l=_%h2X nf`yX'yh?/9Skd;V ,^)2QT| 8@U1F~jB oq sk>u_: ?gzy`UJ^lv##efG'$ŹD<5gű C3VCms.7}{MGW]Z'/,h8@A.}Ve>u5B)b02ȷP\<v~tbҐ1s?7=ZZy;R4O[|z՞Ró2sAҧ ԅ<Plv##efG']}aS}ǮuyǣՐպl]1.G>/:Dp5Dvt&] ie>u5B)b02ȷ`UZmvtBʁ[}/||ƒCo}cζ/o}m_̧.F(QlvTb +8}eb1,C7Wfw=2¯G!c Kƹ_=t*'< Pp|kRf`Je(6;o8+)1n͑8yOQ;Rٝm2fZ鑸/$\ "ҧ ԅ<Plv##p ZmŸDЋt׿y}of=v+/bw];|&/)U\}2GyRxpd[&?9Zȩ^g;ϯGn/<c͹XI~aQڢbyy)>#~1w6Em.Jy8˖VRʵD876곾뛛ߚegV!P✼S=-ttOC~muhgoA/8r+{f!tK\׆U_yRy_oO[ތ?k[to1('f.y|n h 2NGŹD1έ-5'SFe d0[ΘKAY 6GGGO * ^#7U6n̲`nM]6QlP>7.9#̷^!?F.sI=r߸EPdE&>hx$meC 褟.,z+$'押aÆ5(b?-c'fȺɵ0?~=MZn>r8xKy{ZnZ3#O=R%Hȷ*R^$w0pȔ]Fn}_~y 3V` >>>[P~{[ϧO*%^#7%ÒMw<[6tSXvB]G'mulrЈ"~+0qE`;q ;{,޾wu;OFȋ(7ϟ?vXppʟbo}!?xܐS=B6>8wƭw5kۺ^5q+w@yɾt%@Ă~tmG?^pm)n].JII>'bgdd+#k䆂fgr-y1w4wUrlκwwJ1oXGVdl{:W^O%sc9rC 2e%;A*HvtGQ ;e3|!A`ds 7~4Nw_ ɱ Vz\ =]#/NDZ"&yzmr@5;>~ZFMҺ]`/ȷ̒~§-{At<ȇEY1lדOi] p<Ź)A#~ʧ-︫N~,_oPWzl;&twk6 g,'LpT~;%\ P~JG_==w%ƹ?ma_MBOk [e?˦Co5qԡѳ#~ڐ/ yȵfy_,CODF~oas^O} .o΍O@FTq^Sm3gO׷w[՞i5EDI?ɵMfd{=vr`G~.pȔwCoΊ}GH+T>[ahi/\\:vޞVSoM1jj4r3$y}(EٹƆMAn,q:.a03߮N8VW wJ| س+W\Oշ豧͍\~^[L(+w[|6Vkܖhrpn=:''Z}ϼ>earW [h5GΞeSЈt@KS|&HQftu(7{Y| l(;79+?vG]}/]ȷg% :1soﵷ뿐y_D+@98mk-*2%OBT69q 9ӽ  qrO(g[@rSb6zp^wz$<+@9^y/Zh5A#> EZ86y\ ܂s 7y>8,7i3x({珷[v Jor)(L= M/5,bgr-`e.sEn@qn~®%ݚ P}[rc6z:ӥ /Lޜ^ 8-`ȥ}{sk> hr'qnr-`E ȹ(ޙ;^&E鞟*w`ȷ)HoLpk6ȇÈt.ʧr-`$#:%70CQfΥvl{>ǝeSN;B̢/8dkM9̷U)ir`RʁpF׿)+ y{Kv}?.ʝ'εIæǹk syf/k㮅vm/q_h5IG>ޭ=qzqr'E ˹x^ry.Ƶq|0$-k֢&p894̷>Ԇ| (v|e6 4oZX$55)K~؂+D( \nyyy.\HHH{*\DumyLďƧȝ[VdK[ |ij~^ b)A=0e˖999iZF-h2_ {冊آEf͚5i۷0aҴ`WWw7f}?:!&ywQ*ʍ| G'}ńag]w7ϻmrmRJ,-hƲs 7 q\M?;G!'_{Hn-s[%7X%q1ggy歷ޒk3gL=%&82zM֒zhrC?~|uw#V4[>:T̋t}wPeSƐoҎ }f§,̎[ʁV1R/̙3_{:uDEE]t^YfV\\\>111}U#<{mV^.^X/t]mpOO{oo޼sDԩSkԨqw׭[t1gʎYbdϕzAT*w ._~eq_OҡC})4Vo5Xik;۾J%gϞ?\s@9Il=W]o߾J*Gرc>}92q&M~'ݼ|||:t蠋d6m:k,e?{74MA> ::;==4Wx fpLkF9s]wݥD/ϟ4}۶m;}>۳gO /4S~JG_==wT/z۷oꩧvZR"<򈫫ѩS'W* ,X̙3nnnť-hpE/_6l؃>(8.6GlՉ}чzhΝj)1xPI<K T&dNʁwqW!S\k *ALģ5>*B xqkK/O}A''o6((h˖-vxx#Gč+V(}R`epѿYf:u U+L| 7{JEGGWReƍ)S}Qc xeeggwqmlAc%>^(Hě'>]n(ݒ{z:o޼Zj!6XtRq *-v~ H7n\@ΨUR戒-:8Slٳc4QG*1,P✼U>ڸ_b*v(;=)ՙG#fq. _ȉ"6mrrrjݺضmr]ѣJ[))߶m[4hРde) +14 {ӧO~~w߽h"c x~77n,Wc /40#멷ç,Jo?$"&)wnݪ[Jӳl ļ|JQ-[3[yãAUbYjY'>͵I]ǹ G*9=w[a̎25߬ "Eq˗w8qℨܾ}r_M6ϔQF~KD^ez?;6LD&]V_r[JwE̙3+M,hpE%[UR2lđzj~ԩeKc=ox4Q[]|˗SRR7o>n8eoF6m=70c)&yby(1u\\\Dk@@YZL 48G~SR&1hݺӧOҥ˃>j:4idڵ999&4-*)sD\߫{7ndpAn=EEE{՛;wnڵ~a 4FSPoj4r-_#Rn?N:vx_.\ܹҧYf׏4c [xjD7MuIGeW5bĈ'|rҤI_|E:uz}=;{h2nv'Dy·^9<rCG.~=xAnB u5=ƙN8222k ѴE׮~AD~MIW]|X71K 4Su݄?c /|+%bԠghM=7| ZJJJ٣œ-Pv]7uDIu֩S'3C=a2i'C]zS{ç.ʉM-A]87 6hNXn0oAŚ3^)@믿>pӧwܹJ*[n{*s91|"{F|v)UonBN R368ўX#2Iڸ_jX֠h/ڤhf[Tɩp>VWp-ÿ&rl.L\URSrXUn\txPOgf_ pHbMŞ BΓj\9'ĻxrlBMA80-*X{_< 7lrm|i$@8ԭӯ;ӯVupԗ{:J?uQnj | @IIȝmxf] =}vZ3~.˭s 7 C"$Ճ'Y'{?;67!EW/VGvT^GX[[*$;GpL[T$# N ?/77+ݚLTWڸo:ʢVz츳onDڈro1(%0\n  9>#7F\{ȭـ.~?TF/dgۗp @%$NJ-E..7| [˺~}!r`R~rj|+\Lď7Uyj= ӳ~eb%"P9&yu7 p[T^ⵝm_-7\o*O"-Ҏ*~r{*̑΍pȷMAg=p܌ح>.7Ul`nAƙh}tOowTNnl=4B5-l'lߘ%<sY쵹3*VG`æov}oe#/,Fٰ׽Šc~o`#<@e@cͷua;}e{}i=\՞-/eSէ4ߝ{fN3cM`f "O]tk> *? JSNuqj0Щh&9=8ǩ7:=©7mw:&_Cpk>0ag#-ʝxo^޼OnP(/k\ 7?oٓܚHTn̷ &|pLOʁpfҎ_B@<t@nBR=27oOhhSRxz ;}1;*^nIy?z2/\\\|||;O(b.۵ɩr`/ȷ(G{]@%Fر׬Y#NDiiiK.uٰaM8jTTbbf#hQ&3f#݃NPSsVh.oP^Ś]H="7ȷ^aaYj֬o˗/:t׬Y#/v5_zUn0)11Qٍ ryL` TGo[l9 h_xr`ȷ(/kv ȷٳřf͚?C\\\FFF@@EBCCKKYkׯNn0#(#h!߲oyIpLnԏ| r),l=Zi@Fo6q5kVJɥ/_>nܸƍ?sL4eee?e˖ѣٳg/GUֽ;f8J%r۷6mÇ׏9~/^4TѧAZ;w,LIIMz ./)~$c]tiĉڵ]C=i&Q9:u-oԨ!##3uTܹsӰaúu6,22R71&{m+eIDAThl|%yr{{B@D@Gۧ$%.]J)T n6%ܹԫW/qWt4hq+rW۷g͚5J+Qz֭[שSeV~FjܸR%iڴk.qɓJReGٳۭ[ӧ^b\~2O<1z蜜#I6mt2m413d&cz-Gp%!oϑk#\WPo%KNժURJvҪU@%VIMMݿQZ5R-ZwEePPgݺueYjXJoէlݺuDDF1peE]۩slZV{;}?LoDɄѧ~ⶉL4J?Fc`[(;>#7jFelU*۸q\vMn+d'}sbbŋōի,uw8~TR&26\p?ѣG6mJwWIgŌ`=Vb1 r^ v| ̥r-5 `cGU WWW{Kbqc…F*U矝J+8O>F([E}ڵ0|hh|lor=z3f?L4J?Fc`[B6=r=Z[X{A/˵Ԁ| >C# F,1oy{{+ʼnKg^ɖttOػwoqm۶YYYt؊"###G+WT*|buرc&񠌍?n80H 8Pܝ:uG߼yD}׮]s9~G0Tb)M@"rBNy- ODl3\[lۻjժӸqO>:th Fҥݰaك^jUqq?d$}D{]wo% )1":uիzʶ)ȑ#M?QSiel4;v8q_,+6Cـ~Aj&ѷ}vQ/vԩS^ۆ L`=f1 r(3Hԉ| +;?ʵT| @ ԩS*UԤvO?hرYvmɿ1b.ki֬_%cbbﯤJb'|RG_֭ ~OիWϩ4Q'00w[nxSiel4Ԯ]oݩ8ĉ윐c#Y@VڤIv*D{c4VAP \ Vk< P/-(++ѣJdH ]p!''GGEEiڲw%*ccc;31rJE||AZҠlvttL4db >FcPȷ&qh̀o`Mbn.LjE)oT|фO]ter-B[XS ۿ&P-LzzRr#r4I~ayWT| kٰ7hgr- C!r4yQ+7jC5S_k >.q_\ t`[}ZABN~˵ڐo`MsIzoo9[}^!jC5j72t\ @=ȷPȷPOqr-6[X{A9Ir- C!r@v)jC5m׳0#[[p([(?9gP-is.%Z\ @=ȷPȷV\\ ִ) oo9&._o`ML#@!rL3aȷ&&ڑoRLːo9&Ι[XD@ȷP)eȷL- "v[Sq2[s&P;-T8`-9v| kb*T c ;@51AԎ| b*X|1q΄  jGJ1,C8go`ML#@!rL3aȷ&&ڑoRLːo9&Ι[XD@ȷP)eȷL- "v[Sq2[s&P;-T8`-9v| kb*T c ;@51AԎ| b*X|1q΄  jGJ1,C8go`ML#@!rL3aȷ&&ڑoRLːo9&Ι[XD@ȷP)eȷL- "v[Sq2[s&P;-T8`-9v| kb*T c ;@51AԎ| b*X|1q΄  jGJ1,C8go`ML#@!rL3aȷ&&ڑoRLːo9&Ι[XD@ȷP)eȷL- "v[Sq2[s&P;-T8`-9v| kb*T c ;@51AԎ| b*X|1q΄  jGJ1,C8go`ML#@!rL3aȷ&&ڑoRLːo9&Ι[XD@ȷP)eȷL- "v[Sq2[s&P;-T8`-9v| kb*T c ;@51AԎ| b*X|1q΄  jGJ1,C8go`ML#@!rL3aȷ&&ڑoRLːo9&Ι[XD@ȷP)eȷL- "v[Sq2[s&P;-T8`-9v| kb*T c ;@51AԎ| b*X|1q΄  jGJ1,C8go`ML#@!rL3aȷ&&ڑoRLːo9&Ι[XD@ȷP)eȷL- "v[Sq2[s&P;-T8`-9v| kb*T c ;@51AԎ| b*X|1q΄  jGJ1,C8go`ML#@!rL3aȷ&&ڑoRLːo9&Ι[XD@ȷP)eȷL- "v[EhT\j` cP;-"bd0]L rL\  jGZ'4|AqBDh#rL\  jGx?ݕK_Ƴc]A嘸|;@51AԎ| +fL7\MU8-w`cP;-eG.󂘊okO;0|1qv| kbrj 1 _Q[N"w`cP;-TFRV{M$- - "v[Ρ7f)G_Z!70|1qv| kbSj1wk__ 70|1qv| kbjb*~j o9&._o`ML#2KKK3dʍb*!7j.̏|!qv| kb| IKmNwU ~"J嘸|;@51AԎ| LK/v(cP;-*3RÎUM嘸|;@51AԮ.+svL9͛zS9 rL\  jU3*3KjZ/\\\|||;/w*[[XD@`_xk֬\vMt3tRN 6 6jLҼysk׮}9IJJS&ʛ.g{pppttܩo9&._o`ML3(R͚5f͚N믿]_zUnG k֬lСCbwuעEN'P|g6!!An3L4ʙ*jX\  jgXRWF}]lllFFƁ/^,wӯ_?XDnG[3fkpÝoЭGP&F0T**gbqv| kbfbKxm?\n+5~-[֫WGgϞϯSXQF۷Ȑ+oG;wn>}6lXnaÆEFF*.]8qbvj׮Cmڴ8>;ө[bQ;}t1TZİg.((P>\t[~ȑ#ׯ gHMM}4hЪUsbԫW/q;00PܾpBR2 |c#oԩSMDPƞ#h2|&˪֋ - "v& g/KDEEmz%Z;t0h qh4ׯoڴO=:''G^LepQߦM.]L6M % ٳۭ[ӧ;;;X8-[ֶmۊsww 5uyWkԨ!nJeS֭+Y.)f(~Q7nTJ|=k.qɓJReG3 |c#'&c**gbqv| kbf™KtRTjbdZj'Zh!JqW[&ec!!!~ݻ[ܝ5kV{DHƎ+>#nPPS)SӧO[(`l3Õa=<&F0Tb+1՗UQ9SE[XD@L\5_7*Djjm۶믿/^,W޳w{EHQ@DQDz@D"t,E4iJ/ 𣆄$B*!J 5${r 9{9&sb(TUո o R'j险KKKt\&u.VIŋz P?6l }!oܸA? XYYyG)//GrwwVnH= IڌX>44<=x Kp7;tPͫ %ԛu*uPGBOz* >b^<q-/@.NO ?S{yyq7{WW\ysRIz5tN~t;;nܸ"}q[z67$7܎#{q΃PoBR { !#&BXX l!|2p ufV3uHHH޽̸ESNU>s݄ 4ً֭[;v''':[%((Gȑ#S~FSOխ[w}!MoR$rlԨG}o \b 7vXMT] ms=WA"_G)/oߞoBCG=M1}*j\` Ą D2j 8SEFF^|9//j*++խOII~ qqq.o? xRZx7lJ{W2))I{amzz=_O7AO/=[BVy&V[_  &\ HQ3`35- L@~ @L@:Dfj&rrrF>ExXX l!|2p u3 cL@~ @L@:Dfj1Vy&V[_  &\ Hf 35<q-/@.Q3cdUո o R1L 2*j\` Ą DC acgb5.[b"!j`0S3Bd-1@50fzfb @RXX l!|2p uzf&J*O>ar{a\z~[/cR3qh9\ӃMQ ,ffܘ8׷?nVy&V[_! &\ H[ƣ~ ѤNzRZ&{"@;{&ױG;kb ܏tH\J7)EJԊ?usZY1ݒxK7$ٟ'{/\c\ _?~W.Oپߐ=rgfJ0ޣqŚnӿIu lhܡ{clp:fB3uyAQUݿ̻@=Vy&V[_  &\ H[OadGr繄-ǸDu-R&.~ڬ\aȿɕI|ߔʿ4ɕCΚJv(YʲrzkEEqfSGcĕ}w~V#4Y(E\%9%,MŬ;o[s|ohS_r [%\׷byE H.RFo kҲMB1enqy˟5I&=O7lIy[Lui[rC$Ќ{-ۘLzC" 24SCpIvro+ #n0.t&<q-/@.Tddħf]BaD.rnsܺMtn;eˇj5Т* m.8>*doU^M2gK=zN-K<q-/@.r6Iv\;U3M_3dVg̹hFɕ}3sWzL"#eB-%c Nt{¤tzaG\c)_E4Nvk lE-ECaT-b_b+JYyd3o'syD[o.%4_ v7>UgZS/[.p,sˇ/Owoü|o'E '=',{~9.JɠP~p y/ܩ* <q-/@.Q3J&op^Ys bΠk[|WmG~vp~}w?K_͆m>FOvUuoh\J)f!u7frwpߗTW4 *q?q ?ޑ~ TOoEEaqIOJ{~:f Uz3uiVg|mT_R3Bd-1@5SV{5^.6\G:ףT.c`{'E-{ku?:S;eYmΒmTdw?73)^3_$YESM®sCci(.M:}eZ˲ VZ"o=Ei&*j\` Ą D~ $#;'*6#^G/=ӹSu{n2O lYd}3# SetwbVw31'W02(/P uPu~.S[nkba󶐵bҾw{JS/NY ᐹ%ZU|Ý^6$@XX l!|2p uO5*NϺr5ݶK\E(}_ !s[{l}^UUKY#o8YwA','kZ|Lq;uNkZ}~=0SDʹK-Ȧ9*yqϺ4ys<':g6XX l!|2p uO5#W~7<gzsϼ3}^9⨅s63>aIʒjfr SB~=տY+Ҥ_ @aݿ$m þۤণdS^KWHEH~pL[[ҵj藮DUJ%y91ygm3Bd-1@D1(/(ʎdw#!VL68پNYi+ + Ssb}’N?rѫ.pگZ+V-,)M!PfՊ?w/<|瘛:_ HoctG^y7%D%L}+ȱeC*<q-/@.NĨeYyN${k\{7V#N7K?E,] BU;Y}o@]m:zЌ5_87ʽ$綣m9U7yu"$=M_n Y=O}B LAQJ=ߟ2CK qgj0b~ifPXX l!|2p uF&e^;pg̦Gn]$bq8e\(Nˬ|!0˭=ѫ4}9^~v]Ss2}+Zp~s.X'vhS nv ҏSwЙ웙\~.TT $gjUyEO^y PXX l!|2p uG%zGo|zO be=W76K wM_s~\^ߵm؏ǻ;N5w]wZP8̮߸UP ).~ïo;A7"Vy&V[_  &\ H] EpTWr,)ZXݿt6ѹǗ-Ʉk|(M_s}6?~)3 >y[^r%jWԅ i̸b&Uո o RWsQ3ħ Ov8mdk vMOC&S7rxߏ };$X{^/JGw b+L{uaN\ڏyK0R3[]ڍ N7h}󡡳7fDKV 3usj<q-/@.fV_T`y+?W6y%3o}vnUĕas7Gw9Yq!Ǯ®a}qB=/ؑp8=j]_y;^k̒${eiq:V;y6*j\` Ą D1+{5>\haրWhvm g_:{:}u9~p B(.ߖ苟0Y׷s4ϯo=^r͵6SW929Qt@`gb5.[b"Z 򂢼>a;`}eU?Gv>qVSWG,u};Iyt_G\/^de_p'^Q)n~~kj4sm$17,9 5Uո o RWQ3Sy?j­=Q+ g]=?.Ɯhui/ ֶ '=} S+J"⧋ 2ֻ^)L{eNm>+om jν勠kp6*j\` Ą D(JɸdwSK7C4R=gB =ޛ;nx2L@~ @L@:Q3)N7vZ_⨅n&~v}O<nVy&V[_  &\ HDhfd^w9jހWx?;9)yJ*qʸQA) ّ-,R`}qzmԍ'M1,;E^f&mUո o RgTQ3`(;F٘.M^ u*!^f1kI8q?(ZfmG\vcOy PTwwҽ$<)#o9h9<ŏnxLL@~ @L@:#A){mm9?lckKF{L4r.N*JEKĒ?z]E7tzmw&eJ㜩3_$ݦh*j\` Ą D3ΨԄ[IɎ6 vV87*tΦ;S 똶;G]ɁRU(nJMM" HT_qq1HZzУ{^<+:lv.̹0bwoRZ{c!sIVy&V[_  &^ nݺUPеrdQ3xBIgo94}Gϩp~SOF,ycf`tYvh)/(rjIf@k׮sZDĚ޽ꫯnݺUV~~rZ ۷WU* .%don&fSb3J˖iAtaji &uLXX l5|&-11ӏ>hɒ%ZeQ30Xy^af@ݶ7zR7˱ vt?jy~uWj2XCߡC}YWXB1kzI fËî$#n6=?S;HR=ޘ:TI cR%Vy&V[ _)C~ @L5t1OKN605^G7fK 5#Mf^ROLqHjBI%oΜ9pѵ;AipZ^CO(LJݾШf;Ib.{;RM/oLQ23 Dw(Vy&V[5Mo@OV^p–-[ٳ\kRRҘ1c=0nܸ{ːuWX1qƍݹsgذaat07&׿!,w_MNܻo߾̎;ȹ{ۉsxW:'hovb]ݨ3B~ d-1q3Xz,,,>>}ʊw}|rS(yгgO[u̦NJ>#_hhyZjK/͚5+>>|QnݺgϞ䳢f-%ȧ;ɓEtY x-[֤I-^HfVz?z 훙0.`N;V[@/ 5g*ޙ"ʪ{c]i6C_wtW7޶P&=DŽ+୔;.|nG$7Sdxua$,yߘZ+y[m9wE=kӜy;,Vѿ`gb5.[b4ؾ};ښ|f {xѪi˭Ү].>}g;#G޻wOϒڽC )))i޼]ʽ{ϫ-[$Wӳ{ 9Lv[{l/M^u웙_1ʪS\3,TvMuBF;\yO-n-zڻJի39r?x yhՇ$jܸ1R)$:~ٺu5jݦu͛7kLYQӏkɑbLҔ%~WvMݺM Q׼ HYnmti_hZԙ([vMhkyӿUjn!ݶ(Rigg-{:bB}詔 eiٍ֊A3%'@tgTWE6sB7&_;Kϻ_^|9W?i$o>gtmûagb5.[bғ*))i׮]߾}cbb"""zݾ}{@i[^^;tÃ,IJHH(**"322^y+++%ވM.3gjSBKnݺyy/ILC55`{ʒRzQ02ێkNӊ̥MyjHWڄVhhRж8qaÆ7\xqG1kx+%!/g:Ov۠*RUv:>LtfwVߕ-Ҽ+Ik-ȒC !p-N:H}NЦ<]a<q-@!̤kMMMծ&5% N>yyyngĊ,H5@536I+,ofkF=w*诮1+Ntxq]"4s; u!x֦wk_i{Cʻm)))YNo.m}VĻ!7vZ+ 0)nYySW;ibL`haρn*j\` Ą DwԬzrĆe]4\{c5] dwαp֚#:f{I<"*j\` Ą D}챤 1Υݘ[ϥyzJ#Y-s4!3 nQL]Q\͆3o}K3Bd-1@L!jfg}mb)/RI7 Rμ=p*+Nx(Z\GYRJLgsM"'Uո o Rg:Q3!g?I>t[{O0mYyu۷sŻu8u5xi"XX l!|2p u&5ddQ ֞t@eeEaՈܘ8T rj*cj359X9ѷ6Vy&V[_  &\ HE4bW|/8qcыZSQ\zy66=LA 3͙:nyagb5.[b"ԙ`Ԭ,'oO7=V_27>LInL[׉(Σ۠L!/<׷?n`gb5.[b"ԙZ,+K1?nWm|Nzz7EUL5CtSN]ubx dUո o RgRQ{.+ZX$; z:t-ܩi._W2WAg }L@~ @L@:Ӊqɭt0*ɋN _{v"h:n Ǟz\uPY A u"lf0gj(/4_^R*j\` Ą Dw,ߩ'it@u]v7cy U !7tyO~^Vy&V[_  &\ Hf-gG O&7R6gsSp+H"֛n eG:gl %Uո o R'רBk]',+`umQʜ+lЮd A7ѐL-*Uq3Vy&V[_  &\ H\fW;|~JnCifK ֞ei&Kei-`Nx8l~nc"יZ\+^s̍n q;]FUո o R'˨YΕXE Ļtxr+ZO=㯩9q\0jq89:4~kBNTk db0YX l!|2p uF53tEt$Ú={v633@zu;v|k27Ő~ YFqIvt:T~ESu ,zVTEp-+B]ڏU O֭[tiʊkY0ZZ udgb5.[b"Upc{LkM֭[ ][3 khР~&\}kxM?+*V<ΐ~ YF{R򆊍um_XFXZ6T*xʋ2]v;w.]kG*nuԵ6Hk"0rЯ˗[X'LVy&V[_  &\ H!Q3#tsV}ђ%Kk!aѣW~&qJeZސ2Bڵߑ/,h]^*h*^]OXUVµL$ڜ|Z9巨g?l/Z5KnDĩq7nHVY;h֭ _zyCuԖ;d>iӲAcf᯿Ɛ.]:JUhT#>3gfP7=o.1Mh@CfϵlٜF]4=*bkkLqwC'V -Q΍t{Գc \qywHk"0BHo_;vy_p32|yĉ߸By~gmDɖzs>.?QE&<q-/@.7jft}5'|z lٲGgϞꓒƌqƥw=5+&Nظq㸸;w 6gi۶K~4h+ܸq]R\'H%tڵzz駻w奾ŋ&Mtѣ\?d1nEUeXҥ#~\&ФIM|݄㇐*\̙_x7O=޽˹C2cGv뇋>rNݱ4?οvܹ>χ~qɊoN?ɸ;єc " k덤o֫WwڴOw233 |d6|UKn菵TKL{YYV={vֳ:ٝ͞ٻ޽=卺 U=izC[)T ־[{OC׮]gϞ{=;U rMHH2dHÆ ;w_s۷-crv%'OG?~>iZtyS>j3cmdS=34~~HmЯU=iRlPP+\[ˮo;7'V$g' ʳhhpn;x;'zFV^nToBs7S\ щ+BC? uo 8h緄տ%څ]:a3Bd-1@ꤘʼtū 0 }v.gmmMBXXWBjh˭Ү].>}Acǎiȑӳvo!CJJJ7ok.r޽mڴiٲ%Yʘj2L:unWš^^'߷o%W]_k~ȁiVŹ=Pee!|Ŧ L޻wy)Nm|睷tB:ŕmcTvZnqfffguku.f}ͺQWgu=M*m+uXtkaZ=N:'@JgѨ(Rigg=ݽ{w˹I&iK[ɷ%'Oʽ;y0S\ щ+BC? 57۽{iG-4:;X(uova2iNL@~ @L@:)n5 ot]v}ݻw_@Ѧnyy;ӱcG$Y+!!h򊕕ВToDqqqӦMb̙\yP(|}}ɶխ[7//OzK)ix; :Jz5k. s?'Je؜9v:-^*-R,-6md׮BB] M[u7O-WI6Zin%AGvgXնmknU_]O6n୤6X5ʲrF7& ޳ىH,J_{5ҧ?mƍ<0߿O>d J.ۉЖh̚5k蘓oO?СC҄퍘={6YF{-[~=N:_^zܗ' ~\5UB:mnge5k2W>=\Xsj+i^̙իK*ׯSN >SQg֮ĉ(ƙSP'[JR̙0jo{R2s7wRt]֭O~+-k>dH6ںufI-?vKbHx`˖_g{TIwy7PV)bkk kE{=; Uy%'Nhذ!|7-ZTQhт,9d DhK/֌ʽ%u{ {=RӸqcc ;: i0$xǎ_qyg2= yϼn'Rinк["te2iNL@~ @L@:)"Z1ܻw/33+77755U&;;Tjpt4Dzzzyy9yVR%&&VTTgG20ɕ #7SJJnr"Rg4_0^޽sW*_ 9N܋O\]M.s)+ ]^U=i-{(x+yXڡPK7޳ PsciiiJJJ/`ݿ_{I NxD䩻fǢT-Y%n&/J# >pw^%&ބ<Ҥ=r+v[:a3Bd-1@$iw @ kH6gݺMvP-eT3L-udgb5.[b"U@?] 5Dš(&[ 9NKhh/BךMd"7V֣hX'LVy&V[_  &\ HQE +&D1"V ui7FU@jDNL@~ @L@:-VDw+\[ . !/{dʨfjL(\*j\` Ą D3bZ'] 5aMyµ5-;ckKeI)2eT35&udgb5.[b"U@q8XCBCXEEpmM6kAjDNL@~ @L@:(ZykȻQ--@jDNL@~ @L@:(;{It-<5Q] ֨#AׂL+b0YX l!|2p uF53Pqz] 5aMyµ5(5AE)tȚQԘP" Uո o RgTQ3Jۆ*JakȻ9w\KׂL+b0YX l!|2p uF53{SEӵP4R^[i Epm )γ{~pxΨfjL(\*j\` Ą D3ႿZρƅ5or5%n1T]u[]k[pm Yt-1pE&<q-/@.Ψf6wjDAw9t'#Ŋ70F5Sc"@.O~dgb5.eH[b"Up~^fеPrvyePxN>7%M(?;霓Xl:WGJ/ r!oomȍ־#е`jD!H˓0YX l!|2p uF53\y~g*tԖ`ooo'''uuGwnv;ϲٹn&m yfFFgAYVڨ@蠉2ڙڔ'!2IO~dgb5.[b"mԬJn&kܼy3<<< ; ;&e6S4lk2B^M^)))KΥkdLmDW'?a3Bd-1@6jV;ckddd-X^~# 7c.~g7?_zz #j&ܒ_*K-g3)OC]e*j\` Ą D3ڨY<ϢkIV\PH9K7ѽ ⰈE; [YBׂ)L-c$Vy&V[x  &\ HtfE%v *+X^zn;iv:8oO.\ቕ+ZϽO7)L-c$Vy&V[x  &\ HfKvkj4pjdm[ߙwșz \KׂL-W$Vy&V[x  &\ Hf׷ EYg?etIu GEQ+*J5lR2-LK7L-W$Vy&V[xw'pQUlϲ,6[L-T\r=-\\s-LM4DVeWqYDQVwW牿P.0u^;s9 o 'vSYIzXE8vN-zhks!lw:2zaFHXF;-b/GՊ 4TPʅ o 'vSY3NR=^%;ZZ^ 4;/k'ޑi/c^j]^=}aX(/*q$ʙqѰXP-"tJ?k&b/PǢ\C;=U;J.g#PCIs#~A\ԪA\*gjEbBȷ;)YQezJP7 %A00)MjY'΄ܾE'2{UJKJɥrz\4,V.T| 9( Κh+uRw j8=+rvV~=&9 |y^%%;>'?>E,@TZ}8KCL hX\rbP:5;_ܲOƹtjі+FXÍ0]DקĬ]/0urbA\*gjEbBȷ;ҩYY~C dg]K 6b|zdMnlX֤N/p$ʙqѰXP-"t8kv5n{D}#3B"P~MHZqU_ uVP9SC=.+*@ANgrXJP;Ҡ _{tOPE%g9o?zߋm#N_P;S_j H. 35ar[ȉD@Ts̯t>;*ery":s7;<冂bYB>XRP_j5 H. 35ar[ȉD@Ts,^fesi7|R?ZM8!U''}/*- R Ari  @N Jf2cp㒜u--"nu{vGwJJp+ةU~|XQ_j5 H. 35ar[ȉD@t,^wGg=-Pd,?ѵK%ˍk7?\,Q_j H. 35ar[ȉD@t֬,P2LXjP\4~Gb f '\7uh@%n;XcKvZ? HM&vL~[ rbP::-IĒ_B&{(_ IIFNΏyD{_ P}Q_ϩ3׋LaV P96q*@ANeVIFCޅIibV։3.XQ, v>]&Ko\JrP\rg JPoeɒ%͛;wb4( HB-l޼s&-mT| 9(-I_{%ZZ& PШq:'*^,%CI۟܋PwIAbjG } &N_@ȷ;ҩ/*LNwhѻ8=K,9j2C([uY{X6'Wí3#Mqtt J:P Ҕ&4Óy_ȷP-"t˷$aӿ+m %A{tesnęv"qDC3492Xj $$ J:P Ҕ&4ccc26q*@ANVQe +# /?99>k}T@ <s}.WaRk-65ӓcѠ MiibK;''>-mT| 9(Z󭒌M мD?]K;*/PR*{Ǣ~ļb2qt[ʅ o 'vSk%X-hbb2â4 -`tZbX3I[mq  #&V.T| 9(󭲼B]g4I_Trd"ώ SarNLJN]pE%tF)"J;&(AJGM\rbP:[u6>{=E:M @_T"֠I%Qr~lwɉ{}z8r )S Hȷ  @N J|PZ찋^bZehQoHu4CU+YQO ["U@8R:-mbBȷ;ҩ;ߺrom4q!qeX4nSWDŋC Ԃ #&V.T| 9(-O)g9Ѐe:?60+3x$ԍ #&V.T| 9((Veyb/* Ӥb vu~ęM{EJ`LJ,O{]|Aґoi+*@AN $hS P”tώYW6} XN^\O)ޝ?ȍ>/AJGM\rbP:[w6I,@u2âbXnJ~|JZ67qj(.XMv7hAJGM\rbP:[w.B]|t--?Q c7غ=;̣; %RgSC-mAґoi+*@AN;Qoτs]"m.:)1>3Pg]|KXP-"tɷ$Ycu] P8}aq^oϛV_xbF-f"xR"U@8R:-mbBȷ;i*ߒ1p|JV AK =4~IQe%Oovx #&V.T| 9(-}Q۳Ò)iG^C,:qƷWǥ>.e=^^Ot}rpݥ9Aґoi+*@ANk$1G.%ҵL |S-ֵQ_ZwxױO%e*Aґoi+*@AN$b6>2h]ТwĒ_ r O塁3/T #&V.T| 9(6+F۟\/C E#2RXP_t}}8f$n㾗Fo?6'JAґoi+*@AN֕+%.OW,&yt4kC1g(-;N׺ߑ% ]K˓7eAґoi+*@AN$3,Z27Xt~Է =]rYe>˙6{-YqX4)6r[ȉD@鴜oIns⻥9b炍eXjh0\Q^&] )΋^KAޝ?H+ Hȷ  @N J|Kr5ݧ8fh#] =4hC'n,LNAJGM\rbP:$^r{fhxj'>e9!#8"`W BX6Wyq>]@wgDe #&V.T| 9(bZ/P/E:8-S5f|x1m JPv:MJ6AJGM\rbP:J2s:M h05ԥK ~oT5SKA>o~ূU,=$>ݦ8?: jN8@ Hȷ  @N JGUB?i#JFFp#p#ΆޡE#fFe aVFAJGM\rbP:K3`r vC2XNw.~Ԣ-Ii\IDATu)qjV]o$ |KXP-"t[0 wb4ZwOʼn%Z9Q៯u|Aϒt+P/Cqɹ\ݦ8:զ-zhL]K˓7eu #&V.T| 9(VM;Yg#P Ii ~oT>>sC,?I5Zw&'kud"U~`n8R:-mbBȷ;ґoP P #$Qw1kw{:'ڔw6IkJnwW?-}`#P8R:-mbBȷ;ґo՜(p9/p#.ԵLv9$J.x̾E{K/^1A7$zwnP֥9Aґoi+*@A|Fef{Ԣ-.eK_ y,?9kX :2z}yqt[ʅ o 'v#ߺ 1<:?4hVlkr . L.)IVx] ~tw Sq'mԵ<ps8R:-mbBȷ;ґoCi/78?wXÕ+',\Ua|Є %Yc '1+rj߿GBjygIFC 6 T=_6ψ%<>0ęJ.zdk۝X$-@,AJGM\rbP:-J"Wlw|OmSgиŶuIX5X[8h8^o_t1Cm(=k_@T Itxm#~v#TAґoi+*@A|K^yg_MǗ{n|+HFɮ|Sתo'P̹rɋK^o)84hֹ_8!Cֵ<&~} PC)6r[ȉD@ȷBGоFvH޴꿋˨78?6PY7]~+#^G?_V!Wp%M:-<-\Aґoi+*@A|Xy=vߖtSw}s Vlw=gƙvɠY'Dkf}M\=iq4;/z͟.>%Iw( #&V.T| 9(VƯK-4yݡyOF\e>Ӓ]|L>,P8-}A8r{fh.xUȨ7$xu ,dAJGM\rbP:[Y%; [V}7#wiou+Eȸ%wz-<瓘{.VV'NDu`<^'C.Y0|CgX& Hȷ  @N J|_WhTyY2z6n=qzˁ'z8Pp!5|zZ%- Hȷ  @N J|+zlh춥Y_ -Քof/zZe228ZUW>o~xh qt[ʅ o 'vSkuhXw6~5ZE`4}Lv}jH{j.h AJGM\rbP:-6͔oVn MOO')/b/p}jȑl b4je=8rկr_ Hȷ  @N JGEզ W;::''' g6k/7©}s >:ɚ[3C}w6I-zpeNTX #&V.T| 9(VVie,ӇL\ТwN9q@iN~FpĹ_gu+cG?rGT>y+=jS[+3e@i8R:-mbBȷ;ґohi;u:wc>'*>?Щj=<:8j$'ܘ e2ߣ<3!ۨ7# Hȷ  @N JGEզ- i-f$'*>= =a3?3giao~5lS,h,>9XZ|'P8R:-mbBȷ;i<ޭ,s/oɵ:jO:wzlujZ&|2~MKu->4y]кCSns:91;.n;`Iش1l]F7~ڕy,:p]~o-Nˀ Hȷ  @N J|+<ܦC7n蚦MoyM% oѾ]nɵhǏ1V?8?WPG6k՗괙-;^qĥK~xaR8S\>'kZ]~|XAJGM\rbP:m[aaַvkfwےmVcǗLZf0MTl۷WWy駹RTI5!V5:mg6ii}=L]v LVr9;b/]qV,f #&V.T| 9(6AK%3!~a e9Um6xp_|bUƗ-&G*UHMza;+XՔo;+6[O~u͝]>߱@ƯK>G Zu6Ώ8n}<<6 9cƻ~/*Es]xgX+a553z~}vռRcXmݺg;}2'PTUoK|}lI?۪U [XՔL??Α~s'~3tԤҲeӪ鬪dr~)pm]R JJ8tx12`8R:-mbBȷ;i0:r)X1b,,:6kvwͦM1lX/itSiҤA}޽ߏGz7uM?xsvoTbUS;ooݺܹMWUI? /<ӭ^L齫|de8^|P!ps el6.3Aґoi+*@ANHOߗU,EǦMoqwI! ^E<))^M4⋱a?ӭ[g}vxov矏@W8t韗/5nxۯ42 ߸ *&=6mpa韯\N6VjJ Vo(%kWzkQJGWbUS2;Ϫ8n⭷^4ΪZVl,-H]7Cv5aJGM\rbP: [n%S>C/Z"nퟑ=WL2gHU7^'>(lnvՔ*nt]oB+7m:cƻaMBjJ7I1VΊ| Sybf*6r[ƨ7E(̷׮1jܸqرYH-[?/U[R0a-4;wɓ;ʯ3J6rdY[rtl[tybdeյyf?8?KqpXc*}f]`~֊ӥm=,lyuM^%K4mzƫ)Uo|PW--ߐnO>TҦmkV*XՔ*>Jo6ugqq.l@^^@|}#"aJGM\1*Pm[k3<ƍ“S 3zzsMM֒%SL=sYПׇVK-'PUwLHWX_hl[PIIYYTjJ4_hCtG鿦~i ιxjJJ850JP8 T:-mbBȷt=^|V}A4ous0OL#&V.T| K!P-Z5mɳgOO+o[@\$3Sȷ  @6b/E!ߢjȷ:ŹH'fґoi+*@l{(ܘ vy8u-Z5-Nq.扙t[ʅ o }A}^bV~|JFpDˡs:G,Gһdvlol홡ݧاk"WlS[l[@\$3Sȷ  @6eyb/Ei|PR`䂟[zGZ<=DP s"ϥ xrKw%-X4+lӫv~/,\L_-%2[4Z}?殰vvv '%eùH%fґoi+*@lݞ&IAˁ'/xF6Cgez`EmW?NZe,Ӌ[yiLm8,_{V*]G6e;/ڴ9iw޻e]ԠYo,NJ7s0OL#&V.T| \pn F朎OVǓ ~trjkiRZ$̰,q|z!!!>>>ζ;Ǭl^jYyOmZyn&4Ǥ&ͷtq"Yybf*6r[&t #^KK&=̦ǿ0+ώu--|_w1V٤&;ߊ  Q&v;tgp}K75 ۝}bۼkGT 1iI-999''Gn"aJGM\>Qb/mpbw{ukWs,O~l76!3 ~^on z=䕑+_x0|?LZաEs~*˨wygAbOCI8@\$3Sȷ  @6!'ffF%z+,<>pONO nybf*6r[_o)b/숳IN1kwgύoaĥ`Q%Y]ȷ\z ﮓ{Tʾ@se(-K8ag6#ȁs0OL#&V.T| ٤z=]̌4'\`w~ʷߒu{WF'kb$h 5V9.h0e(AқkiyjіKb@p.扙t[ʅ o V@ʍMHqYo6}?u}ro?7/֝ٴW%o5i\2z͟b-*# ھWDŋe7s0OL#&V.T| $zg ԗ¤^qÿX?`#4}w4lw1mRrcvoճs7:X!U3d8`}jіas4ύ84h։9?ߵ/34 VIr&wib iokƹH'fґoi+*@llb/ސq6-q~49ROg %PȷBg<&Xo?`2Cp.扙t[ʅ o 6>Y#B$&^#pG\K|#>hh[J6E {b0C>'V"aJGM\~Y^hFiVnxۣS8q=\ 0ڗ/, ߒKFH>3\Q.g(-;ΩUЄ AJybf*6r['m{VF>1wΏ WISWdp4'_ |O4q[ ebBY^ZzX,\$3Sȷ  @6_o;h eDh:u߳-F:'rٯIyg;@ȷj#;lyber9Sߝ5@8 T:-mbBȷ?G.UbdzE+^ewGWƅL^rɲBP򭛓y.p|]~Ғ^z ώL:qFƹH'fґoi+*@lN7P#қxxvu _ kf~ߣ-)>[ij9_dӨfI[D @6_Z+-SgO΍MGC[-^ب퍚e@5ҩb F. 03Ձ|Kkȷ[&ll^}Q堈u6GF/t{f= >+W.\p.*X-2Pw~۾kħu5a;vU@lu3TiWvÓ=cT| OZvWg,wH_Z}f홡Ĭ/*GU 𝝝m!YӞ4gܗ;@سvͳyq [6:]`f+J;1T| *@l->]EȋK^i2v|ĜtK.gC}m;Oma:m> 3ooשKm+4(vfN+'''{P- U؋:u<6=Zu~tǬ8zPZ&nJzztxUl\ߚЦ_1bh8tyR`Mb` JpNNNqqg !߂ o Cf%9SQwϏ8Ѫ\HT S\|vT,f7D.MתoXTs0OL@XP-ds里pȩ,k}C_qz8@0~K'-@q`f2#\B"ar[ƷǗ^ܔkͺ{6.|q0ҳ$A =;, "ar[ƻ'^XԸ-9܈ЩdP\rfKA~.ze@!.X{ZZ"\$3P"V.T| xt%Z}>]/9ӽ09]f^CZzͺ!9ȐVJ ybfJʅ o }/>'29Q:{=|O9jGhq:Sx)u-z4;X,0pWKkq.扙 (+*@lܞ /2%׺>5ĥݠЏMr/+Kg&|ТwIibP؟tJtpybfJʅ o \H{54'Ǒ1[xu\4kY|T_U߳[dybfJʅ o ]E3^*N:ίtz86 uϨ7$z}ۿh({ygݟqU e\$3P"V.T| 7({58-VG?i<2zaX^ĬqyﮓNCs83i95@8   @6vwtYWcSZc%:4R b 9?#$R,ZbBxuXC8   @&F&N3%; g"p$Ab-ae=|gr~u}zH$( "ar[C_TbwbzeD!K>:f/ryCvb؀~vpzgybfJʅ o Ҭ\-^)-8nѮOX-?>E~e^ޝ?H1-J{}u@!8   @EX*st7[ wXP.EA *s7TRXs0OL@XP-;b=^ۋR/#/}Qɹ\<:wjkKJ'Bq.扙 (+*@Zz 62qI;xj&ƹH'f&D\ XW2O-eGOe.e_vw4~IfhXp㈸DybfJʅ o GNyu$*Mivɯ69o:uu^\XPJrcv{fSCU%P it--b0WybfJʅ o K~aݧa()Ygk7Iib@:qG6~hЬ}WFq9HK׺_N9%E<13%bBȷGʾ}?{h<~'|@2&yC6V'\xI@nA\H \$3P"V.T| y$:k.0i5Ě{k;VկtgΜIIIxXFe8s95PZ&/f5pÇgs.lU׵k8 ǹH'f&D\`qdB׌g! bMlҨQ#[Uw_^k֬tbonhpU^zӧW]Ge k$37';XlߵJɲ.$/^|GڶmۺuÇg>sNlU׃ ~8Q_X,s¹H'f&D\8seb]ת?퉼kު)ѪXUt5W|ˤ{C##サI28f"re]Ho~= PcshwoU/ 69\$3P"V.T| yd6mk~SX~ڸ̰hVsFx &OVuǪUS2guoURPz7!/.Ώwy&#E5o k5vFFV_7uTiV7Bc7jJd#fcU)՞O~I,fs0OL@XP-ݮ֋Ĩ7D!˨;krȀϟ?jԨܹsϟӧwѮ];GGGӘ .;|?g}tk׮6l0Yn]nL+݈oym=111Rٳof͚IܹsgUwч V)e˖ڷoeU䥨,꼞e'4ݾ%K̜9UV/w%2Ys*JLLk/z%2>|wz=o]P& ,0_tL8O*r~]TBTR[b}'f+n| 0i}Wz5y[ͳJ.g>5$wE<13%bBȷGmn{F^\glj~&*K7?~k~~/ܫW3fnʵW_yzzo߾d6m/^l΂ }Q`^C}GRs[YY\t;V[j޽{###vڣGjXU>J;) 6nڴ}ǎ;wWU|W*lѥt:GGGF}Ѧ-gdd4̙3?\ٓ4iDzt[[ݻvmyyyUUSoVwg:f#UճݹRYwB(lmqՏKT6O_T?Mc*KHH2dO?-m6))M؅[U׹߽Uuސx]O p.扙 (+*@4K!2eJMT-U|Ъ^\+5u];?68=K, s0OL@XP-ژWOΚ K_u7ƦQFO>ٻw cLcw/5"&HJrԈܩJbR %T]ԯ(036یa.c#>יfv}Ϲ|߽{ޟr>3W-ׯ/z2]cM/΢j!`*,MʦϷ^o~+(8 ^]6&&D+WݻxCܫW/QZjohfL뤥/^|ԩZ\`%/۸w?lfԮoEY ϻ`t)99jժ?8NHieaWk'f76Q>xx*-s觿|ylf`-  @8_QzrcBޛ~'I.ӅE?/ZȴѣGEpݺ6]cOM?C uP8+T0e%ҪU+t,6qt;wcsUX57V(̯ZHZ+yeK,^J'˕)^vGȑ+W~z9rH[a6,^:ʱ~Wko-.S V`:pqzbַ埍{)/Z$ԉ h3:@~ cUr>s7~&HJJիW f%555&&ƴy'nAӈ!ۥ.]d\7Xjh< ׯ_OK˺ƣXkh-nd,^Š5݆7r19=7y il(xn167tb|fsBw͜y?ֆy͜8N F+'f*wü>^#Wa17Ga3( EB1s(;\FG^ջS.(+`6~^~ZoW>wE8^{Kx{X:12-bBoP0zFQ9ZvņZݣӦM@Zpƍ}Z:xiԙkF˕G[{?#v\:"NL@[M ;-G EzbށӶu\ Rco_\wU:?7iwLN-pC9 ."NL@[U;EJR_M.3]s{&^+27>zd(DEB1s(k˷OOLvssW.,I#f́s|~؉ *9 @.)9 ""NL@[5%Z}yZo:)[́g- Q/=9oe܉pLfFS# Z$ԉ h3:@~ v;ѣRG9jG>ٯA@Ș~{MELͿy/P .G@EB1s(&G \fׯ'E[Fr|{1)yݹxMS7 ubdZ̅ğޫr@egE@״ۉrP$efd?vbxT긵ջO-zO,rU(8Wr(EB1s(7n-8iA|w"OLZWM 84>>n%f\kP'F&E\- @ԮCGs>2RR2hHp>{NP7ߺ}CϺzKr=({M?9 kP'F&E\- U݁='т~'iG1oNLK]sZr|5xgoH8U./h[(`EB1s(V4][#BpO^]^kc>+HM|; ubdZ̅ / 7}y絡c_Jyea3 r#bs[{?ClbG,IW毖 ubdZ̅ /N[3W/X7)~ot>̼u܃^˹O>~qOg/5@"6m}9 kP'F&E\-"9g׷P^M៹[,J瞧Ki=153t"vbf,XJ75|3VݹxM Z ki,Z$ԉ h3:@~ \cGll桓պ\Sfzƭ#g/9--oN=;B3ZCW1UvZ$ԉ h3:@~ cǎŠW\PΝ;DbD.!-3-Vs7w֖k{N;5uԮCiwlC(`7EB1s1ւ vBT̟?v~+=1yKO^*!#5桓~ f%oS ܢNr/%&n.r"NL@[J~FZwϷ8̐@^ގ <|5Fo6dmvo~p|4-ź]RnQ>X:12-bBoc3Sٳg>}:::Xܿx'#9Ť;q'/{l?/{MyGbPw,=1Yn샵H#"f.t8-S?|>|U(jg膇ݹyw Qe}6c;n,7]o?(G`-  ֕+W֯__Ϝ9sٲe[nx˖-+Tj*A.i~k|(o)SV4;888<<\Fo-r[0v;1&4WjK[[:3 >Gnp7^\/G`-  Vjj˖-[n-7˖-R>7!==ի7nܐ C؃2q[6+W>}:#5m['f/khHNuԅ&sO>k˷n!3N~U=w._иO~dc-  ֌3DeΝ;ʕ+qqq?cHHև *͓~Ν;vXllݵrk׮ïZ.O>ONA_B~DCf\EIZBГVn>2eើ~ýt͍ Kx?uQnP3(`EB1s1ߊ,Uh5}t,YhsvQ۷o5:v?(񤤤iӦ5lذ\r{Ynв~!G`-  … Eŋeٔ4Oڵ/_{bn޼k.SDhQ/E0((Hb ~,YҥKJ4-5b~ڙ(Q,OhEr˪UT\'xUnN-<ͽF ˕e)7bo>awonSW`#O}&Q>X:12-bBoc!zj%cy\IbڵkJȟ~I,Yl*U* Re˖>;p@"S[oY;c572egΜ:vX^=̕cƌQ/\P~rH+x + Y-G|hHZN^rvb|U;e <]f%@"xi샵H#"f.t8F[R0^^^rY64OddRYbϟ/+VLIkQ^)S&,,̴vs-kg"U3=yC.[q5gggQԱcǷ~I~+##ᆱVrБ#GcjnN-%UGdT.5.fhv*ko{^]=*tdW?>~:Bn]9 kP'F&E\-p--Z?9'N(VVVkʴ>32[tFtБ +Xom{UQ榃^ZxXT[[O?;XZ$ԉ h3:@~ #-Cvx⢡}VRe̙)-[|pĈ3fիג%Kӛ6mdtF-:\|[XgsKkg"U[s=мy$'Xn˙4i#yZbi(3qW\ܹrW1C*ZDqƵnZ=ҥK˾ݻw?z~ae_Y-#9%ky䓅> us/ƯA]ƅ{UWv=( #|} ubdZ̅s~KqC>kɅ6E K_ @KvC } ubdZ̅g~ ꧼ#µtb҃coţby,%GEZ$ԉ h3:@~ ʉԄWצY~y{^xkaޏj{?k[|vOw୰)1qr/8NQ>X:12-bBoc=[Fw._9psXW?tg.kJ;޷>n>z5T)-Gr"NL@[tғR#_^1~C!~cP!?qQznU7CO&E}5k˷} ubdZ̅ {6[I1yegBdvf, 37;Q{>{okޞo;0&KrwKFJ{6r"NL@[tb~+)2GwR#wEl _{b#S]ڽK!+ۍ{w<6c\\g/N(J2הl%G`-  A~KV ;ޙ^ Ԃr킖z+>!z]#STԏtcߢTk`"3= kP'F&E\-p []5zT^͞~Us,Flǭ\of,:<~A'էN uO[[2Z`^yafViOf6 5?Oהh)G`-  A~K[\XV[GҭucOVvJٮx _l|،Eb;o)l躩ڽU~Ij第UwE|/?vu򻿔O\e}a+\S kP'F&E\-p [Ż6lʶ$%%?)S ܔ׵-=z6!<"5|G샵H#"f.t8-H$beխDK%ΞU:E -;0b샵H#"f.t8-}=z֧vﴄDo-5~R-˴؁zra샵H#"f.t8-HޫVn [˖-sww8&ok˶%n 9kP'F&E\-p [Ϥ1s[O{|jgR\ѩ!>X:12-bBocҺYջ)rѷN[O7tr"NL@[4 us\`v~ ׯ`EB1s1oiW 5En F~ "b;_!>X:12-bBocҨԴmm;>k\`Eށ`EB1s1oi`̔ ̐샵H#"f.t8--ͷn[r%(",`-  A~Ks Sr(">  "NL@[䷴%-!7-XG~ "b+`EB1s1oiKSCϒ6y샵H#"f.t8- 9'6(9kP'F&E\-p [Zq#sׄW{!@UZ$ԉ h3:@~ &$G~U=rAh7BZ$ԉ h3:@~ egp}9C~ "b[r"NL@[' wv! --ޑC} ubdZ̅ rW6<+9\c("4[Z$ԉ h3:@~ >{˹} @l샵H#"f.t8-JOLd_<\"@`9kP'F&E\-p [/.GsE&`EB1s1o<7=3 N\{(23oа ubdZ̅ B1NxU\'( 23oа ubdZ̅ 6)1q>{_^C.+[iPhX:12-bBocŘ]]? \( 2Soа ubdZ̅ *O}{G oP'ln:HZ$ԉ h3:@~ zDy?3)2F.[woа ubdZ̅  ^]F~ v"-"NL@[ =)ſِS?EAj\(`EB1s1oAYoNEAjm[(4EB1s1o9ܹ65|3-!Q.( ( Rn?G~ H#"f.t8-Ǻzҫz䂂C~ F,-"NL@[(fO>=EAr-fC(`EB1s1o9JfFƮWƅM9sLDDIPd%]I~ H#"f.t8-G96cQ@̴tcQFcƌ1R0r2=yyyy]_vFtƖQ>X:12-bBocrMH1 )V6m&O|wy&G@bD4-"NL@[ _Bxs=aRNW4[Z$ԉ h3:@~ V!KOJo6nrAvߟ0as&MmۦgΜ٢E2e4nx֭J׷]vKU֩S[խ[b~ʕ+VS~d={N .e˖]Œ34iRJ+V(5Lg/bs;oy9 kP'F&E\-p [loN5jTd.][^hݺ3g۶m/HzzzjFm۶ػ:fl3ϘҚ6mZJy8qbÆ O'~FFF&M:utc֨QXϟ?/ح[ׯ{6g9Xk(\F~ H#"f.t8tnצFl5j׮]jj؟={vrLK>J*f͚͚5 1cN8py~k޽"8qDRyΜ9AAAN8'OhO=/vs ׶ BZ$ԉ h3:@~ V9p«zSϟoL/<򈳳sݺu!;S~}#**J 2z衇[nnn"+R*7Ts߲g/ZsWoа ubdZ̅ U8Rn}ytxkxb?w\cǐ&ww *L2Truu5oԩ7%Zo)Zh]U!;5xgv9X(n09 kP'F&E\-p [ 3#cc|\p7"OOOyyym۶D񉉉-tRtttZ\]]V?~7n\F˗GFFz{{߹s`%'''])4', 4^znR^g/bs`%\]9 kP'F&E\-p [gҨ n<رc /_ސqqq}7k,lРAlll:uʔ)ӹsk׮ݭ.vVjuΜ9Ӽybj֬iL;t j>6l*OX~J,|˔>su0=/Ba-  A~ޮ~WrM 7_&v[)>[蘘FEDDQK233/].13Wk)mQ>X:12-bBoc߲g/{9w\PX[r%qqok샵H#"f.t8-IOL3xEA܉mmo ubdZ̅ e?{M7d-t( b#BZ$ԉ h3:@~ }sArA#@Qـv#(`EB1s1oCt/ @~ ֑3GQ>X:12-bBoc*pI1 E\(`EB1s1o̴f, Eçoа ubdZ̅ U4?GLqoP =#-"NL@[ E-K/8-r"NL@[ Jsպ:rF.p4[1Nxi샵H#"f.t8k @Q-"NL@[ @f>BG+ՁE}v} ubdZ̅ g-潌4@oPNc(`EB1s1oӵ{7x5Z.P [AGvv&BZ$ԉ h3:@~ V~$Gx9w <, -=a;_+G`-  A~+|7@eoPD9 kP'F&E\-p [yoȌQ!@Q-"NL@[k77~'I.P[Q;CwuPZ$ԉ h3:@~ VDrz@oP\qr"NL@[r+ {DjE~ B!a-  A~ˆԸ){Ǿ\,ՌEA䶐Q>X:12-bBoc߲ ?):f R\oPDnc샵H#"f.t8-v`2|ƍOn.#@QpmwkP'F&E\-p [֤%-fI]N @Qpm^[(4EB1s1oYˌc܈h-MGa-  A~˚ P~L2' A~ /(믄H#"f.t8-R֔j6]>Ȑ+i-AL} ubdZ̅ eQZ߱.[oxv({Or"NL@[, h7Be-q'*F~ w~Q>X:12-bBoc2l[cNkMޏؗoɵUE {^%BZ$ԉ h3:@~  +<*r.|>)r= @QpkWkS(`EB1s1 *z+P}lZ s+½tMM3wyn四?䷀IOenpy?} ubdZ̅(/c3TC~ (x,@.S9 ؇kP%F&E\-ponޮxykygEIun޼y19Zkԙ3g"""h>B loy2[6;(&\ Z$ԉ h3:@~ `[wefboo233{Z\5j4f9y͛%G .`iov!6X.~@. ~c샵H#"f.t8-lʢBBBN>-+lrԺ|xumOJ? ,СovڞJ׷]vKU֩SDdҤIeʔTpŊ^xSNʕG,f)fΜ٢E [ƍnݪ5v+l_Q֭wItnK^^^> VZfGrr9;xD[WWW~Ɨ YAq¢e_r 40rΌ3&LܤIm۶)-^ Q#}} ۫joD?dʕ+VPl0{7bo墹G ;IrH#.޲(`EB1s1og3.JV5rrr2doBBB&M:ut~)KɶdyƍyzhJ?}#<"v222;F"^Z#Go۶-66֐xguru Ł}Yq[lnjΜ9Ǐo۶/h}z֭[bΜ9#^#F0X:{ipwv䞗ck/(]tTT!;oTD #,^ŠE >7nܳg8dɒ]tYn /<i&O=i֬FCnNTZZZӦMT2o޼'NlذA]Tfg[<%"Ղ&Gpc-  A~K=[>%5jԵkWe޽m3gNPPaþ 3qD[y2D k֬٬Y,6J*}k o4jժ%J,]Tz .Z\9g} w=42me0ˎؾȐveq}ʏ=1X LN^8ĥ]vb\ Q^}:_/8`0~VA;[ fg[<%"6q]k˴b{M} ubdZ̅ -nb֭۰aڵk}=ZYa~˴!{~Gǀ w/[l:tHc-K/;;;JfZCkq[qϫ4)Qʕ+{-n{%V7ܫOV#cމ#ΤA̿+) V.bxk׮EJ{4|%cl\)h<Kq2 VOX4hZ fg[<%VZLq]Xot9 kP'F&E\-p [(iD|LdѢE)S̚5K̘1C6 zO?Ti;p@o)HR DJUVV-6i$1cl\)(,_\y_;iCnNɔVT:uZ fg[<%WIqNi= _o 9 kP'F&E\-p [(iDMoٲEţF>ҥK+T`o߾uaaaǏA%eIrr(xbbbE.]]V-WWW刃 Wޭ[5G}m۶AAAэ<==E+00P,QD||n|Ų?#Eŋ2X9{iJv`rub/_Ä,^ŠnѢԩSű*Vd^ǘѱxubt=M?.:iٲO䊾[iFCM—m7 9 kP'F&E\-p [(3ό;2<qDc`Æ իW5;u$:Q[R'<Rӹsg{챒%K*1ohUʕSnQ4f͚U| hh-nVظ A-0ay)_Nh٧V/ភckǐ-ۍYX >7n^B: ,GQͯ`Bְabٚ4irINԙ3gHPUfHwbo-anX.[>[iWRo)G`-  VBB#?0y܏+v+~ Ǣd\\G'T߳g?ȭ[D4bK.)yÔWF]~=--Mb9x9g٧V_Nݟ#F^xRZPkOovZ:xi(9"7ovy3f 2dAAAr1'jgC*D\DzxZ>hŋS</~y5mɒ/>]xQۊT~+)2&w-loNf@}iݺ~|nui"un@} ubdZ̅(VűvXd^y`FFyvmѴCH5s$W=% [t jgڲģcKmoZDzxZ6mgnZ׷ >nޕ9i9m1--ͷ<(Iyr{Ǒq/ګ=W{vy([OB"6:yL8VR\y %G`-  QhNNN../)&}ӦOVRyҤƢ}W7-_JN?uu*QxZ3c?J3\EogH>~Gq2Pjڶm:}Hʕ~-*/?> EIo*~MV~HN1౜yw~kij[Gܴi#*V,_t)p4رo*MwUsOZOTͼœݡ&9yD[l(}pY[|^ʕ+T1n%{Q;CU~qMJuŢ+^l?3M[;ZnRb;w*Eq{,y5kV?Hz4vmxt3ohdlwhIN*U_wsegD{淬u5bD1c7^\Eç5_^sv'ֶي_3ɿ K}[@sdӿ^y/< imgϾ߹seK>Xe/ sey?GSʊ_'/^mtO^pvиqu7y8ۛ*Lq3r"NL@[Zkqef) %KY_/*U^Zx)7סC3c~iժq`KR.7Q|V^Z;R3Wg},V&%Ps&M D+Ӑ6eQ-eۺ|,gf'T?-h+Wj%ni[OZ%b*m|D[kTtU⩩ď-k]n^P^֨Q}r҉%FϰImjW >u_;酃nc)-%er]\j?tf5jTPӧ-Q3g\+U*]xÆFzNxF^y{\:VVT+^iWҥKܺQrM4n]?~bj0V/Y/qwۼCZպlo_Lq#o UbdZ̅(/@ L2/R6~PTV) 7C6n\Ol+S1nԪU־X6!Yo,l6mZX\5/o|~ @D CMnh۸k~,gf緪V,P!ּyC3#.3-g*m|D[kgƳ߲UfW._ըQuk+?嗣K#Lck[n3L,É=ԿYOK1{__^ڳgpjw zuoNZv{Y=07MD@QmР#n׮u,cl¬7TVmpbbַ,~UrJ*qۻdɒUVsA9r"NL@[:{GhȐEfmrgʶbWg 7Gk֬VF}$!99+=d_x~b&-YxG6:YQu5\Pn[w⧻o=*wvye߰>Gqy[+W'\O?YG):q=QTs2VKJT'R߷ohu8)eܟ7ϥ@[K.5cǎEGGA,\{k9 kP'F&E\-pBol艒%K8޼42ի[wo'տr_ĕ2',lM4}CH5srVbfmQ2o:3ԣBkMX4oedklR%\iׯK!a7sV[6NZ6~D[ktRcǾ|yqf;e+/pK|w*JRK\J(.~~YfGI\\իWÇLy]_hҦwFl5^^gs 8ȝgǙ~o[W)2ťvӦ*zƸ2~HKL>"^z۷ubcF~ST^jՊ+GlI?oNtc|˫>ϽY++*~&5iR^*~~&s\֧Mk[ZGXѳɗ &lޝUUWsH4JkZZpu9L˺_,eiYjY,3s D%'g EqDfdO9gw\8TMH($6zUQQ=zG+V,o+7xEfoF檢(SUg/^'-Y$66V~%$i{*Ww 5љ0 o[yS:sK.]Mz=j׮QӦ 2Сɿ?e^"ʕm߾SOL\֭[2eʈϥÆ"cq/]FcxT))*ۜ3g~%x ]Eɿ| 0RҶ'w|ޖwߚO^o?-.6v OmS\Rnmqv3f|Y/ּy;m羽s6Oz9ߢ=pٲ5j\%UT^Z|HNSEQl{wܹeP0'gm|yg($6O%onM bԳo-&}*UU^:}z'ʖ-9O{+T^o+:e˞]8٠AQ8W6_N}͚g׿T;מּvsNi ^⋶>>ywEoY^+ T5oKXL@G\| J-gKHXrB9-=}ΝR山W} JJn:qG~~~A__E6깱(IIe676>J "22RWhh4;g*x⹋W@~QJȖa?m\Z$Dg:b[P27bāË+ȷ;lV%EHJJ,֭wnLZ,Ww 5љ0 KܬeU;o`5}>9HtȅȷdؒoeL\6XʧGM;XL@G\| J-VN2[[kuO'Ww 5љ0 '=z"-\_\o`crpkP 舑 o@XoRK6!lQ>\Z$Dg:b[P2iĈ@CZr&}_hhhDDDll@sQ/rpkP 舑 o@ D2a\[...NE4"irpkP 舑 o@ɋ  f"ҍM*W!~+~ h.UEBMt&#F. @%/::zݺu&/(|j c?\#KJJ@s[-x kP 舑 o@KHH6%wl.W!~+~IIIhnsv ;XL@G\| ifeU#zØ*"&:# kZf9Yrz~t HtȅȷV90g>,Ww 5љ0]6ȔFX]?oKXL@G\| ஀-r3*V Htȅȷ ";| iSCv!Ww 5љ0]>oSxrB*"&:# +Rt `O{L Htȅȷ:oJFx?oKXL@G\| [XN%Ww 5љ0][fU#,n/*"&:# +VY'ɷ;*"&:# | lL"߂ 5љ0]AUZg%F=;XL@G\| ஠*3R*~2`ϔ9rpkP 舑 oE 7W`-j31ra-~B`e< Htȅȷ 0ղO/a-j31ra-ȷu~srpkP 舑 oE 7M[ Ww 5љ0][`K;/ \`-j31ra-+:*WcA *"&:# | lO_$Ww 5љ0]g-`%^;X`-j31ra-|>&W!K*"&:# +b `=0c\Z$Dg:b[wM<'| iq>q["&:# kZf9YrL`-j31ra-k-W+q~;XL@G\| ன4ΑFXԦ*"&:# kjƧss*u0| ^Z$Dg:b[w1a;o!Ww0L@G\| P̒*MZ CsVUEBMt&#F. @(fGM9xMՂ&Q{.K殔;XL@G\| Pҏ}~ĕ?a{{(4мUrpkP 舑 oߒNTlD\΄I\V٪>قf/'߂ 5љ0%n%I[YF\8nUh_QXP-hBH Htȅȷ۴ ͂lw&oj&&LJGUEBMt&#F. @pŁeVi#&Lb~U{g~/ώnc-j31ra-[_u旷6Lwo~g9 xkP 舑 oܲuҍĄ)䚎ϔwo?=d\Z$Dg:b[gTh.&LW/.˻7=,]'Ww 5љ0@!։'PB|-&L3$?`y&az Htȅ ɷ?JP./UVL*+/̽ɷ%EBMt&#F. 1/z\tG&߲ܻ{$, Ww 5љ0|_UsSG#6UEBMt&#F. @WՈ####`9 ˜]w}'裏cz!w}|ǩ ^B|c޳gO߾}#^R=w?ox kP 舑 o>Sƾp^z -[UtձcGqRJ=X 'xӾ}{__6mڈb:u7no}b?,SLY*O?SDQ/*x999Ι *gsW^Y\9'K?* ĭ{N\3g믿1_uU!C3yȷvkkɷ%EBMt&#F. @sGy$,,lN2p@go֘1ceyTLQŅK/4?qܹW_mժU:uo97/[l<]vYPJO?ˇ*'>>;r$}jժ5h@\7o.A/NgL\YQsrr7n,*W_}СC;w?!)OVwtזWǏOJJ믿 *x/1WaÆ}>aÆĉ]ս{:vKߥKz]KTđkڴU~}qڷo/UVnݺ-[Iɓ';gW %v|tw|sc-NK/yVzfO9''GGN:mܸ^z"߲Ǭy\Z$Dg:b[sHMMݵkW?/--mΝNʯ>|8%%ŹYs2|111"Nx.Ngw-^B=fHҖrpkP 舑 o@1#߲N+Ww 5љ03-{̬9i[\Z$Dg:b[PȷUEBMt&#F. @Ō|JޱW`-j31ra-(f[}rpkP 舑 o@1#߲nj:ܹ_`-j31ra-(f[qmǓ1["&:# bFe5;u@`-j31ra-(f[恔qrpkP 舑 o@1#߲GH)*"&:# dML=^Fb;aVl{J'O]9$?2Ae꾧2큗 5љ0($7' Fl}0,DeUۦ%UL&:# 32OX@o6g>pT}AUZmQ>g~jؘq~?z[Jӹp&L o#芶ɷ%L&:# `촌Ö6`[_u{~e-ã:/`UL"߲Gm;ZCMt&#F. @X'En3)lV<9xAf8\u]jTO~˾1 '0aX|AUZ;ZCMt&#F. @99IbBn|rx)oetc6 !vbX²o 1aX|bΙv\j31ra-:zl]?ev[e̺N1Q|K:0,De-ӏoKZCMt&#F. @@Ib-euKg>//J)`=+ˮ5L&:# cIQ;|a?s߬,{0ik| B[ՌrpSktȅȷ#Bn_L"߲F٧*P 舑 o8kosx^AUZ/}uǗ.&ȷ!:٩rpSktȅȷ"h)sV>=4F[{}+2 `!-[ɷ'[ZCMt&#F. @࿎Fl4xFUi{~>_ń `!-K䈩NN:5Dg:b[r22[3VAY.7+[>XL"߲Dnv^)j31ra-RY){x*c˰Zp &ȷ,}&̒w`j 5љ0`Si{u@@&L oY";-/~L@G\| 0JZmMgȦ!?ܹ_>:&L oY";5ݿt# 5Dg:b[ rsr^{ӯl҈G% B[>6Lc 5Dg:b[N=iZoB?#Ƅ `!-KdN| j31ra-@Oq~_ұ_HuuNܼK>P&ȷ,u2uj&rp SktȅȷdO>jʬoɿddGa%2Rk*W0L@G\| Ɖ ;^1jە|p,jPmǏo޼YO7T|'o&W6.L@G\| 9+Z롭pB Ͷ رchnn\U5EBMt&#F. @"x!?W]ܰ1݀ ^lY__ߠM6n ޜ ⶥK~g͚Ұavڭ^o߾\s_`͛7/<<;j֬9d<^ZqgDoK#Rbʻ[-ZGQbEqaʕgرΑ}Ugvb q|iӦׯ_z0n8瘂_WhUI^{(ް{?[JJ;se˞n~<^BEEȷ,~x`rp kP 舑 o%)TږW]˻#[}q*Gv-f%y?sntgڵ1̷ =Ĩnݺb׃>xVBoXĽ4fq5j7w-O7T?}/iϢ"=[H;|<ʙ_Htȅȷ]Ќk;xr]݀Aȷ.]*>q&NǏ/FDDy?KK?c6n(AAAo&?Yԟ|ɂ%K*U8pSiҤI=˅ް{'>UK+Y}oyx)g? OEKȷ,,6rp kP 舑 o^yn~'6)"JOO馛7oe˖7n[nł7?ʺ뮻֭;w\q޽{Eo8'LPR@kڴ87E'*$?}^z9SOթSĉnXĽ ŐK#/"n~ΗgQh*-K:tE[ Htȅȷ:q?zo%|`>>7hРT nذ! Vmٲ8tUV1c(UXQoz+?{W.l׮8ozoQ\PB:Qve:}u {߿VVVV۶mEJ*ÇO[o-↞|) }"߲DUɷ=EBMt&#F. @xI#+{τȑ#GEJJJ:x`JFFF\\Vffc 쓜8qB VN}߾}+g0ދpᬬ,q!999^|}*M &)ȷ,zU5ޟZtȅȷwŅ.y#Rޟ0^zr/"߲ĩ_8t&#F. @%5.ag#0aX|W󕫀kZCMt&#F. @ 7w@MC~Ȕa%R Naj 5љ0P7Z^N޾GX B[H=R\\j31ra-fl1!q3}Ř0,Deq!< W0L@G\| ('w=&ȷ,qrׁ5;U5L&:# oBF `%-K?Nrp Sktȅȷ$5.a<r~y%p.L"߲D]n"W0L@G\| 8⟬.&ȷ,)f-[P 舑 oE9bS5N w8oL"߲DƘuU5L&:# <:<".&ȷ,qbukZCMt&#F. @nϤk8rb%N]*P 舑 o>jN+p0,DevMaj 5љ0 :b7j# B[8f{Xrp Sktȅȷ_fCG.&ȷ,q|<&W0L@G\| mN]ԃ &ȷ,q,jo?.W0L@G\| ' bƄ `!-K[P 舑 ogD0NW- L"߲ıU\\j31ra-3{P80aX|GWnI 5Dg:b[]²!}O!PL0,De+6͹) 5Dg:b[ZؐkJ0aX|G#6ν\\j31ra-+5.a &P0,DeCaj 5љ0,1n@qc%~_?ާ*P 舑 oR/ _@ L"߲D²\\j31ra-(flo :*&L oYuSaj 5љ0st `!-KYv~g*P 舑 o.iz(n2y0aX|G9 5Dg:b[ηr0,DeËo\\j31ra-X$fl{̒wp&ȷ,qxM_kZCMt&#F. @[$G 更-VeL"߲D|xԂf/U5L&:# ߂r7z6ic%D.hNaj 5љ0qK:v:7W}L"߲ġy[$W0L@G\| K޾'S B[84we}/U5L&:# ߂}e_*oa%Y޲\\j31ra-n9w=#-L"߲""߂0L@G\| &LJq] B[8|aU5L&:# ߂־6*rw1aX|g/_Ԧ\\j31ra-+9z_gMw.&L oY"nھ"W0L@G\| ƊxlЖ?U^DŽ `!-K\} 5Dg:b[0Ӊ3jvJIw:&L oY".tU5L&:# ߂>Ǝ* `!-Ktrp Sktȅȷ`g)P0,De!KtxMaj 5љ0 ]?U% B[8x*P 舑 o4gur2J&ȷ,%ɷ=L&:# ߂iVp r@a%-\u 5Dg:b[0Jڡӫ8$Pr0,De >\\j31ra-e౫=R(QL"߲ľ)W0L@G\| N!yyJ[4a`$-K웶`Yg-xkP 舑 o{&krm55W ND]Ϳ ȷ,޲ߒkXL@G\| Xᵽ~s*K?r<6Wi~W/P Ee[H Htȅȷ`CGWm}*M$,lʴ͜˙0E4"BGȇ`"-K{rp kP 舑 oۿ[C $nKYF\zi%mD[3e]ɷ=EBMt&#F. @C̽G|x\Prn&J[m,y|"߲Ğɿ-t\\Z$Dg:b[0A]ɑw(9i[>?|"߲ĞIa Htȅȷ`-~Zh MosoY"vbnU5EBMt&#F. @o<(hȉ –v)\[evDArp kP 舑 oA{G.o)Pr3.m!&L3jv|fE;H.HLL?|{rp kP 舑 oA{+{ ~\}.Jb$+0:ȷ,{O/Wװ 5љ0RCy/C1 =,Be?xr\\Z$Dg:b[[⦘u]`3>-K7cSC*"&:# ߂Ŀ'W"Wa%޻fΧ####/"߲ĮCV>=Ta-j31ra--UX.)[...N>"-K!x3U5EBMt&#F. @͹cQ[*{9t_ƍ+UWUÕYYhЈX ȷ,36xU*"&:# ߂ƲNVn%xٳKݻww)KWKvvv\\ѣGvw~(z1O_,Htȅȷ#,h\xV.](/OMMͯ{'С8'|",'-]ЪU5EBMt&#F. @mo*l{>\\.]\~&N|+55uիXbJKKt萳+>>>㝓qw}qnx7Ι8^:O%6qy\sMVV3L2T^rB_S%zHtȅȷ-r5ӼwYgqrtѣK} @˗ &8*WNLjժgϞ<[_}Zt^,\0TΙo95?(*o?"N?Vs_~3 2eʄ oVgKES8| x/d P =i_G\\Z$Dg:b[U⦘*,{m|||ZhOaaaʕmr9s8Ȗ-[Hʕ+w9ɓcTM6w!.WPթS'q 87 |?,.έ.4:ݥd*-`bZy:CLLS_z:~x9-O',5wS%z;xkP 舑 oAW/Z_&w?_`K.C>+)x5;;W֭;rz-Z+ 6쥗^ U߿VZNa>L\YQsrr7n,*W_}СC;wXݻ;7{oG,uϢaÆ}p@ajժy_fح[kQrp kP 舑 oAW94̟wz>5-xkP 舑 oAWsq|v 6#KjIKl9qm/*"&:# ߂f\x%@5%~˺GU5EBMt&#F. @]Lbwz[?/*"&:# ߂_.x\x%@5%ͯ*"&:# ߂+JI]`3Ė?| Z$Dg:b[մr2*,{،,'-Ga-j31ra-ʿt#6#KjIKlkXL@G\| -޻fd P =i0 CMt&#F. @] ]b?nx[ 5Dg:b[oޥ,F+/ KjIKlwU5L&:# ߂x .e1ZxYTCOZb}'W0L@G\| -޻hd P =iMrp Sktȅȷ+ނ!KYV^@Г+W0L@G\| -޻hd P =i4| j31ra-芷`HxR%@5%6;fӐ*P 舑 oAWCxqM"]"(R*]D0D)"EE' *^.:BI !]=g{ټgfvf򞗝},KYV>@/!'-b?1A^Kkt̅ ߂(Plr"y(5L@G\- KYV>@/!'-b-/&23&@ CBRKjI~]*G5Df:boAW`H]bz P 9iCbP 舙 ]Q!v) %@5EDaחQkxi 50[%j|^TCNZĶA?zP 舙 ]Q!v) %@5El{o]^Kkt̅ ߂(Plr";z7S(5L@G\- KYV>@/!'-b;9U^Kkt̅ ߂(Plr"=jϷQkxi 50[%j|^TCNZַF|P 舙 ]Q!v) %@5ElQ(5L@G\- KYV>@/!'-b#~?]^Kkt̅ ߂(Plr"6o 9 x /&23&@ CBRKjI}c(5L@G\- KYV>@/!'-bS-/&23&@ CBRKjI?Βj"31sa+J0$.e1[Mr^ZCMd&#f.LtE ڥ,f+ՐW~5Df:boAW`H]bz P 9i_9r^ZCMd&#f.LtE ڥ,f+Ր+G5Df:boAW`H]bz P 9iz}~?(5L@G\- KYV>@/!'-bCj"31sa+J0$.e1[ ~v;L@G\- KYV>@/!'-"ǧ&Qkxi 50[%j|^TCNZDT&Qkxi 50[%j|^TCNZDT j"31sa+J0$.e1[]4_^Kkt̅ ߂(Plr"ֿI09 x /&23&@ CBRKjIX1/j"31sa+J0$.e1[ȗLY(G5Df:boAW`H]bz P 9iJ Kkt̅ ߂(Plr"u(5L@G\- KYV>@/!'-b݋X^Kkt̅ ߂(Plr"ֽ0%r^ZCMd&#f.LtE ڥ,f+Ր㿏X*G5Df:boAW`H]bz P 9ik@[^ZCMd&#f.LtE ڥ,f+Ր\&G5Df:boAW`H]bz P 9ikrlr9 x /&23&@ CBRKjIXc(5L@G\- KYV>@/!'-bMA笐j"31sa+J0$.e1[[)G5Df:boAW`H]8'GiJ/%@5ED{D?;CMd&#f.LtE ڥŭbq^TCNZD^-GFj"31sa+J0$.u%+#, ߪO%@5ExDHat̅ ߂(PIś-.c~M6J^OJ Í0Df:boAW`H]J9asJ5[E< r~w*z P 9i۾;at̅ ߂(PT3gfn4LK1[gxtaΫzG^ Umފ [+GFj"31sa+J0$.LYXYŚ^olN/ ^TCNZĪ'\N^Í0Df:boAW`H] {S 1U޴Ϣ%@5El5B[n&23&@ CBRЎe {4_C/!'-be7N.p# 50[%j.*$잎!UcyO 8xat̅ ߂(v'&Yò~ 7s!%=邜^r^TCNZĊO-p# 50[%Ԯ3+:U[r"V< r@n$23&@ Ce@w,eN/(GFj"31sa+J0$~]F@m+ Kޖ+o׏^TCNZDc}Oop# 50[%.[,׿~ ߸qTp-r"›~ff9 x 7P 舙 ]Q!KrߚȠm۶ʩZ%@5E,oG p# 50[%.[,׿)C>}zhhhdddLLj^TCNZ&}#%n&23&@ CE{7@A/!'-byVop# 50[%.[,׿ezElp# 50[%.[,׿e {ůp# 50[%.[,׿e zƯFj"31sa+J0$~]X z P 9iKnat̅ ߂(vb/%@5E,}ճ;(5CMd&#f.LtE /X~o}9孷̝;9LJa z P 9iKlN9 x 7P 舙 ]Q!KIqtfiF8ǽ+Vt.>>l - _KjIXpsowL@G\- _jWN[ Ԯ[>yaqH"o^TCNZĒm%GFj"31sa+J0$~]o=\[Z!+kX7zX3;tzrn9#-=蹿r[a{E8o۴[^Í0Df:boAW`HRrodѢE]D$55_ʕLrFfc^{WƎwꕆ)"&h۶7Xb|{ュT/5PZچ!Cz'|衚KoaȱQ=cǖb> gwy"*(zۊ]%F˖Rb"E o|7_2ǯjvy<+py0[@Ր+ QkL@G\- _jW[kV~ͪU+눰"ҫ346i%U\~О+WN_~s|׹skqG,%WVz=#bo>S>.\G&|R} r!xpw2]ҥK)S7:B+1~HMÆ!-O]ν:47:LlOC:x8W`-|A/!'-bq앣p# 50[%Ԯ\l+v'O.+X{u5~S68mϞy4uDv踈WrѣzfÆڮ;4gϮ,Pؿ!cV{졔HgtH'"vRp OX"ǞW)RKj雊-ⲿ=\^cx'ty0[@Ր. [owL@G\- _jNjR:uˍ7jƐ7ZFMŀ~eXvH;B &&F\9Al>z`s~fjnj#;ǝ oTk&o|j-Ar<WXo<w - _KjIXTK}rn&23&@ Cu=e#Kd,S~a IM͛?|-e{q:rCEy/V/^x~l׿㎪⁇!ΜMzGbqRZdxzXRup0/[o^TCNZZvp# 50[%Ԯ6|ߊYP~Mq-[>j^)\)RV4n\q']2`@49ܡ"ּ㎪b RCN-k۶o,9j VGV; ,(֩Sޠ݉8R15*U*/"7Tj„Ow =MjԸ]oJH?0ux衚.qwB r"씴at̅ ߂(v夿;(%%cZچBt\V@>?tĒΟ_#9WCEسsܾx8wxÇ799.Z\`R0]+pw0 - _KjIXpߋwp# 50[%Ԯ8][@Ր wQkL@G\- _j?3dH8[@ՐގQkL@G\- _j-[@Րv{QkL@G\- _j-_o^TCNZD';*GFj"31sa+J0$~]X z P 9iaw=at̅ ߂(vb/%@5E̿ QkL@G\- _j-_o^TCNZ;p|at̅ ߂(vb/%@5E̿ÅC'(5CMd&#f.LtE //|A/!'-")c(5CMd&#f.LtE //|A/!'-"v)1'(5CMd&#f.LtE /o+u_N.v"w}9y,'¦8_DjO9%GFj"31sa+J0$~]F[uՐR5Q[@n$23&@ Cڕt!nc 2Kiq[@`&ElXP <˩/3ֶEl0BKr(~:w5qݣ nZWv켭CCC###cbbTp-r"B\<~F^Í0Df:boAW`H^ÃO|{(XnK&SOʿNe[(У찿\~SBz:ұj8Sڼ>~9Ik+CdE۶mk\Ր\qrn&23&@ CڵqY*6}8{&PF3{~}6?UǮr)k~$Ga-sy3rj#Gdp-r"+Mn7P 舙 ]Q!{ڿm"##Õ,8,Yٷ==d31fUx2|8xͭ\.QX:ibMN z8CF/_. 9"Dƞ?^N/B/!'-"bԓ;CMd&#f.LtE +>>>666&&fڶ2j79V-HزaEf@/!'-"֗Np# 50[%jgX֠w.I0"+[YA}\񊤝+?u"xuչEyKK̋|ṽBDuq!ZՐ1Wp# 50[%jG. fw<}cp[G.*y47ux^V>˸RWg-Gz2>ǧ;"y %@5E)xz9 x 7P 舙 ]Q!v )c}%ǧ.clbߵedCܦK&GtvKcy> [cD/!'-b΍5q# 50[%je+ly\ޤOfj<&+JʖuhbHp6G/!'-bvǤ4at̅ ߂(Xvm~p"=c}ټ7|5*RC\z P 9iK4k;KCMd&#f.LtE ʵkz.yQy37-%q9˛Y֠g=5KjI]iF -7P 舙 ]Q!fedn|˥q9ỵᛂ+M_21Ć|3rjnF\\O ߄=r"fiyat̅ ߂(Xv%=vs>UԴDyWZrTC=#+Oz P 9i3 7J|at̅ ߂(Xv\aز>~TO:s#QmĜxvw^+r"fl!GFj"31sa+J0$V]kCElr9 ku,6c/qͥ']<`dp姎L[, z P 9iP0Df:boAW`HL_"lNy N.\R% p< {9j ⷳ+z/Ր!JlY;CMd&#f.LtE ܵ+.b;IغWȁۜ]<'ZyrQ5 [F&Uhsp|<%@5%lWJl69x 7P 舙 ]Q!1q[59pL;\}:yr\:Aϕ-H9)z P 9i,JcDf:boAW`HZVm t=@dg?O~I?Y-3kϷUho݀KjI+h GoFj"31sa+J0$]qW~*o߹%l0rMA5WL]I%;I^8x\%@522gl(GoFj"31sa+J0$]g#wWlMșC~>E7qF˛e7&0BOՐV1-7P 舙 ]Q!1YJ\%Q@į.6OLPţBfwЉ[֨w#@/!' r(MCMd&#f.LtE L+y笐r&D\HO.\'5XӬ yl?ϾA\@ՐVvyVr&n&23&@ 2JN<)᪄;M6tWijgI停JA_OT"N0p#›[xGOcKgm"GoHtw;UlWw-(k8p<Ə/f݆0GJO\X9"; o-r2lYY{FN ԅՐVyҬbM(M 5TF sީR`{`w_"-ʂ WQܕkr"LPϧ,yrluͼxIPLT1$F_T˺N^NHoT~/VFf:RjJo{6iw׃Ntmz0;/1W߂o9׬pN֓vyۻC?4lp^TCNZAƅ%Qz/#3y9/kzmO5y8ewlz>ϕ|yteoРA*U[ˍ?AŊSβeˌ`XXXf͊-ZZ}wer+G۷6lX.]J*u#GnݺxwqGPPѣG[jUDZjk5j0nӦMGc1c4oxr'·:dq`K;:u ^pYR] sr)]0_h]lYkmYYX\>w~b篑thbȆ^Q:)[y< %@5d$_]19 x"?"33om>o%4~t^S[]nΤ۞b?b5cǏ?seرcl__.1bŽ\n۸qc0IDATo7mzݽwvگzϞ=˔)Sz~MŮg_woGvx.Oں{;Ia 'NԩS Ҳr%۹8g+{pmڴ;w>*ʓ1_ϙ3g׮]*hBD233|۷oLL-III{38o%OW@ݻ'͛|ʕ+g_Bׯ__tpQzEJ*Ç7#eAVVN\ >`@@ƍϜ9rC4m.K,\ֵkCV?NVz`oAWܬYt*Q:(KξZnV*&8sJ"m۶׋"̺uDm,&N9~(eNg3]nٝ9 ųsp)\`~ѷvpܲK~DzFmW!BcaBr.O{uf<޹sXg޼yǏw߉~ѷs&]+d]][]X_Z;I{F rѣGۧqW^R>\QHvq˭9>]``ؕX͙3ǘ[n5ֹfNvgw|6tvs.BڵY kv|6e3vS²..4.;+/^TCNZAzbܛ\{"@MdWݽײeVZ/_~+=ݚ{saw&ZOe#i&ٳv=w\BM3 >[n:t+WPCH=l6ikB¤VNvKp.i ;~cٔ*UjСVvOgt'LŽ;DpܹƏ?UR?4⯼緤Um{i]<;|Ou|[ۥ{kJrR5K]_L prQ<=z P 9i.Df:r~s[|ӦM+P_|QdIFܭ)C7td?vODDXʔ)F|ҤI+o|[ؑm5kϊMo.RFkr.O{whl/ǡx"E:%=Z<<'f_첝~vbb}Ke+';[-ʹۧAPP`16mZPT1;_Z5ǯ3JZ?3ZF%KEGG=zTo6u&O\T){Q{7n,V{Do܉CڵޛnCn4m.~K,\vlmFF[\Ķ'$_r .;?LK9 x]t˷DҥKeʔ/n{yM⼡w&t܏]w%ܵkx s=wz|[ؑm( ,(~؍ ]){ԑaZn*{b'O̾YժU&p]K_Nʕ+O2ӡ/^v{F 2J>o1cƔ,Y2iٲHn/5k&%%y (VؓO>we_3͛7ݖ-[6$$DO.f}݃51ZbEfVN˝;opFyCه \Rݝ ůy)\`~ѫv]Ej"39->#޿ܸq5e;mImODGG?7\Un۷qo KmȠAy䑊+0]Nծ]E*T|{e_Ɋ+Vkܝk{ͳs8p@\7Vz;w<=]]ә3gDϟ?o]rgb5nwl?~95wPŮ;fٮ6?.O݆ΜY ^?jWfjڒyn9vrmwWls"$BOr k%Go0d&#3->wr#L:(..ٳr4gg[gΧ|N.]|}V3`%r .=zk;9 x7P 舙 ]Q!Qve^z[uHw4B ;j3(dȩ!UVmˠՐVrThr&n&23&@ Cfȗܰee-ofKܶoq(7&P^TCNZAJ:QL@G\- +D\-Srϳ7~:B:d[P|&fG/!' poL@G\- kë͍q?<`s˴|uLM6|=/:+F/!'¡xFč0Df:boAW`HT]&|<k{۠䨉,5a^9_!σ`^r .<>g(MCMd&#f.LtE DڵW~2^y+Cϸ*ȚgB"? \\E`Rr .8vsr&n&23&@ CTJ=?\{RComg϶wG5M"R5ϳ%@5$?vr&n&23&@ CT[)Gs#z؈ߕ-o~'GR.weecKjI+Hw4[)n&23&@ CNJ\G;'fOLDHDDAr*=Vu6ߺ%@5{ (GoFj"31sa+J0$Ԯ#,Z-9+[ d9jF=E"227jq'1,%@5 r&n&23&@ CNZѢ9yެcu9]0)sJ?.G{#z{%@5f'9 x7P 舙 ]Q!Qv]8x'Gs&1SQefAe*lf-%]: ՐVez](MCMd&#f.LtE kaN6햣9pjI֖e@ [;FkNήS^oL^L9)ՐVyϒ(MCMd&#f.LtE Ŀ!Um6y >جr2›>x+[ɧÓ_]@r mڽ![)n&23&@ Cڵ})GsD+oc搸mܛ3z31ܱ!Um)ʣՐVpn%w7q# 50[%֮U<^P95gVnQmY歙ljKzɅ+?E ڡՐV \ȫr&n&23&@ Cڕ6Rk9x[gVn eW,y[VmZuS\y>9_%@5ڹўr@n$23&@ Cu:|&}h7wdb9MFkά TjAgpzƐjO'l#ՐVpv߂Oq# 50[%?֮?w?k_=mY73+m,ZNTK'Z}–$z P 9ig#w,kKč0Df:boAW`HXV~lz P 9imX9 x7P 舙 ]Q!K>"WlpaNg#wQaIz*^}1r;FuFMs^߾lhFuyX+:xpw!-d6ۆW?[;YuKHHعsu7=O'O$;ՐVpj醕QL@G\- _jWxVmnܱf'9[9o5jT*O8WgʗY\%KƉy[bZ#6~xu6-qSk׮=puQ9?r N-Z 7P 舙 ]Q!K SrԍFzջxOCŅΫKVV:ƍM,C cfPZoQG>97\%}@z^4is<u`qrqjUߔ7q# 50[%׮ś2WlYYp<[yo\9A\*s`6 *TZdgkĈ~b'Ηv>i҈m|+>=~Rٲ7UT^ċ)loby2iW^iО"կ \Lwy9#cft̎[*UB }.['. n?U9vS5%6tw5ѣy=;Wrs](Xi\[r&n&23&@ Cu񰻞nĭڲ~79syo6M}kωxnO;ߋɟQ%-sb#G>}/Rp/~Ӱb0$Db+:ʕlW\G} ,8n܇ƚѿ:B˅b.T 8p<ޠAbŊթSgٲeFѣZ*QDZ^x{;]}Æ ҥKR>|ȑ֭[/^; 2֬YEVVm߾}gk:y}DNnȐ!.]FSNǏ?seرcll_# TRu._܈ܡ֮]ΛVժ>W-}u䲂 ֨qBDĤE+{v\'v#Eҥ1Ihby]}ƒ%o:80fbS6W\JI4~tlm\̙k׮Mh"pׯ_vKk[N.}Q@ݻԭ[UV7o~7+W,V̬PB߾}cbb/_.(۞ה6lذem?"rC'nw|0 `ƍgΜG%6UVpppPPx;ng_=… ifܹ>hƍJnsel VvvFFFzʕ+7jԨݻw0ַ]W@Tqx YC=tx-+8`ޑ7q# 50[%׮#S:\b w쫭m׋_n:jUV}7nh}om.הS@3fǃ~m9ٽ|hr/rÕO^ng_=f͚_}U%=:o򼌕đ,Re΍W>i$ݻwō [Vvu-7P 舙 ]Q!}7zƶwGQWF/q7-} իWZBsCz2uܸod8xkڛXgto֥K?2SΜ8/cAVN~z]3RƍW]$JJwub1Z[n5Vsųn{{osɾQj׮]\\ˠݚ{Zl٪Uˏ;݆Nܑcϖ-[7m$~={}eA=hPyءs׼ vaaaRe΍~ac0_r&n&23&@ Cڵq,G]ruĒ ԭ[ 3oƚ58{vBphkOc%'-Zțodv… M,Cg?6v8nKfW^ (T㱹 J Ͻԉ?-;vܹsu~sų0>3aGGFTRC۞ݭ)sM&R/(Y ]n5jԨ[/""B3eI&###AaϛfstAĞÆ .[.wxo߲ЈvQL@G\- k>_,G]Yȫq WK, .xέW8}AA;>h"MKMrÆ SV}v ۦM#xP)Soޞ8qwT_*aHoG :֭l|++kˀBC8xpxފ]eqW JoCC7((H#""6mZPdďJ*uwqǶM5jXdIZZZttѣGSSS'LpرjժO2(۞])MtR2eYSd?qG]v{c]w%6ٵkIÆ 㻾]| *;t|^w;/w-~:u*WvU֝֩sэst9aw=wEU-[ۼ/,Yd͚5E|%J{Ȑ!v>owӼtYWlِ;SXX'|ԩ+_ 2(۞]7brwy.\HVtt Ґr(%B F oXa sב-ӂ&-H6R-,oUYʯC%o' ƯNX.Ob ` ?w) ʣQ=F^JQA~nCԲ]>}7r{v4 `I>gF.H[ VԼFk*;ޯ [ u;j߄}, $;X.Ob ` =wA=~Ш u [*Ih@}C.x>)ɇ?\@ az&a b)+]eN-Ѩ ?nn{A~\wieWp1+7r|G?(%B F oXa sW\EwiTɷg QA~붗t/{vgF.H[ VUH%jWҸ uۋ}v{[sv1s]TUTs 5*`O4 PKޠOA(%B F oXa sW~hMIШM!uۋF'{Y8}O".3yFY^yWwz\}R;O…0 =@0rA 箜s{Nњ2<O=Fm ^[)^Zo7qKlsʏ^NtieW@ r I9q|: 'L1 @~ S0VDF5nr8?s5[o Ww*&-?hOv崪$8'ʲ ZpǐKޠO}F, 'L1 @~ S0V2O٬3Vm>H6%b֯뀥N ;itUE|[S{⍉6*NWЪ8nI4 pǐKޠOF, 'L1 @~ S0V{4Fk?#+FmJoa6[Tg#.wf8et𔕉{ǧЪU]\v)N A}G}F,%L1 @~ S0V2;=x.iDUYBEV[ۻ$&&[GQWw[Mya1jVܠ(Ew3rh@.x>){ip! |B#\@0a+oh&}Ji>>>...{ltN]xxxZZ=w4%3^Ԣ?%џx{_(@.x>){<',QK…0 =@0rA 473K:JQN;uqqq֓""o>$֋{M 2ZUJÅ.7r=ip! |B#\@0a+ɳ#QWR<I< N;uiiiZ@uIYg`[{Nvl۫ĈTЪbSx9ѥ~{ ,  $4-*\3#$-+L@XyJqp>IvvvZZZbbb.vة+,,7*K3.,kʡ<_47?b79x,@.x>) MZN az&a b)+]gG|G:R]N>FꉲwE;|zO#<jɷgƬ \/4 `I>gF.H[ Vܕ~t*he}B..wS^'F~%3PYZN8>ť]IB-#7rp+hp! |B#\@0a+X!_Ѩ}Gύ]L,<tOiN;=jٮjZ?5hr I9:k p4).׀~x?zCXW[GHfC.x>)W9LF, 'L1 @~ S0V#{MQq}Mwj3/B)欣QK…0 =@0rA 箪 QeY(Ud'8;{7=Cq+eYg &!A؍as…0 =@0rA .Ue=hTZrl[YZN BUQ~, x EoMYZt[R|=_Qiq%o' vÁ(%B F oXa s=*8肨4 9l d ^~i;Oty|4 `r I9Yo$\3#$-+L@XrjѯFu~434 ig"sm7ĻהΕ9yFwF AuF, 'L1 @~ S0֟mgѨ/WnrQPT'CT6/KYVA+.OF AmQK…0 =@0rA .N#JhTGF3V(**S|ϼK+3W\S+ AkG|X.Ob ` ?wy:.?\D C.x>)QKw^\X.Ob ` ?wF'{5$vùOѨ򢢢hx \NwRW~wBff%eO~pDnл4-7rpqQKw(%B F oXa sWyf`5**hyvMV_|q֬Y4zt,֭S(4j-~wBfy<5RYVmq<ْj@.x>)ZF, 'L1 @~ S0֟CMҘq^#to{i<VP:[owѨIMڧ m֠:gG}y>빇,ԩp r I9hGoUB F oXa &sVѨ!!ܟ@202Qtju׵hInQ=z0aƘ8 N%ߋNuQ5EkFoGu܂\}R.(%B F oXa &sב%Ө^Gx㍦MvĉB0))i͚5ҥˇ~6l…cƌi޼yBBµky{'pqqѻw&Mo>66DP?ٵ<Ӆ _|1w\S#۠ҥK͛צMnݺy{{׬{C^~wƍ+<6xۯԩS'Nزe:޽i`)))#FxQF-Zܹ٘3fn(+%}ZO\uOu: A GX.Ob ` l2wEN5"IW᯿ݫWkn~ W^y|||v*57wu=<X\S3 |f~HKH5o|0ܵ8777xڴiw}wFFΖn1wQxX\7P]]/wӳ""""))lΝ۷>}:i0Xe FRdaO?[ލӳgO֘omVoMEV༰m$33=(***,,iii:Zئiko:`Lߵ;S>4zrssi_\R%''+5f< &dee䐠ZߵA&B%b0fRUV~p@iJ&{\~=ץ\}RmYX}}Khp@0rA M⫩OQ3$=x? m@vvvf6l@ &fHx;;@ Ao6ƬF,U%4 `kE F oXa &s>4jSf m \xq^^-H-"X%<>etA՚ w%o' 1i\ZUdT~ a b)[]WSiat-Cr<_\|~Ŀf9r I9j}4 `In ܢ#\@0aȳ&Ѩyx}c|9TUNUW\ݩ5@ސKޠOAuh<3J nF.H[ VozP5[Ф(r⍉g"4M5 A9Kc .( a b)[]>[r4j6eYFH @O؜u1a"l^Z \}RBg|F,sC.( a b)[]7F$1ݵݐ43jAҝAAސKޠOA5qQK3-/Fl h#$-+L@jr  `ZCb.  ~+ {GD[4b ` l5w*N>F.y1Up1 q^KAސKޠOlE~ Ʒ?F[4b ` l5w坿8-~6siT]Tt__ ~Kȴ_i1r I98?cՕmN4 `I h#$-+L@j*Sە~ğm-3MX!l:Z\}RBݙF,+v( a b)]j=T4~FсFs1/7rVZ\}RBBtyip@0rA .N#Sh&e6gK Khyyq񎨟w277r-K ,q-yC.x>)A'FT. hp@0rA 箄?݂&ZKKCvasF'b>uݯҏ277r&NVjKUYE6[4b ` l8w] ?F-<#\ۿw⍉q*IL5.t֚-_,/,!AN؍XS#(M a b)])O~@VҏOYiı> .wiR E!aE9  r&NI?_p@0rA .UUJ-;,tm7龾]h+%yt5 .V`ϐKޠOʁ4 `anrQ-1 @~ S0: +fOPU$9xE-0'~ӏw6q[wl95hV? K[dKŸn{<<ZT]RF`uQWuk!ڈR  [(XX&Ykhp@0rA m.SN(7TU%lɏ<ʖ =lp@HnMZ.`Ots^ԠYgP_xU~:%8&9xilIԲ^7AxdQl  X[QSvshp@0rA m+o+*q\\}Iw{lb7:^J+Է ̏"<臰Gg eݲFm17rpnk(f) Q-1 @~ S0..t'JTIbz!+=:pm^e>/3;ɁXp{֩PVv)6r I98kQS}+E F oXa ¶sWqKhTp6Ϲ倠I˳Dw,n7]Ty6/ձi3WѪ c%o' `OI4 `y|G[4b ` l;we9F<3/f?^sQVΧ5nWɵ f}\&N#<qqfJiU17r¤H$\\4 `;E F oXa ¶sWyF_2u]UEyz$ش{p7b@ސKޠOA QKs?sj,ܢ#\@0a龾4ViRF.좗wۜ[s lhZ@ސKޠOʁG?$Q˫*,qjOUQI lh#$-+L@|:XqKmnt@6Dι.b3o5|?8KޠOʁ }h*N9)d(nF.H[ Vuvw)N4 MD=_w2-0Z:!hKޠOQʅ/.Op/p@0rA 箈o7]^FAVm>|[UUմ A9`Qv2?rpv)omd>Flh#$-+L@|2u%⫩>}y6(-̓XD`7C_ET'%o'QPW+]Zٕ&e[-1 @~ S062NFPc79K$/Z$''CÂޗS2)7rp.6{ )gKuQkQUU<x[4b ` x<_w2j*rV'?ȏ>䷰Xb[egg']%o'oؼ47殻hXnF.H[ Va d(|[.ޓH[X,V']%o'o7igh \ۿV-1 @~ S0<]? =>o|~ -,X־+i.7rigi{MIuEh #$-+L@0w%3TA $-,XB.>)dx()*mߕp@0rA 'sשwf0ܩv)ξ@*b-+rpjЬ K+tX nF.H[ Vd .f=BǺ=64n#-䷰XbA~  8 ䷀ %eïp a b)N+ۜBQ-%G`+-:JJ"#%&> nݚ5_WR_۱2gguq3(bN3B.>)'|y;FlNo4 `E F oXa +O4j;W\IOOQ)99x JEVxku뮻ԸqΝ; Etѣ*u]k;6_6mf.&V7QdŜf \p}RN=O؈Z:43(///**F]V+ݢK)Chc,q>Io !b)N殲lEwiv[NPh]⋳f͢QQ.)?#󟥔2'jߤ-Z4믥׮ wX_J+/U_۱bNbbuE\i[%W'倽E=O~sUUKw~á;v_VL͝18d6FwwyDϑ P'oXa UE4jOzN,j':7vZ)9Ǻɓ;5R*U~4']. MB* N&H4kr I97N6uQ>D!ۻECD>&×C&ĢmwKu{#+oXa ˫Ĝsi<=z0aF ?q6eys>^(W,߻kE"\\ֿ=EeeӧȂ-[OUQp:oԨawIҥɾ&O1v͛С͂M""Q7oƊ/t{f}3Gkwz|l >x6/Y2L{>}[?Īi&V7Qt`L4؂r I9;=[.슯ŎA7&abE3uWww.a4}&vg![$:ګr{#+oXa +pܒ˗M6ڵ'`jjѣ[j;w?>{ܢE fu eO?i78fky{'pqqV}fyxxݻI&۷55**j%-Z[cyŊYE2`@-l/G@V"MC=a÷Miذm? ɓGjƽq*lGo3+^j֬i۶/X0Q<]M=|'bß.e{1ロдiB_{էLyݍga՛oo03uY>vrZ:׮uuEwx* mD3-o!\A>ӲN(Ŭ?5-ZuҥK͛צMnݺy{{ 񔔔#F<ƚg`; ǩ3W7xtE9JS<6 n b)~#lՍNNNfY_~衇ػK.*J~륗^2dHpppffFՒ%K؛<-W\\]wRػ(fgn۶N-6&oڴi-ZAAխLUYuo|0_,?*`L~cyE2`@ƍ==NMdO4lo>uL߾i;wN]j#νv@xjlli>}^-) Юk2ld!v=[,0zvIVa̘B*tl&zm6O {?ws4Ʒ޳mفc /w#-?{GDDh%[0]YmlvػIL[֤>;{aTL+}v4$$DwE-[] Unժȫpyzٱ!_BF/v4&yiCn٤ .t˖- Lf nix32ёH2v*j ;!&Mu+"۷o'Z{ o/ֻw>HQ^ 6m4WzȚ͍=f322jcTl51o>:ޠ26G0l j>!S~E4 2u_) W^5***ڵk۶mׯ7g}3 a*//oٲ%۔}ѝ;wHJJ[0X`[ kpu/+SUTeԕ@ joسWݣwyX~-& kԨ/\8GcjΩSuqYM[/' B 9sۦxw;[?T\o AoN?cz˖-Vkג'hKOP n\q̚Ϛq(o<ƶ` ѽjظq#.GDD n֭[dddBcu![Ә${{f͚SO=52Ҿ.{t ].YMOOOœ9sؙ_|W[j*?$鶧ޢ4v.^"ֺuk 5_3߿e~'N[ ;XFG? Q:Lm^!@RʣQ1˫fn$33c ւd{Ss ?eۖN-m2؋c.dg gM,AWI?1QwEII@j_ Ύ"!*D[TtV}3Ď[& .gT B]V'rp⍉Ao}(do4ZYYY9994;CFʴ4H7b4{-Ĝ;.aa QT&6e9|) ۦZ:u>!  NKLӟL07C~ ʵݐljJ?pǜ*ҳ=ôcuoabr I9>178F8S]TzQOcۻƾ,*;;Yf6l -+LJ&џ9j*KR}FbJt -,XB.>)^rFhmWB moݺȑ#4 x<|v @<Y|5FAx?4_iMQWmM?O K,o!\A睿L\xʲ ZPp@0rA:GE1hd˧>!4 Nb-+r긼Ue.Qb nc#(osW5h,,y)[X, \p}R<_qpw#$-+﷦dQ1.A(Xb?☪bZ䷰XbA~  XXIbY~.9a bunNs4 2ܕpDYjZ ䷰XbA~  J 2^A+Mɤww#$-OqQ1RU*ZNx[X, \p}Rw_bty~W)*h]r1 @~ *p²=4 2Eq4 V,- ѿE K,o!\Ac/)Ob.GQim]r1 @~ *q[idùy)͔g?~'-[X, \p}R8"[ JJw#$-[/FA8.,t'D]U}&ؚߊYlcz[%?' F'(H_u8_w#$-_p;q8w%9x=uexh -,X, %O?.HhmpԑF.H[ V殣Q1#?Qk{ey%-S¢XE-X!ﯖx3wnnnIr I98etD @EwKrpp@0rA5_hdùK]t՝tQ4{{2gˣBPOX_b=4  }ntQ5Cޮ+J ̆b Uّ7s+rC.(X}e KG~M;t!A#~TD"tu£ӈҤ Z`%#\@NF c|]A -uG\ #GWl=j 'WZ%֣XJKK+,,NKޠO%(8mqt{lh^X -0F.H[ VQW 9w~(do4 6rǮھ[N l*;;;---111N.zq~dPKG~UXXXQ!GE.x>)O,BrJ.?- F.H[ V9(sWil'CǻU0vΏ4&~3-'%o'㩑oFvFe.9a b>-s.)sjW]`;Ax!y?%o'ӈ⫩4 r%2|zO= %#\@Ĝ_UPL W]GΏB`;Jmq`119WW\}Rܟ$!FOR]m]| ́b ^tq;w8a;M]\`Ϟ&ez\}RܟxGya1'ޜN2p@0rAzhvYO4 iDU~-zUnu`ZPߐKޠOʁ{% qIܟy9hp@0rA;=xnv<]w4 GUYuIW\}R(7uq[=}b!o]r1 @~ DgKQ+n箪b*-[SUUxcbz{Z$dJ(e AJ(D+TH Jo~-07rؤ'r?7f?%HF.H[ bWR<I W<]9-A<+OqieWp1@E|jXr I9P}z.M%e~!SW*Zy56'|%.9a ",-w\q>ww^B=^,`'<:+\}Rmg(ԤV?"BVx)]r1 @~ č?ǍTy(p&as(bK+ܽ@.x>)vYoem _ 1K F o20:FA8`+g;O?O 6UGum1Z`-%o']Ev>Iʸ-6,=8.9a vrO(sWȘQO0vC]CuV &l.>\}R?<2F Uő?luy䝫2!%#\@/MA K]9FKn{k㢖3M]V\}R?42Fl=_7lLp@0rAqxGҝ4 В4OH 4n_ϥօ\}R?82? pG{ >:$t-]r1 @~ -OQ%箳#K:EdVvWRhNsi!A}Bػ${OZ"b {,?w]Z' 4 |)jKʎ4&i- Aޮ*(Q-QWݟx]4%#\@܊SܟFA{{L-ڷi IiFKޠOʁ KhnWYz"l=%#\@TUՇR+ UUPxoii$1ݥ]DZM̺^\Y^I l 8W]TJp*s 8}b [a%2hGsF睿L.^'"Nd u}tHiuZ`;%o'龾%e4 w,-ۣӈ;8b w)YhGsW%W:(ݜiTJ>:$<-)7rԼF_MumwMU){ āEE %( "" )-m٣QFٳPe$< Hn|$Ϲ99=On9IWzDoA;s؏w!RսSggw%߰&`A,r@~}4bе}1r0[н}9or棋K,}ߋ⩞4!_n֤,(ј?E x9+#q%Ya#2@} wrz^(G]e^_h`0fJȲǻF%߰&@^'~O{ַlp0[нM{V5xS|޵_o$.GnN|a*oXf0X#kFUwʕQ8K/kj^xaذaݺu+Uɓ'O:ղeŋ!!!ʘ7.ZhjՎ=&Hիx\\\ǎEs"RLZj('Hњ4i2|p%.rБTx2cs7]kQo< bnl+CCC5jy?bĈ7x\rիW>}wynn[?~w`#2@} Fp`#Q޻6+(tšngwOخ=R1?oXf07_󫯾Zp5jԴi[yꩧ+W5PN6ϾЫW'|y;w4hP*UbŊ]zkԂ1r…ׯ߰a[g裏O?j=yO<ѦM .8:)6lذ;3==]Eʈ \>:rdL|lѱ4]̡3JTx8zKSúZ[j3{B ]|yƌJ*M4I (nyg1ZϞ=,ZH48n[ٷ~:\|?\-V/0-w_c}(te;}%G fo=8r௨%߰&`^ߚ:u3g$&&VVHA FL<(oРC==zԬYի(rw.Fʕ+'&7tshCq9 ؼOayZݜ_]ևm)X+ޠ0ٷGݽ{W^ϝ;vjqeŠ&ؙAGo0[0LJo0}w|뛣QMdGF|Th j 7I3_Yk?}6(~wqѣK,#ٳK(!>~h şxAٟ"66I&7 ;\xkxqXb-ZHHH JG0a8C={j׮]'|r޽J|˖-w_…{.w'HфzK\>Pm~(jSqiTΓx[|/"rgJ,is7ٷRիXx`ٲeR; bPskǗv="saԷ`{>x?(D_]\'rzs2?qSYS>o72}Ú4Y3#.\ʺIIIJ0###>WMŝΝ;z}|A]xҥKRf9s^pÓvud@7>dmb]YVT4➰Y>kK 89=|[ϑrf'ΆNB:eQy~_Q@%߰&`A,7T 09W0];p"RPZB.+d_zDo .G\QI+J]|u ŒPsnAl;i%@_%߰&`a&ir0I&-YDzSV ?.9Gd.  "zj;`{i9+(t(aֈ;I_O;Wm_\kfC-5i m8rT?RN%VitZ k h"GЙW9v="saԷ`ھ{v9 {ץ-VrJB6,Guպ=C~;CÚ4IܴGЎHeOt>v="saԷ`4݃_NuСmF5DB">>yޏ'S%߰&`/%Ghf '8]r@\-5=nkN=|GS~߻Nrwu*3];(2W(%߰&`]|Woߟx(]r@\-FھῤJPf.,%fMke^˞xoGøux\X( !M>t86RЕ@%߰&`C7Q::j}(]r@\-AʷkkFa kwBMG- {jmSNQ_j|-n9/wPV򍥏v99-\Z k 6љk(MY3h'_`zDo bgD7E嚝 ^ɢy~Ab~AkK'S~0}rglM Z k yϥrַV›%̅P߂ql/Go!ޣrA8߻b~v%xSV*틑;|W3V=o峭QKaMAT'QZ;ퟻ/GMzDo8,7Җ"G5zDolQ,u|o~!Ga6~f*9?)֤}v="sao]z5&|jm{ʟg]k׮e:J:rr~%썯Iw.Fc0<*@-5i烉r8o>,G5zD XR&wOs /^_{߻wI='Ga o*Gƒsִt-B+㣖Ú4c?5p_P9 a#2`ք VN&Ǐ=D]–nON0U!wA=W҄e[lLZ k NKھe_v="sao*z|tאmFQ˦NC|̓c-˲~FḾZ k N]09 d]O]XװK  )98f̘5kDEE;v,11Q> ׏DžUAXņVn"w֦NCy62݃lM>0j 7I3x / ̼,GzDogJ1"$$d͚5A^`$g)J-ůjP'3Ú4 kwm~9 `#2@}K7,[ 4(Uٳ]E1`e˖X /0Fo5*fJ\?q6BˌIrGΥJ.TJoCcÚ4|wK K,)pˀuAfwbtat^Ocj7ǒ]>0}h͹sV.^Ӛ_C*:oWCaM'~E_~XVqԩS O]dIqsU6DOGiQx\v"saԷQf۱cG[֤IF9 \X39 `#2@}%%%,YqkgҥE1444۩xwJYE>(m]JMH1fiذ}QqXbJW۶m?Pzjbhܜ@ә;pWM>]ӧrwdfeeJxjr3o\?Nic7KԷh455gķ!!!/:::>>^NBxVW@ k$>5I9 \v`U7(]r@\-/3g`ND^v*9޵X,uwkժ7N:;wպuk+ GffGp[4-?PÚ42Rh*GzDogZ/9:a[4-?PÚ4, J4v="saԷ3vsq"׷͕շRR;?6vIVN{p!cǾ|pN[^coy[p8?7NH^O;6O-?PÚ4·5v="saԷ3vsiXR厜= (pK…jպO 3uӖq۶bŊ~A/ֲ3uyuBҼ}b߱y0o >֤I Yrff/wK  ic_|sK7ќ󤾵kE)SjڴQN-\ͯB^co<)cۈ} y9 Q>O>nw@]ƥ=L8XÜu۸rÓ2m˧|i+?6u9;vlZ=>oNwR{F7q' aMĵ1&G:P9 `#2@} @>j7ǐu:ќm}ŵTBB[*m$ʕlؿkԨVPj*M2>rQh11sM6Ue˖T鮑#Iݻcm~ah? 76ZZ61ȝwQry/RR z[۰ac8p`=bv.gƱulРWۉbٳR3sRJJP86eB~uD͚JPRFg/AmZܼwń-Z^GV?o%EPmͮM?#UVA^r48jϨ6j M'jX&aIM_Pغ#2@} &c7vsQG8Wm[s|-f?pRZ``… -]} q_ѹs ,{=ԩ٤xZ93u6lVhwDE~}cK,{iYHnZ)];(e Ptŋ~;PҙoURꥤl˱zp2nz4hͳ`oΓsԹsrإK;6U:Aܼ.*5w3ڛ M'jX池DcKjغ#2@} &bX?>YrV9F!ֵ(Gs+&ٛ0C.9Vt,ɚ5SPNMJ.q ZJ*k.sv fr.m40cJ6PmӦ`,կxլ]')\йs9?HM}bZln_c& *x/Hc:ȩ?ck?h_:ן\uIEjXƘ4cѻ~u|~DɱI[͛Mݺ+/r&M[o,uz^ڴHc:'ۿ:>?WUn{XrTR{Fp0-?PÚ4ew?Fl S  sl>0a69|ns];qkhNܶe| .t"^zE!>~e@@@۶Ϟ]!O>Y8{+[#Rt@hx1)ێeUɦ~j%'o*ZȠA(-^Ƹ|y]bEzq|xDiLo3O?}T-ݻ)T#5i~ )ݿit|9'OFۯ^~}-(w;uj 3Aܼ.-wGfPkԷVW@ k<4{qCغ#2@} 沣(V9c?r4'n[߲N@@@egĉL<( 7훿y g_wo#ڵu͞UHY3h">[75uilxT~#"~ zF898C\њ6gre?6.2F.-D{*E3qS۷S2vŊ-պk.aacbVtm*/Gj⩋)A"_icO=Zu&wx̓Vͩf{ c5Ij_ >V* FW&'oz "E ?s.]Z,XPK,oK̞xΝѣMoߤJAu4Pmi+w︣ԩ/ X+*MLql;v̪U>{}w/^R:5*M8=<^y%ƍ^/AmZܼUZ5O$K7}hKϊSr?r:6wGfp٨o >֤yD3%Dl S  s93C(V9e_-%&Fn6ݷǏJ7.Xo/U>cu>esyj:y2"3s8S pΜY-%eٳ+._]Qy]WnzoԦ}KJڨNssԚ;7Ϩ6nF}Ohu԰&c'~9Ml S  sz=(V9O7g9շhg\~dJ//AGaMsvgغ#2@} q%9\Ej7عiہewvv/[6ɹK//AGaMǩm}uغ#2@} \.0rEj7o-89[4-?PÚ4ֵ(Gn}̅P߂鬨CrG~_7KzF}Ohu԰&#++֭0Հ0[0/}tf*9nsܲ-kEyQZ]}5Iw!vr`gj@\-Ξ!?jEj7oM.~[4-?PÚ4Kb(?֭0Հ0[0_E#G1Co:P9[4-?PÚ4;f^.Gh[a="saԷ`: +G-G1CYf}QPߢѼרo >֤,Rrغ#2@} s=&.FG9nr?+%5l+rPߢѼרo >֤yu(u3L5Gd. Lǚx#[E@>j7$rvohkԷVW@ kT6wܕrغ#2@} f~'QvsLf-rvohkԷVW@ kTv3脹rغ#2@} flU(V9&̤r֑̕;v.ܲ?47YԷVW@ kT}5cП(u3L5Gd. ()!rDCO&GR[4{mGgϞe˖X9 }Z]}5IS99=|kQZ֭0Հ0[0Cc+H~ܢf[׮_\Ki˿ˉsF|chن|9^9#f6!!!/:::>>^NBxVW@ kT-۲.9 @kl S  3:3o?'Z'ʷH={QLTTG>W%wDN%ăiu԰&Mc˟.Gh[a="saԷ`FwZt/9|nr?Ϙc?-[A>Y x3f"rJdȯ$9}Z]}5ISI;9r+9 @kl S  3ʸ\.P"hcܿ5u{Q$&&C=rdUi='BɈ%+)))==]Nqںw6}+eiu԰&&)(Mu3L5Gd. LjEwZ-iǓ(|$*ЂVW@ klV7인i)n}̅P߂Im~3VQvs̖W}&wOZZ) rhu԰&fsON])Gh[a="saԷ`R{trAz=Vn𦬔ԭX^g>aMM㏌%Gh[a="saԷ`R1SBZ0c~9 I:+’!КVW@ klx9 @Sl S  :j{drA~fRʢ;/^;g t0aMUaQbgj@\-g G1go#%GY3.NW}VW@ kl7Yݰ)n}̅P߂Iٲ, 7fd3vs̙.}EI={quۿyhu԰&&TBؽ(Mu3L5Gd. +NQV9e_!Z"?_6#f>֤X3kdXagj@\-׺-"GgZ6(9<~jm/D%>֤ U9 @;l S  QV9Ky5Y@ne\NZf}S%}VW@ k҄V7ç֭0Հ0[0c~EicӣQʕ烉,iu԰&MHJvϥrvغ#2@} vQV9fK[G<ԉw1, (RjX&&Gh[a="saԷ`^W[^U9c(u3L5Gd. ++M(L~̥[G3GO/{?}tB5iB ˷k9H[>TzDoB+%\vsL*;p;q ׊tE5iBIc<n}̅P߂z߉QV9:[%xehu԰&Mr#mA`gj@\-#cX"G7Zg/.*"+Wt~φve^MVW@ kҜB+J=(Gh[a="saԷ`jG*EhC [;|9 'X\͡13?aMn}̅P߂dkryn/$nڳfg*wf;8zm.)w3>֤9mi̥rFغ#2@} iϪg-G7ZU ތ[)GqKƕ m]ݨOj<_VW@ kҜ:rFغ#2@} p)REhC+]ݰEv]GD[EZ]}5IsH9 @#l S  [XIV 9֤9'Vi-Gh[a="saԷ`v:[G:V9依%5=r䣧I>vfݶFXjX&e-(\VJn}̅P߂`ofQV9侣5kC+5D`DZ]}5IZXkc(-u3L5Gd. .EQ}QV9侣ījva,K{xʮ#raM-֭0Հ0[0G6 GZ7g9j+raM֞&9 @ l S  K9~_{9TzDo[$G[Z.m:ȸYrX:MeY>PÚ4e7wD[a="saԷ]0W"!]m,iV [V`2Z]}5I3[Q(cgj@\- 脹}'G[Zj6rT,^lݒVW@ k̶tvjr9 غ#2@} Ȏش`9j7Wsy[3=K>vfyWi&0%>֤tQ>֭0Հ0[@vSKjv-vs}7ֵtbjխEb~Y$w01>֤bsO(cgj@\- ۚx#[E@hCqiȔ;F&rsaM˞&G[>TzDo7?89\j7wo}'/z.qMd\IVW@ k, J>gX֭0Հ0[MZ LXU"W!ݻPXrN\ܵZۃ_N˶>aM\DQ֭0Հ0[M;O (rEr6v}ۜ_%whu԰&MnCw. G[>TzDo77k(rErF]\%5]c׮olꆽS^VW@ k|0Q֭0Հ0[Mgo=9\j7ĦC%Gvq,N5ir'rou3L5Gd. pSҡ%,G+Z?&Jr9{Xh3WVW@ k.mٷrou3L5Gd. p5=cA61x ǻV9侇y?ele_I:+ >֤e^6غ#2@} ;\?q~ܕqk)vs}Ğ 2=D.ckORt!3C5{ۧ|[a="saԷ`^Y7,7w׵|rjpCZθ݃>pIrvJA'-;vjXXfp|9 غ#2@} Ĥ';|{짛Wm堿 iC{.•E[J;|%RfhR}/o? u'jX3ǃQ>֭0Հ0[0OL q6uMԺ Z9z>pi~[ɸt1aMԟKt.G[>TzDo*7uv}{>\.pa5hiCHV*폑;oB%nvO#YG@hu԰&q5'Q>֭0Հ0[0;[%Vn|Ek]1vs::qQ/KKDcPG ״jXe,(53K+l S  d_ں~F=}n+ bоO&cZ9eZRkv޴W4閕|cK7NYI@5 a#]4<[>TzDo7m1Rs-bK[GcZp6d݊zo-\/|kF|VW@ k.Bn}̅Pnʼv=lSe}ѝ>7$vsYӤ9Qo|1`%.Ϙc?-5=C>rH5 W3 Qn}̅P J4_mF} vsܹpXRRv[EU[J׺DE÷ >$[#G [>TzDo!g2&_eԶns 68o]Fm׮?%3Vryo #>$ڽ|l S  Q/DεvtίڀV9}+G{kiu԰&?bʡ|[a="saԷ3)1?Y&**رc;W1@_9j[H5 Eܢ- |[a="saԷ3)9# YfMttt||vs W0=aMB{n#](`gj@\- iJm`GgϞe˖X+Z ɯ5azPÚ]HY7(cgj@\- i@hc'rԼVW@ kv\-Gx[>TzDo!g7L^m@>5 @jX~s(cgj@\- iޫ hc'rԼVW@ kvZH9 غ#2@} 9ajZ ɯ5azPÚ]K*Gx[>TzDo!g7L^m@>5 @jXY <*w2n}̅PBΰnڀV9}+G{kiu԰&hU77[a="saԷ39OIٲgϼSZ,;{5o-8xsq5Ո} y07jm@~LRUJo~+O5azPÚ䣧xQ2n}̅PBxޡ⧰v]f1ұcڵt>Rzs9xǠ•Ü;؜kjǻ K{vs 9ʯȩ]++ q;O|n..RGZ]}5IͶiq֭0Հ0[O/^\X߹Ki鮻ʖ+WfW?}K.YhѣG!ۇ w *XZ)SiZjXlJ9޽;v&$um;\xH"+Q=:(5ڀ.N={uXT /x .jZ %ur>G/F w\GZ]}5IHַ(obgj@\-'[!~GtRڛov뮲0bD Nn4ZᅬAzHŘ={]x? ]|i ~׵kK{Reʔ*Wowyk֙JW>)[/ϝMO.{qa.]F9>.N4:^<lޫ hc$bv_nֿ q_9cW)z9?!0=aMB>,Gx[>TzDo!g<3k#/D;wnU{rNM@5m3g~9`@6m~ywLŲSt)ߊitq?rړ:xnj<~{vs /ǦVZ~qP\fT@azPÚ$rRpf6U5l S  d=&&\xcON 7VEvjʔJ/c[CѤ*{'of><ɻyjǻy:n݇Dy6nrիsֿ q_8իWZb*Op~"R\kiu԰&lI8!Gx [>TzDo!g<Xv֮`ѢEbbA{}+>~e@@@۶ةS38{vxȓOr ??SGm?9y8A^Q.\ȹ07Oxn^c?nNiuަP.E ּWj7a~=CbM:ȹצVgϼeK?bI؏5 @jXp-[a="saԷ3쿋l٤0 ▷oDok/*Tطo3쟔=z-Ycr&iۈ][[_|}"ү_c.83C'l)RxA20ܡwwMݽM-wK4ysNrs={ǻI tK 5a MuL{W۶ OTPrL!M KUNlyyS}]O>>v/rRUI~t~F =P?|XSOgݺ}Nt݃ >nJxIcR{8_oE1hlDORo]n@^&~wy租hj550%͐zWyuy&/sH^WI0'A\wŠZni`"\`䷠i,{'&UU>DSYW"7Ja+oZ22D|&" X^ExS#5pPK%zK:u$F>x]n@^̚U(fjF7us **+U C DXoA45EvyEsXTd7use3z5#k@6 r߂A5EvyEsXTd7usRةGd-BP0V.[4.7 h >W00>`NBtg$k@6 r߂A5EvyEsXTd7usUaE@6j&@~ wajR&@IgN~8@6j&@~ wajR&@I#1w Zni`"\`䷠igM]n@^}&a`"y]}$Ibyh"k@ r߂A5EvyEsXTRSkju15gb@I 2ޤ,d[`+X-hYSR@X_M*9tі7WХ{ZW l'$n74 [`+X-hYSR@X_M*1} >tn~{%+w_K1s,(&H^WI0'.v= k@ r߂aSK"z[DKs<7=+Y<`dKE~ћC!$ЀޚZ6ni`"\`䷠iD ]vbLΒb + :wPY}x$J 9,X /γWS3⭭4r +2CݷAe.%;U]usaY ҆-m0L ,4(šdω/^||79,XX_(MÂLnu r!X[@v@IhJ4<+lF.j&@~ "0WT,.58~3OdXYv-Gzg=W|eN_vn6)CCCC[[[d|79,X/ۍ{1amGOaaeU{pةG*;oD]yv>@^WI0'a$HT!tK 5a E.[p+Yt Yaۏmd'NuSIfW4Mk?ę;h^i>A6C*sXX% ?iVN n8g[CvE%$а_&k@ r߂͖Q,t ϜlP$_ڬ^|o}[Ԛ#uH||lE^}@me1wKc0u"=siO }c]MvE"$а 'V UC DXoAeff&''(FyeV!y;dCըTO8ٕv0b **oRsTٜD6SxQUjbF'@usު+:/"@J r߂"ռ"@@60\?%`UnWe =4|;PW7+ ɮtW4G׾JģN-ٞ!d3QATB]_Ϡ CÂxH>`NBcOސlL tK 5a Ph9lY"ǎg AYy3s둫Γ<4e8[Qܵ/&XU@BP{ca^jF_-Ҋus##Zni`"\`䷠UHY[V- 3OqTŦ? ;$%*3(OX ^bۊT+O Z"~ sQogs{u]//$M!$ٞ!^)A6j&@~ O(_bF6WUIY}c+{=fnO4TTM^E[i#W\u$Ub8_Lu;Nn+>`NBcΓ2rni`"\`䷀➙یYMֶQ>|6qMvQrAsyEsggO\oS -%Iq-ל&gI $4k[`+X-`9aer*QURlj;v 4ﵧ|:cSrFU|w+k??זּݤZ}cjV[Y?uNԼ8<'\(]5y]}$FMmZni`"\`䷀br'o k[]Wݧَ]B-NI.svܷ\w헦fy9qRk9'8M~iW . $4R^PZni`"\`䷀&Բ@^C||st=n[-x944=@@6(j9n=r~{%qkN*.%0>`NBR瓵2C DXo;Y MTzoP ~g%hL~Uiy{O >tl`ʢT+ 6cVW:hߍTK @H9 MdemZ[`+X-`.ʅh,i>pߖ T1ȤZ9>DgyEsDǽx񢽴Y]p6چd3YXY^gl/gBc8?7Y[vfdWr^N;"`D};G-d- BP0V.[BAoyyittި }7}?E}Wz@g M ->hOm:o5vM%mҦs6]WI#-t (×eHBP0V.[6eO)O%@ _gݧYߦޚMz_rS=Dsui[v\:Y15&[j]ǖh^no|}柜dWt/^|R@#䷠lFfI@ tK 5a l_gZ @nae+}7ZyLI0~W͡jhhŋѳe;;*=wV'O?_~S꧛a]"*L^h4Uȑ{ːC DXof|>$9lV lVƟ3@}E7~x힪{VdX^^:oMKmuH^uin?{&h 8w~ DEEefb?4UGŠZhni`"\`䷀U6v **-^ۧƼJEGi5=gbP #QQQnnnfq2e 겱datڍ=hfȞ*O^h4J.ni`"\`䷀=Ҩ!,!^ќ۬Gd;y ת)f2d>8gpL@ 0r{b:}f,yqKͧgGSU ??<-hW}G< [`+X-`ﵧ^'kvќ`{kzS6p:NSt]zJNnǿ,,!Fv5~#'l& 2wgM牸f id?V@|GU}cjL@s!tK 5a ,Qll܉HT$x,;lA;m'DG-1J2|&MM+:*:ЪEG]uǰ2GMR~,@tI Zni`"\`䷀%< 9|yhL4 'LĿlǬ1l%{|MWeJ2a=ƾP}IY}`RD`ęXJ@ tK 5a lP\_D6<45S*1ӌmx?̳\>4)Ir4=}Aogm {qѷ ?`tof/=s[@`D] =vfA6j&@~ mZfGsU,'kd<ۏ4hLߪ%rUťdЮUb=#C=ՌzRoѷ d?jZTK۱kZhni`"\`䷀BbݧU 'RTxn O11qǦ9ަ45yAFmbcx.?j{WwxCHeGPUi9ㄊB[`+X-`s|]URFg58'|;e1Ido*M6dGUģ,+!(/A{-2:4RbRi=4'A8(oIS~Z(koydxŤ7o/pqMH*OEuMO()nuKADBT ykG 2tRiZ6M-]0L ,0RY_u|Ɋb?#N?vSI2l/Kг sivj,-8vQ]n14)Z_^nf;v>o͈b4E dg䓤~x6Kn<'#d[bO]<'*F_MO 5G^kZ}RzuLQeb0\y ؇&d-4BP0V.[H#Wſ"kA1)IɴVISZ_UYX2puy,odT_p'`娫 :E"Z~{%۱kJ*.%J L@XxL[/i0ZDboT{cVjfS_v ;U%8^s{7*O%Ѽ#f5$k) rI1u5Z($@1I}r?Wk~_g\U4y}-c,/骾sQEABn343H͘lFzWͷG֦٨uZov-hߍڼdյI2QLUqnLtN1sƧ/ysGTc餖OE [`+X-`rزD{=AY&2]6zUtx瞉e^{.?Q(r(a(fTQU5>PԣD͍e{+cTUzm^zR\`TbZ/J%rj{2?'='yEԥYѢKJ?o~)uit(I  v>d-4BP0V.[0Ö[hN~8祮 VmR[P)fkxMbl2QM15ɰ+flZnn':dy'-PUZr핬~]rsE~XJ(cn@u >@]mF6ꡪv,\y,qEn=_$FÎX QFC6j&@~ E(48Bփ"u4'sK6UE,dc'ݙ5O;argPofZ\`ߖ k[eP!BP^zÔ ,joHV ;ori|69CֲAknOFC6j&@~ $^zJL9=#7l׊R3]t#?vU}ͨ?{դA-{6?,U> ,SU6WZSdWh5[fU[)B!˜$[`+X-` az' Fvm,/)NL'Ҳ܂tm".~m&jR_F_MenM<a_O @ԅ)3Z5Nӷr:N0w=۲+&&v[UY-*^G^&kq r?6U$k!'''$$1}իW))-9!GYTpKKeTgye7,ҲڤWUVM~ Lԭ1}q뢩AwXĹgN3s:M4iϺ㵭JSȮ3 U%en~15&GF;"hm'E@ tK 5a  }77яlhs۷oSsL(㻺=W+W2` 6M$hN=#敦K#ƶ1$TQA$eԦO}s{^8| P<}{a 0/Z5γw|: ל{nA-('s32|i0dq6IwGPUq)D|#@ tK 5a s0EaѣGڵm}U(^[_kddR _J=\@6Hm I/aT|ukE{G2zFMꫧn _2A6=7T}{aWQWt\6b*^1.ܚ @3i3ܔ=ʎZ-_ZC DXo+LzeyqR5+9[9? */l$u[:::-T𾟛kC6Ix~I/a_?%9 Ug~ϻm  7=vv]dVM>Ȓ[[>\^Bb.llGx' @SQ;m>zf1wމF@6j&@~  ꚞӌm:uܻwoCCC&00o߾ϟuxِ!Cjd̙XbEΝ曇PǎYKKkڵ~J||ɓ۶mۯ_MJJRWWҥG}?QQN 4Zԓ:Çoޭ[tVRܹgP7|YogPx<رc?ï*22R׭[G=|н{7nz63+ PAfT3P (d8=3;3γvXvU`i݌IDgX~bb9s>}m޼yo>^X.Tw[eI/5j7.N$IјI3Tl,"m rEWURf?'Oqݻ9{vۢk֬?~~XXX(Q?;˖-xEEE/ Bu4hxVΟ?fddTUU󿠧N 3f̄ j+++s8~mԨQ 6G@P 8PUU+==]U{ IŏB]tYv-ϧ?//OvڿӧOϙ3P(feS"$B|x8+ }x{9騺|bO'}2 'h1R-=Cc+ gQl.|S_~\.Аaذaԩ@uQs}AgP'UIkzGc&8MO~Kt+q@P^Ah+lZxni`"\`@E2ow?ݩ{)WWW]]]瀀ב3f|C9sfmhhh~ f5TTTD?>>>]رne{vAOcǎ pP@ w{=z6lU) ,8v=[=#܍lxM+L0GeaI~8?3IỞ+9L`O]DU6{>r/޷U|Plm"͛?`R3RmVMN~tNLjA{#kcljr~A~[.6C DXoB,*vS[M>})22jzwj^TVVZZZD}ݟ}~QOG<[@Wrƍo[ns @ŋcG@zT7]&jʱIDATU{ I=?H1cFFFFm~K.T޽{z+W}^ =4 <jUEF6 #v5y}~0Ur2;}7 I L瀢8y]+W>x{i뢈\~Ԭv]7^b;=3 qoپKF=TKAԆᐵ6C DXoB ;ukD}ݺկw4y!CknʕucyAAAco޼I=yڵ6bY 777R2E6~ٳ 8JmI}$hȑZW#ΒRuuu߳gOm~:ќqMMlWZfBZáJӲ #/kHfjj6t[ESl,)ޅD'O}P3!44:#Fʺ օkNoiyewU#Uh$h:I-JATY_um^um:vY B6j&@~ W;ˏ|>_II}FFF~---E6mGn4`Cv҅ܩSڏ& 8pƍ?WT/ڶmKU~w;w7ի_k=zHKK4iώ;8qO>۷oGtU/YQՋU@=J^^޷~K|ʔ)5n:Upjŏ%I^H 3WA+T a&((NL?f8'PT?_$$E2I ? m^0ߛ *Y2///ONzjefffgg׭IOO PO[I\c"O .5@sGͭn4Gi |Tt_a u?[.* _- ۍ4v{#ݧYen-"?Oе)I$m2227.,#'AwY9'8M~igHc荙uAoQJU-([P/AEJ @ riދ hNn*5:,=ۈ /\pF_7¤jnA57AyV^"oyK+9l;(-I;fk̎Zn*!o mV=|  rETmt_njjJւ,5&C(NfjMJ0fʪ˜ڻz98m qC'Ck" P JS6ON#wDBca[ VN3 BP0V.[\|hd4G,#'pu/F^Vnfѷ ߸#kvruv\A>@ T%8ol'9r/OPVNvhHXY4cu9RO-$@6j&@~ NIRSpgyjd4'7 {)O'?ӌ/+`v=pߍKZ_^cVi-lƬ~lxD};$@6j&@~ Ϻw_#k9e澺orja'ey(OXYU\sǦpbNƋvHURMTYXDBTZn3OJcfYU\JvFhxG$"zce@6A%Z[`+X-P,E/g FsK㞙;ntH?y 5Aоl4MzywWӭGtUreDt$74kϠlǮod3ש" T*U9+lWijg-m0L ,(G&kQވ<|buNo?1Cl6ʼnYnAVgn<<{P /}z +2B?pAP  "o Ö "㍾{}4BP0V.[(򂢹ݧuS_cїf^fEs@a ** c}x!Gy:0uY_uO|6N3u&'/2]ȧVfHk`Gʛ+QSoD{c Zf(n*D짅Y!d-A6j&@~ 응_r"BO<qn&Z)ʣQWuv^q[e=r3n{K} 5/87c=aeUGHOUwo>pvedWVk(puEZd-kAZ7^'k@ BP0V.[ÌYUZN6")M?6u_zM ^Lg`@POלtTlBJΓ,4c3a=t(O%%>3ts:M4￈: :֥idWiᎈ󾛛nC6PjO1C DXoBpT}ˀPURjŠ9'-k( h^yNAn@TS5]Wiٌ#_m~GL(񍈸ybW{%)dOVhZ #W5&ni`"\`@2y}**? =~zmǮ 9z/='֧h"~JC>ob܀K.sv|6^O=3/NL'0vDB!֗lDTWn8K]ZxBP0V.[ @pz.swBjbmRL]+J~o#h$~K-_JnF6L1w,l{Q^PtU]{ (1hO*;"}bjlV/z ބ-m0L ,YY?uaU>RWOoQU޳頼S2lKնn8~iU[PIR`Cc_wUblGc&vDÖ + >R[4[`+X-3V$ڐ K=3ZuF_~ 枑CҊDyV^_D1NˮG4ꡪnG܎MWOF[nS'θ!UHPH>4q_'kni`"\`@C4pdƿ^sOq;^~' ҊȂ0&#_x!Gy:0ekTˡγwm>qymgHij Q 1wK=*1w "~ CZ;2.EZDԍd-ԁ-m0L ,ȍP 08B6^Zy=eNr;N^P4OڤYizNwX".K]u9yϵ犣!>4I.|(( hQ{ca^jF_-ҊJ;"\'kի,*tXWH6 rT5q-lFITB '{pi}:yKڹ 49 2>3q;ˏMX3GӬp]I9eSSxI1v6WBb~⎨ "sVN_Y B6j&@~ hԬ?U?K[ S\[qgae{"EWc~sj-~^jdvJf7y>zYoTjV}? " \twDgy>4q]!tK 5a 4qV^1w l[MƏeof5ӫd?yn4* K3W:h lǭ5FqO517=ףorRL]? -?2gW#~2#Qer:O*-'5ni`"\`䷀&z ʍ{e{OP@(ED^v>s<o\**oҍLQ\j3מrż"JNiv߿΄ymTA> )s:Or#‹|dJ;\HsZjJcI rP'#/tvGUIYo*;7[t t"n4(L2r㲫>+Ty$ˏ{8ڳ *APoҴkg*D#= dWȤp>Y ^%m?OBP0V.[@v׷lvaKdmST$;j=r~;%+v\N:]ԣ90:1;IỞ+O\~T~[:K r|#ʳȧ 2rl}75q3O=CUuQ{j'@Bk%1w Zh)ܮH [`+X- Q- Cs_zHTYuE'퍮Yq-|m>p~S7Rnԣ9P,]|7sModOQe=5p+⧐:=IߦPbjN=rVV]D0A#gqOB4P0V.[@Sݸ/$e:.tu$2ϰrQ\* A^p45KRvSq;vo Rh’3[{S۱k{QbCPоTk[~hleQ [En?rr~{%COr~GgRM>+L6vvr2J7M <8abG rgY C DXoMD-Qyw73N +zN ⡮D{2+]J2tꉞl"hTYfnO8u͊lmܮʺ1q +K-Mn7T1ZE~Q2%`_&$ 0rc*ld#r5؉h r&ێgeA1كxO1뫮ވ]Qe^HLmC?t3__S?gdj'{Zv1Y P]Rȑ{d-@-m0L ,DWZq-'IYO~/a cC>uE4MqBZK@s yQuyESBKfnLVY%əY$=puQW6mat(/U=U bG8DTWg8[ kZ=ni`"\`d" *5d3M+G<޳,yw(oB=oOFp%R1ra[jQ2ٕ0l!(3tBw&Vx^ְYjZ)ZNذr~X%GW"u#-#]YS)jo1GĪ`Rx7>3NZXZ)ZnvrGYS9 [Հ0-HmzJQ64vִR̫n޲@}]+=E+=~P>t"x u ] 舑 PRF+6m|Ȑ~ܙfWT9Of֞ /ky]"ԩ}FnkطS":}Hlr7c&9 [Հ0-rڸ/(RP>ڵM֯Z…|)x&wխ[?L+a*E4E`bbt͚7 "`*檖<}+=E+GNL/sחrpR1ra[j0!᯹}:txD &WݻcԨoHQ6dvB"o9;11O^j{殍^ϞC2cJOъ(D괒(WiΑu] 舑 PR+' υmh=~9YYkEa99NNɶU:7VCg6.5R@#9Y>ſ $.#4n-'x6m<-yVzVDigm G3'ːu ] 舑 PR+' _[Oii+:uzLRKx'e{^~8%eaժOtI>$*x8-}6nZ^OjFji׮ȑoq6m?wCߺ=z<\>B|e%˗/ۧ o:7m޼^R˕+#}mzJQ6\txЏaaabIj9_>ey/VPX%o~I-CIzc11U[~_6nx?i=}'^yt]y[)Zy22'G֓5?qrpR1ra[j]4a5o}+%eaz5/3ݻcxx=x7NsٿMV)5ҥcn YyoPRoD( |V=vM|kShol`ʚ?N=T_~طS"-?\PY9 [Հ0-.ڰ/Y2;[nqoUD۷otzZ >TߪPlÆw:=~dgWծ]M}UMu啗5zz7թswo}X"M ~m6  ^ t(oExvǷٕ0l"P?o~,W^yMlJ֦ ,VTϻef?Tx[}vUdءyz-b&>BG.6I:ҥcD|Р7{^o:wьׯsV-Zxo+a*utqEy?b5j9\>yy|[A^ʷ1*:ER.q9s]ha ,,Сź?=gޗyVzVDd"C۽rp&LՀ0-.ڰZ?/z7/̭_m61T=p}KI>^8|-[bti*)⋏}I$\Vg{^P5y奻vm?|xM|]YS.m)|7Y-˧ kV m[ANq㍕>|_FG\j:`eǾО?U9 cj@G\ZMmX[Z۠푑kLO燅ԨQ*Æַ< ڵ(X0AmA9{vUZԬysǎ-ʼny\~yiMnuB"_~tϞ׷*6vxcƼspzJ]6]\t("ժM,u~uVOOA<-[]@/|[VSJ.+y5T|oIp;2cJO݊Lr(dڴR;)o\ 1t5#F. @}K6oysL"V>-mxڡÃG7llX6=|%&Ow+\sVqqCm٬Yrʜ<,q|[}~⍝>B2eJyV&wEFFe}v(ث׳M[AոqҥK|G\\[rx^xt֬ gyo+a*utEʖ-%b)iӴ#޹Ϊ.yD+48qbi m[AN!X{牧x\.?z,<ԭD ?Ʈ(L1ra[j$!Zz5n&ӏ>zU|n:x.J3<QPD"##Ur^{3f|.o{.|u"R 11Ƿeϒ%߈gŋկI*WxUKڴitオ\ʗ/+%KFn"?}"(ZέzQ %ٕ0l"0>~ 7\SZN6|s_Y&%lA^ʷ1շo'iS:Pp5oΜ9DлwGtlߧ-<}+=u+"2ȹg$%G`t ] 舑 PR+3gV{߽G%S=Vjsx;zt[\cǬiСa9+kmRV_C]ڲ=`eǾLIVzp&LՀ0-.)c74R@/ nvԭD,+(L1ra[jݻ=jve=L.._Z[%uu ;0a:tȅoEjfWT9Eͮ\cϬ܁ 1t5#F. @}K-RF6R@,mvԭDRͺcQ0CW:bԷ"ad+a*ut"PfJO݊L.պm\ 1t5#F. @}K-RF6R@,mvԭDR_rp&LՀ0-HmzJ]6]ԷٵS""KȜ^1a:tȅoEjfWT9EͮBBw0CW:bԷ"ad+a*ut"PfJO݊Lra|Cct ] 舑 PRԆͮes@}]+=u+"2ȅ''30cj@G\Z6lve=L..XZ[@,l(`4&LՀ0-Ad1M@mzJ]6]h7gfJO݊LrgːUј0CW:bԷԲR4#۸'L$]L]6]Խ}nEM&rr 8s1a:tȅo~Ek6wШLG &,]L]6]"]5]WM4}nED&6VG&Gs1a:tȅo9gժU6..n'E#&~*~[nMII..BnI>@9y_[@sFQ\L1ra[7 &aIuďR@ŏ599esQH-frs>)o_}nED&v5 j?-Gs1a:tȅo9'%%E'%%m5і&[M(TX?~b9(tϛv}kN7OWzVDd"kY/kry`(&LՀ0-#)EQlȵcVGrbyP""XX(`(&LՀ0-#3ԨFr.r{z`(nED&ycezQPL1ra[GƩ4[\H{hf凷!oJVDd"1&Ly`"&LՀ0-؃es:{Ĝjmd[@ͯCQDL1ra[-xKv~.ozVDd"GZmLĄ#{dLZ+df-yxmPO݊LUfWm-G1a:tȅoԷ.rnu~jjVzP""xb+T>9 1t5#F. @} /uC?vnv'R pu9  1t5#F. @} /u{/= p)?xMat ] 舑 P߂=2NQcզr͎,oԭD ҏVq,L1ra[VT#9 WR9838u+"2E|[!G0a:tȅok(GJ9 go7 ?[-6}ub(`&LՀ0-#3 -Xes@+9|nED&cզ9Q,L1ra[Vr.;ڇ-N7 ["uX9 1t5#F. @} R!-Sm6r:P""Eg.Yxo9 1t5#F. @} (T*be5~&oԭD@㧦{֙@[L1ra[2NR߂lGIhKBVDd"΂:\Vbt ] 舑 P߂mo.{?[ى; Є_z0CW:bԷ`[{rP""u,Xy9 h 1t5#F. @} l.v&\7@VDd"NƩ4q[ǟ10CW:bԷ`Q۾(nED&JͿC('&LՀ0-؆,93V~Z'ǡu+"2P*!0CW:bԷ`[ɳοnԭD@?U9 1t5#F. @} ln;+Gu+"2PcJ̒7bt ] 舑 PB-b 3xJ[9I3*Ȕ7@7VDd"ڜ[YYbt ] 舑 PBmݡEkOyXV&la?ԭD@՝>2$Zbt ] 舑 PB9t$}Φ\-b_k`6Of֌J5I [j;ѷ(!&LՀ0-ɢ6H59^VzՓ ݽyWlf?wOQI݊LTK} |s9 h 1t5#F. @} yrlv1NjthIu,X99^tXmc7Sl m}T9 =[fV~Ė]r cj@G\jM&Eԝ~Y:S =hE^w`J79qynED&XdgQ@7L1ra[ȫkO)P̉Vh~`Jy'l=%QhK݊L}d̊Q@7L1ra[Yŭ)EϮZ Q`+:ǗD݊LpY?"G0a:tȅoV[̉3+?㛙fldeŖkkR""g\,m!9 h 1t5#F. @} 6:svJ ։-\<7ees0 s?!G3u+"2pƒv=i„#e'.\ڶ+k|M.iNׄ[` sԭD~86r cj@G\m.\}oP-Z+_n.ʼnr:S"&G;(G0a:tȅoƪoml[m-_9)٩Q Й8#d⍲3 >0CW:bԷlcշ.x\M}˗lFJYZМ8fn'X#eV}+vVںukJJ|}lF>>11199Y>D]6#-ksrS""l=cœ}(&LՀ0-Prm[z0aB\\\BBBRR|}lFyC'#G9u+"2p̱ۿ#e M`iSx М8Ɠ5=g8.o4#eԷ$9؆msП8iaI&0CW:bԷlC}˵D]6iCoQO݊L_&0CW:bԷlC}˵D]6lݺQO݊Lw⟚*GM0a:tȅoنk-lY'[NП8#0CW:bԷlC}˵D]6,YK(nED&y?Ol%G0a:tȅo֐!?uWN6?A}K.y~\ʲ(nED&Kh+?Q@L1ra[a}o HB͛5irWݺ㾭iӺϗf˛)\Лo>IokԷ$9gnvG]BVDd"-'}9 1t5#F. @}69oխ[k*ݧCK ߰anm9ݱ{|i[ZPy|wKr~XUқU`[A(m0۸ԨoIes0Ol!G?u+"2pXƩ%̒7! 1t5#F. @}6oOɓ?2eJ.]מXcϜY٫׳__`JʍoۼyR+WOzGڵkxďVJem4+^XÆw];*ol8Zz;ko,#Gk?$Л:.ߣӁ%+ /_V|Ȉַ*P/_?Gw+^/?HD]6L-(TԭDy>iBcj@G\m.^ŋKM]nmz晇˔)5x{w >G'zg|зo٥KS?kݺsVwX*4mZWVF 0%'tA|ď]ۗ.]by޳+VRro=y+Iқ)Zp e{xzQAձcȈ_|<::unh}+Сxaժ뾢$F}K.y&9QO݊L}Α@ct ] 舑 P߲M[%KFygR[U"[ "]V~CF5*U|tٿ>85Okong#4+Cz?=vjhxOg+/ߵk;nSV&7SXu&?WJȈm[qn  ^ t(&R e6ԩ99o%Q_+m#FcytoE'=}uF5/t׮[ TRI}+ږ-QQE]}Tz>G$mv tyر{h}+Сh+zajԷ$9JS[qy'Rc.o*GDŽ#eKoeem;燅i{xU;X|1c޳v,*PI)'@s kպf͛;vl)U•Kov t'*m{̍(PAzjkڵkQ` $P%Q%P۷sV=H6lXi?7/^~۬MUr;*^֦M֦@o&ȹӕ/_VK5"E~B:T曯zadۨoIes0wR"A{Bcj@G\mrXߺh;sfljJ'%%}%:K=~!I&OZm޽]CGޱcVzjlNJo8oԷ$9JS[q _쟻|](ژ0CW:bԷlcW}KŃnhԷ$9JS[q _xF(ژ0CW:bԷlC}K޽;ZߵF}K.y4 Kl@ct ] 舑 P߲ -6[u]ԭf_5{eraL1ra[F}K.y4 _X9 0&LՀ0-Prm%Q+oBcj@G\moQߒ`*MnE5|4ֶGQ T1a:tȅoنk-lTVD\3G m9~Bcj@G\moQߒ`*MnE5|ݿ@bt ] 舑 P߲ -6[u]ԭf59 *&LՀ0-X̓>;c4UMЩoRX­es0wR"A_!GÄ#l;JQlTVD\3w;кBcj@G\lƐp+u]ԭfX;@at ] 舑 P߂ Vu}d[`*MnE5|~@at ] 舑 P߂ Z=_­es0wR"A(-<-9E! 1t5#F. @} 6$QlTVD\3 ?hB cj@G\l6QlTVD\3w#1a:tȅo>i[`*MnE5PGY9 &LՀ0- Rp+u]ԭf L_7(b0CW:bԷ`%q"U­es0wR"A(8˶U[Q 0a:tȅo!IcgQ 0a:tȅo!. r..y4 Bƾ~ywB cj@G\*9nߐp1u]ԭf"^]9 &LՀ0-񳗷#Gb9JS[q D,]r%L1ra[ȫæ`*MnE5/%|s9 &LՀ0-զ~l`*MnE5PL)03Bcj@G\j}/~d`*MnE5-I! 1t5#F. @} yp1u]ԭf:5}yrL1ra[ȫ6vEϗp1u]ԭf:VuPƄ#훓 Gb9JS[q tc=Q d0a:tȅo!~ r..y4 B񳗷{W! 1t5#F. @} y56Mp1u]ԭf:-N~G9 &LՀ0-UU%QlTVD\3]Bcj@G\jjgLp1u]ԭf:ck䮺uǕM֯\jO k׮СoZp.-}prJS[{dee[K>}߷oΎ9qF9W9璘׍9w|3S AՀ0`B5f{={>ջw;ǥ6lXcڶm&M\4[nwR"9pUW]Ub *,]Zj]t6 8066s;cĈx~·]ek (G|eㄉj@G\V06ַ ׷&%<[j@g6I/ď)5uy.7 '_Vo@.pWi*u+"1c/^<++z[w] UwxI/ʵ_w&1ra[䤾ղ=/zj'Lzskb7pMTT6m?t J/_O"##oۼyR+WFl| o߾2eJ.]מX#1_[t M/ķYqL̠nQ[o\qetMś3gVW_]^|;i޼}KliodďDo>ݻɓD.jk'X/,A-}prJS[xtܹZjާT[nhh\߿y|c6.rW6NtȅoV7NWG_~KBDDQ޽V8uE WPG.eE/fU6+SF 0%Ūl?Oo#Qr 4uݻUVW\qE…Trܳg#hrr|o߾oFkԨ?`K*UP!O?_ 3%|\ݺuK(!?^? ;v{-zM7=ss8rڵEz , t]vYg{1oDtΝM6-RHʕcbblw?0k֬ GJl!k'G|?1ra[佾u啗5zz7թs¯yEFFm܊.T(otqk=V}wz*CF5{|*79_:] e6ԩ99ǯQpa޽﯂YOsđ}aaazRXu&dɨ5k߈E7z.ҵ^jAz:/6}@>8]ԭ5qm)Sf32/oؘf͚wVV-""YfӦM뮻ի'wnٲUVݻ[>\;v[[h!^{AN[nK*ye{.NiӦ7n8YőŦ>^,wdITg55kּ*TDv?W\q /?;v,C?,{m9 +u ] 舑 P &-?թc =x1bAn[ii+ƍ׹s-NVܷ*;4rqvъ/vuG6ӽ~gRr-39H' 6…!VrA#O4/S\uUyq!}@>8]ԭ5|M+DpĈ/R<]z   (ZONW8s>[l8ȑ#}V@粜:u.Q4۳Ys~>zhk߳xa},҉|??CqďU<^~{q+֬YsժU<ퟻ|QW(G1t5#F. @}+{[/zO Ǝ}߻[JO_ݨQ//ݵk{PY+m#Fcym7h mҤoFA s]noAj T շr6o.y4f2@Yf%~!vZի)SkiT\.V?x'oϿt.~W/_oŪ[Ίg[ O';[oԩSM^RY/_^JO>i=s}cZnݺoe{.qgW_O׷6l v6m6l,DQFYO%s[\R͹/e7]:tȅoϷ,Q"jOy… k}+22.۷۷SDD^65n\t}*WXpo\9^<~GgڬY]KKqĉi,ޣ6L^loyoI&.+kmέl6S˕+s@ r 5!h-Z2t[W׺uSioh=Zes0wR"W^Bq8p ...55̙3_}7mڴ~:uosJArV\"޾}nѣA^{]v{/^<,,̊{e{ѓ/`'N8,\smمە~@:'BX*U͛'V]czvٞ%--mԨQwNIITzt`UQ _cj@G\V09o^]*׊3^{3f|.o[4rwYU[6RS[,|"^dԨQ)򿿿վ}uꡇ`$z.3<,v{]^ q=]Vڛo.<<\<^A褾P߾^ŋ Fo<i^}VדGې>KF}m\TVD5תU+VSݻw_lٙCG(0CW:bԷc}F}m\TVD\3Y3+?|*IR{0a:tȅoC}F}m\TVD\3Ysnislv9 &LՀ0`oҨo UJ݊k!k]Orcj@G\V0ԷhyiԷ*MnE5cF 1t5#F. @}+[4[nwR"AZ@K(0CW:bԷEK@.pWi*u+"ֽvEϗ@at ] 舑 P -/>8]ԭfV>(0CW:bԷEK@.pWi*u+"]>d9 &LՀ0`oҨo UJ݊k!kC~[9 &LՀ0`A1OҤ&[wR"AW>Z 1t5#F. @}+E奍„ qqq IIIEf"u]ԭf6}^_Q 0a:tȅo~Ek6wШL}eӲmFOLLLNN/2`*MnE5ۧ߮scj@G\VNZJܔMk=!fĕ#[{Ϲ8GQ*tzN:<:MH(ED"(5v43ml61̌~ڳgߵuz~{]m-o7ԕ9e~bp0MCWVȅo$n q F~XUJތ2(F 4t5`E\h.E}4M'o7ԕ9eeMZyg;1 8o+bBԷp "{OK'o7ԕ9ee'Bx4 ] X#KwQV7#"gT([QBV7#"g,[^ϋQyx4 ] X#r9>2y_1 \êRWfD UTPz1 8o+bBԷP.m?U"o7ԕ9eږU{Vij-˶_~!F ~XUJތUi"F.4@} Ԭ(1 \êRWfD TF~B)$ij-OV9S(p!vsJ]ɛ3P +bBԷpq9k%FV7#"g2J!!MCWVȅoNor+FV7#"g2J!!MCWVȅobo~XUJތO(4 ] X#j(êRWfD TF~B)$ij-\\KSA(êRWfD TF~B)$ij-\O9Ig(êRWfD TF~B)$ij-\DnrM/Q1y9U͈RHHՀ1r[ QAM?cvsJ]ɛ3P +bBԷpK1 8&o7ԕ9P i"F.4@} 5`YbpLnaU+y3"r*#?4t5`E\h."ES붊Q1y9U͈RHHՀ1r[[^NLcvsJ]ɛ3P +bBԷP^u](P&y9U͈RHHՀ1r[(KJu} F2@?*u%oFD@e'BB PBY䳵D1 InaU+y3"r*#?4t5`E\hʲciK(P&y9U͈RHHՀ1r[(ˆ @V7#"g2J!!MCWVȅo,~|-+$o7ԕ9P i"F.4@} s>tれ]~/**q8y9U͈RHHՀ1r[F |ߏn"@naU+y3"r*#?4t5`E\h0'ϳN}G6CYGv|.DnaU+y3"r*#?4t5`E\hD+7<IDATsvoFĖ5ku/lvsJ]ɛ3P +bBԷ eUx{lX +üop5?(êRWfD TF~B)$ij-ֳfS:}Zߤ/x^l{V7#"g2J!!MCWVȅoAu4ɳVS Y^yk]xB@?*u%oFD@e'BB P?sJ?&l{z֬yu^>tMoB}+&_juJ߀êRWfD TF~B)$ij-YߊrQz:.X0рhS?JYUl}k?񢊸%O=={/}eeLqQ9u !g>+1wG%{{?Q:~׹ TxKnaU+y3"r*#?4t5`E\hCooxy7-h%#o ^#4,/KE ^0kԨ~mYߺEJ_P2z]v}+?-3mOɀ> 7츼 Z6~3}rpa6 I\doK]:oTtKnaU+y3"r*#?4t5`E\hCo%'WR҇mm4 8n\⸣Vn#z7רQ' );Go<=nn_k׮eoDuk[|~ggoon:w-0G7sN\}q5֭h}.^1c>۫Uz [v[ܼC /^_ll3h#o{]hy}C[?{ܠ %q^ֈERlіU}&d+/q@?*u%oFD@e'BB Pr<͛K?8p[zqѣVZ͛{J*[,jv\쏓WQM>Vn{lϜ9zWZ?ݤćwq+)"O&| uI/oѫwqٳGY3)nodWMp\P1aF6~׷A57|--=3('9(̰գnp6M6n_=>d;«3ŭ﷖}>zm'^mlygWjw~9 +{9~NRUƃhm?gΜ?n˖|͛?{}9jF?,\- 222>>>==] o7ԕ9P i"F.4@}ˡԷV>dm..ի;thd5kgWxɇ6+o[.]̝;ޖ,TTMmٲ)=e\_&_)W_]lxQ9~ {|^K3o榧''<wGԌNo?ʳ^5 xņ/oa~qr]+]_mgR[0~tw(nNՌ{\hѢ~ػwoJJ80AnaU+y3"r*#?4t5`E\hCon׿j{>;Ӹ ?78h<J?>r8㙙ƕ_zY>z}ݨ+Q?uZVRW 'qfӶZg΄8H~f0ҭ</*oaܹ7H3Q}+===11xTB```@@+'~j5={ie3byg}? m}ߗ],yԆiqwW=>~[ǰnxNW Zn~>… Ri80AnaU+y3"r*#?4t5`E\hCoGWUT-p}n]bI>gnxϺ`}hm۾PnٳGn[os>c:._>u^W_]d}/?ZmF&J^GתU:jw/_dp㛴٢7+.nopGc=Po]/[w(a|7/}|f32[>>>nnnK,/,ӢY:+ϑ\եǪ֝V>. *8oee ?ݻ?zgnk#(^|燷n}O:5ZXfܞq/ڸqc||80AnaU+y3"r*#?4t5`E\hCoOwJ*?|O߾ouڹZ|IG|M&E3k׮5}Wٶӈy+V|c<3gP|Ј>]Ƅgdlٳq3ƙի_UaI-ꡇZcFEe\d3nKxy~Ν__Vk&NPTf oh4-+Q}_?`@c3\kժn#;v,;^|?n/gdɏ(]y9U͈RHHՀ1r[eo))![,>s&D''xf_esfYXWʊ(ZnC :I'N"ڮ /ueLַ~U8j騾Uv;yS>.-ǪRWfD TF~B)$ij-.E+RS7>/}eԷ[~~>>>^^^t%K,^U\icU+y3"r*#?4t5`E\hCԷn]dRVﮜշrssSRRcKػwotttdddDDƍ/Z*.}QV7#"g2J!!MCWVȅo9D}4G28*}9{oJ]ɛ3P +bBԷUIeԷqT*.}QV7#"g2J!!MCWVȅo9D} o^*u%oFD@e'BB PrV%iԷoJ]ɛ3P +bBԷUI--LΪRSfD TF~B)$ij-oUff}θ*khѢeXUJތO(4 ] X#JҨo o`&V7#"g2J!!MCWVȅo9D}4[~~~nnndܘq{M*}9@?*u%oFD@e'BB PrVeh׷qssE1-7fܞqƭw_vsJ]ɛ3P +bBԷ׷bfL;N+n'W/-h;ڦ+>Q[/irdܘq{M*}9@?*u%oFD@e'BB Pr^ߢUpG[[/Է;LJ>,DIOOOLLRqKƍgܤqݗΪRSfD TF~B)$ij-27Dۚ'KFOVQ_>d6:_Msn䛰v|^qk۰#ԺYb\Xdܘq{M*}9@?*u%oFD@e'BB P*e?J -[e]qeMJ MP>]~k˻cg/;m-/9~XUJތO(4 ] X#U.~Su^^E%+u#iOV7#"g2J!!MCWVȅo%,[/m!fe]I upoooCZ5j ىJKnaU+y3"r*#?4t5`E\h.ӖGBVVygO ׆k_ܕ);y9U͈RHHՀ1r[L٫{VA⮇:o>/g֎êRWfD TF~B)$ij-\Ԩ$PoJ=j5 ln,\'Ny[dq?z>XTP(}V7#"g2J!!MCWVȅo%x. dOc4Qٺg{4ų/êRWfD TF~B)$ij-Ho҂uϼ_'b 27kyC\OgêRWfD TF~B)$ij-Ȳg]ƊQ-lɀ}k76 Z~>?qH<y9U͈RHHՀ1r[0'oݳ=MY<8p:;dר9^Uu6u3m鐨~XUJތO(4 ] X#rά]gx֔s[37QuF+YvsJ]ɛ3P +bBԷ ׹>[Fň`qE6۹nU=j53#n[99Uor"?4t5`E\h;fM@#9yg#^w~m{ǟ.vsJ]QBD~B)$ij-!vՏ.C<cUԵ.C=6uoC -TN'BB P߂I~ܼ-/_<9-}k?} }mBsSΉ2aU3"[ O(4 ] X#lo2"rݜĔ>v7nSǑ,>nk~Zf3J]]tFtRHHՀ1r[0OaNz5G<\nN桄E&Y.x3؃3Kٴ +[<եΈʏO(4 ] X#SM_q?x+I?ikzϣf5n91Έ@@e'BB P߂ٲ$m0tQ9E;b~5u4O=<ŹE6U*pF$ g2J!!MCWVȅo nS{Ђݜ3[徥ՏQ&v ~tXJ]ɛ3P +bBԷ'WGr#XAA!Q1 sÎ^׵n/zج'ijPJ]ɛ3P +bBԷ4G]wGǒ8y9eKHZyEaFU|y1?ݘ" XUJތO(4 ] X#glo榜\ө~{0z|onn WIIXUJތO(4 ] X#'=ǵOW-e͹珟J5jΆVywGMGꗤ[eg`V7#"g2J!!MCWVȅo2@[^x$o7eJ8l]o\ziNoᖼqGAylaU+y3"r*#?4t5`E\hf cT1Xy_cu}QG}Yg6)<# @aU+y3"r*#?4t5`E\h` qɿ`5vsQd{xSQG߉`RwŪRWfD TF~B)$ij- +{}^;>.q [~AjT̡=#{O^X7M辽ߔ?>XTP(>KRWfD TF~B)$ij-($?-s=v#QAaN٭{cg/Qڧߏhڑ+vKŪRWfD TF~B)$ij-%lF]~>_<됷M;msy{0ux!o7G}YL5vEpC9l]FqlXUKތO(4 ] X#e'7r@nHZś:\yg;!-1;+8Hx6RbU+y3"r*#?4t5`E\huXʻ+X]ޙMZaĊ_6vsgijJ]ɛ3P +bBԷ̸vdJ&o7G39SBnm| }uX}2E<)V7#"g2J!!MCWVȅoAih7G<[ɔ|67ա>ZrXa{?* J]ɛ3P +bBԷ'V՞y9Nl5zƗ{ruo҂DMygijŪRWfD TF~B)$ij-X@$c*TfY'w~톗zwGME'WGMXJ]ɛ3P +bBԷ` 'W/y9(p#(zج{]ʻ;l4%Ik(U͈RHHՀ1r[3u;~xODnɈ9z}mo\z]¿a7FL[zj}d~Zx6@%*u%oFD@e'BB P߂N].?^<'!8ScnCf5Y:w4fggU͈RHHՀ1r[3ioJV2QinrjEn.O#G1xz{vYov:$r8J]ɛ3P +bBԷ`=C\l4ҖW4ޑN_,8T=qG4}=u xsD13ܒ7DK< J]ɛ3P +bBԷ`I-n!ٷA/8Tq1h>[~vpoɡє*u%oFD@e'BB P߂e [5mh9P<撷\el 7]P~*u%oFD@e'BB P߂퉋=٣fS{MkYgͫNs`"y9PUL .U͈RHHՀ1r[0[!QWޒ~|{)f//dCAׯey9"-mw]}r׆, PYԕ9P i"F.4@} f;UV+iUXXQQr.vxkL_OoWX &pgU)y3"r*#?4t5`E\hfo̜v:]RK %ڴӨo;M_|o.#1ӖZ.C<ŪRWfD TF~B)$ij-^:^TvyT;-h&mOѥw C:S$ݚJ XUJތO(4 ] X#QߺfoN6(((222666%%Eh瑷#}1!3ʻ;l8rW\w6]<U͈RHHՀ1r[0+ooM NLL;yʈ9z}moB^u+lOO^trUxޙ4lV7#"g2J!!MCWVȅolԷ[G}/v2b9|VHˁ^u]ZX&2,79U<ƪRWfD TF~B)$ij-֕VoTy9e%$x ?[ۆgO'Vf'*ѝpA*u%oFD@e'BB P߂٨o]y!hRwO}eOV>[:tNl8,I<U͈RHHՀ1r[0+oԷ>|w k7ozicAF~yx6U͈RHHՀ1r[0+oԷ˖sl_ :ǫ7=l3XUJތO(4 ] X#QߺF} (ygN ?yѦ#WFy-0malU͈RHHՀ1r[0+oԷIR3n=7Fgm1hz"slԕ9P i"F.4@} f3]_a5듯R:nZ#?-tn]m s'@9ԕ9P i"F.4@} f+O}{O A>V…e4[e(0;yDxwM4꺥glY->.UeKHH2eۃ_}ժU쏳?S???_~iLK?|͚567#"g2J!!MCWVȅol[߲٢ozGiX o9:nԷ Rw'jkzϣfՏel 2'XU^v>ƍk߾}ƍ ~']v;w}wϞ=Ϲ{^~u?XfD TF~B)$ij-[z;mkkv0-no7OW׷,ߦM믯ݠ!{?vZ5jT{ukkDƍ7^_nl/yV v\֩qS&NPE5[ls/1h{{vN#L]fst *+VߘoDFF,**?(oM>kپ}=.ԷzE믷+9P i"F.4@} fӵj]s Fif.. ݱz{}ʳ8T\Î9ZGM ng=x3gUVOKF ׷3ćw+V|SE5[;r췵C yW]W;څuwD)* V'<<ܘoL쿍))!ի_եK{zO>\2rmGi,Է{710{E׷󷹸4WСݾSGu~4ufܱdɤpt~/ge˦<'q2-@{ygNMFѳ{x+-*d[DaNGͦb"..'Rʱc~v5bĈ^zUV׷4]|B7#2s԰lybp#=yAB՛QjcBԷ`Էڵs[Nv΅^uU9sF[?T^]AFƦ5xBYL#+.x|D;:v|VRW__[awi[Z3gB]F} l27Ϙn~_]rơ1c>j⩺uL2hqwuq{}k%>}:n9oٟb\qժo326?ZjUGzJ5Xq+Y`^f߿,w1 ܘ"e4[@%W}f"?n5yM88=e΂l TⶶblRo>~PX؂oɈ_wݵk[ݺjF߿sv.9}7ʈǯzG`={7N3"ի_Kϖ-iQcG_l{5jtό/QP-/?5*&nOԀkzϣV:w4fҤ[ΦO'v( \}Ν?c-Nj[ӧO?vm'N{>s,] }mX1 Ov:($M9S3i7$Feoa۷ڵ|eo>uDs³ 9⟟eEǯ2Y|YY?n=tg\]|G/gq-e(v]=dfp^׿h,:;aى)8OëE| cbb?.Pю3LY,F5zbpu_(:{b[0[[[QpI2%$x5'!>[k]#K ΈUt_$Rv͈lzf C( \QHp.ǺQj;,?rolW^#F(<+}2%/tğ:Xyg;Z+g/HWdO e-Fy3C ^׿XTP(O( Ǘc+$y[VC} f qj֘iK#[5}S~J~0'O|3mbZ:#Zqk۔M;(mN[ph1 ;$1 @m)7lԷ`6[Wިo 3L˶n׼w7ɡ';:0uΈ}8yϸ(c7g1 8>w<1 (%"Fd@ƗQjolԷQ`[^~ꎘ|~=uYuo:0eU)'~vtj1 -H\o 5{aKϏ,X)F!?0_P۱F=FVC} fu'J{ؘGr-W6au$Q<@~!JB RgDsT<8۩u[C^/FgXB;($`+(K1 X -֕7[qX ߍ{37pU5E!3. HWdOPM/NЂ/%eI\yg;1 8-/g'QHbS붊Qj?yѮs(`5Է`6[WިoPVޙ5|4ut=r{ϳfO*) ZQB(/5ç^dPKQly\=uH< Z$1 @mQLVC} fu(>ۘ4G~0)׼]6w3ts$i(taŒ(jԽQVFx1 +u5#F!AavGͦ|l`9:L P߂٨o]ylYr?۳Ns{^7f&-H\tF|nQMЅ 3C \'*Q׊Q\s1 ].b=Ln1 X -֕7[sG7<ا^+`xl@/[{N#F sfD~"1 8վI vNYb/mwE[03cک`wkk~-tJ3z-eLI 7i#v>vxojT-QV|n'Ԗ933 'ӆQ\SBL[*FeBԷ`6{}vm/"""ŎvsvsTi~㾥k}ǘ33Q uN/Иi3Cs<6HΓ}"ٷAk1 Yۥ xbڲ$ND} foVۚ'KFOV5~njnEGG'&&<lly;bh&vc>[]m>ug*SQhQꎘ.C<8I[cY%FL5jT1 C=oo$t@} d,򣣣#"",·uNb{F&&&xd5u~|-={g_3e((b(79u]Oc (*%NqvՏ-F!An9|6h}"YD} 4 (8/_˓{aUc}w팵O*Ȇ'xQ)3VJ$`O?oYbjSb?ֶb&[%}ܾI (rygӓn殟wk^|'%>,i^׵(@/Κkwf+ը. ԑo8A`w7`˃Q;!աb&[%ˈ=S5_∳vsbn?w=ӳÎ:7iA_(v-deQԀaop.> N7\RQM{sI:D'@E;mMbl]1 X-2Mm:#xsn("}ߑ1gxcː~stI]﫜Vƙ{(tŒ0'/E!3Y>u[( oUڰ>bkw߷˶֭o}۾V ZhQq]QqZAao=  af?i|}֪繏OlMn@H=8]jtji),cGfx%-Lxnd9vL74+@{)aɏt)4mU$z6 BWJ] qG,1RPFfT:zB6z] 7!%`MusZ Q7ev1šq|60{QK„U2: X+[@GI V+ѩs%.wH\q.9a/] B["-ye{KYʷ;JYu Yˤn:] N[DvY27@''&] "'=y.%bq^A~ cUM_0Zziu8?5aɏx<;y1Ӷfr)Dz3^c[k:h]MM? BOg4SH[tfaiTG$Y?^I=L?ot)A0i$ɼuUrmӏ{]sL ݑ_ ?NC-rl;㹑OXz0ɵ:"SSJns>V" h.EV^n! oO<֊G " el*+b.٘ ș;@r5[6f)]jdo0//qS4%d!Q3v9By D`1V瞼`YʥCOMPrޘ\M_F'KT&Oɥo]V|ݏ.0")))!](љ#>,//olhFAW*berb4hb=xVzÕp?] D_[j, &f ܥ"'KSkY5Xt$ע:"!gg` z̽ȉwd^-`L:u~69GX5@*iՑ)tqI|.PN1eH:4|ߙ.[(0n_cV34}^4[HTØK&{wFG#-qkZT/tV3N)va䷴H_S7V4(ЩBF./FxB.ISX2f:`XDA[Jt@T w9zw{<723'oW'jtLMt*{N"8oL.D1W`ZRDEv}*gTPL@?EMޔۙ.08ot!߭O@ZUp_}O^1caiѩf@}ZN]74 RƊ` ,Uɼp5B:bg;F!e3ZM3w~>~y7S%@RTaf?dLV{Gf3}9"qsw ݨi+ti[vGR0Vߺk2=heutOkV+JNIRVY51GHIrY}-.>tZ ,(Bu8=@jT݈;۲{GV+tIfͦ|#f-2Ty>t OKYǓ](xy{R7K(-|ޚZjh&#\ĸwj:`"ه t%BY_r+0uSu^1 XEF]B^1$R0nƐҪ |<7l]]b6xUltj2EM>ќ!O2$Ur>#$ރګsv.@WQ#'mH?A@5V$r->|F=-z}˘KTKY3֝_'^19-B: -FG4AG1)ZV9z1s5ѩ -#{m CƬRm碬6p^a(icwr(ohWOqkJgT--Lwփ=Вp0`W# K~.$:hm@(xF-.՘#dWF@7jRθ'<E( iΕ7dUwYE?{3]^/ha~KKҖum{5CqM&ziT̴Y1^jj89e*pcoik1{m9VG2 Y}-ZkGbgK)cE55zb_ `b$!=9DW+tt[F W"k vK=ҰϛS7PҐYA[kS֟b6O?Z=?EMDZj|U&`(Tr=rޘܘUD[IV3tt]n:~lPTwփce^55jK݃2 Jsߐd:3߸%I9t1rXn/b3ĵk3^i.s-(1@7#G&VEW8ttJ&.v֎K;UD7gW'<_4 xn99QM H3>Cڒ)e] FChx@[X=R֟"0U_{E[OWk~ttZ9zuRՐQP\FS^4|q#yݪA[!'M#Bfpq,[U0fjKt5ĵt KK8blSrqC~ @'HAJnVj2fȉUrZֺ5CGd7.b}SEp$|^[:9i{e!C1V0t3w$rK>-*h=ȿet J&9rM3!@_ +-K݂tU,zc mU X[?xn߻3l, 8SA~ɐ햷XQ1w9lZY‚*F75BWJ?93{gr6a084|1iW-Ҕ/19:.$olhZ]풤+u'-ְz,%IBp T2yE>oM=y[DCZVh)j gu{00zu YQ7#x! Z*cE~>?"PHofn>CKUI_,5SJY]cuDrw#1z1XK^{")olׁ'VKȊ6f 0TVqɰdrrHL[ep9@s^"Gmel0p2Tp)A#0t1%E}*{c#+ysjKt@k+b4x->b &ރ"'Ot$YkjOVYG~E^z4p(<Ÿ~D݀P[ 'VƊ[«8*4p?WCW?R4FMbb^SOsgX^fcT *cGf;sGGsfDOݒYڐYV]NK\䧚BK(ic| ĭO)[Q+7ߟbai[y6s\q jrycrCFVΉbMT5dz=e߻3mscg9rTXFouaʺ֑PʃGRel)FF.+3tJ訕pN(- inIU,BdR㼅K=Mi ~p![3->!KK=Q+Eɟ,/k_DJQ6@7dQVL'kҞ+@~ @ו d(ػ(+{e]B([%5ԍNy6E y߬i-1>ou el,̽Z˪`(k9CK݃l!߸t:`LM9GoPA󵔈kSE+Mj{CMm>=a=Li<OrT&ǻ.8mҘ#-F~&5*]Ȗ@=f M $K.gm+nk n"mm M3DW K~Ԯ^ <[epyrXЍՒ#W(5e*+A~ @%dy606@S >Ξ6>"#yqr7A'=*ڪ$:.֐Q;56jC+fTcQK@}ޚ*K^٤cglHǐ-K$I)cvKt|Tk^5yo_6eU';boj;5fw$'o2[67fC8C=^ojy F2xK]VyNgX[e];2m۹ѫ<{ H.Do O-^S9q}WH36ɛMt &*5Q+ c0>u [B^ li~R7:5f5@ϕzp^BG=}li3t8Cd.grd->T tՐ^ bo2Xx4*3(|ه LՕjO&: bfDxoElTHYT.*apkkc /S֝ c5ׂ|&/ q'\Iˬ-k(ysjjft̿2$1[*o1zVMU{D$Rz}V敦m;4!hg&.-7?-7=QQ+UQV1C'm F 0<Z3K{pӑq^rǷϊ)uWG$C~[sB*( {G ]|of␦gUY}-\hłPS[Hd-bGy:^&&nPH[ӰBZo~3fo󠯗'8 4z g$߷1:vtJ[e8 .A_-zy4߸qkO83xz@~K ߘxbFUa|觔u'CLlUw ի :`ګ%i[2^h{!? &&-;ioMtETU#8~h?/x6)r:)SGH"SksOh{~PV3}z)fgڪ$(xu (9ay«> ˅IzgQZF6TΉf'ni[I+h3^NድI2j!+٥}*퓓 XEF%n@~ @_IlWcX?&eQV Mt ti{G8^|<݌N]BNӍn$$=7.=qJdrttXi7w]˙kl5t~&*&ob6l;sG+هJ61iAZ{ь'=2/F8h*i3^6{?_3^O^fN -]FZ1vx<:j&W(FG怏fJ@'r_\lC)d8k[|IVq@\.t(DGHkY5]࿐{-{- /1@C)dǼ5:2M "K{MB~K/(-EW8IqVJknk ^c*x~2 X`BYd3mkcoeȔ N/zhB+CtUA~ @HrBLlޛY{א^ȉe t fTCN&`v/ S֝,xuo闖lҖ N~>-1G?A@gDZ~X.c(W|PI?4bM_~0(xE VKrO0&Šrh5쁖ڌW363^o)I uV~@D# XU鬾 t3O _\DΝƬ"rbc=esۚɴ0 < Pwdg6.VF.b{3%:FX|7hfXW'9Σ:"^q jR?8#MLxsytUm|&80i^ioO+c2dڸ772^ /b*S@~ `BMm}N?ѫؔDO?lp7𧴈*brON=ƫcCF.OZ*o> Z z,ĶW{&&Ǣ* ,ꊺlM:?W'olND:*yCi}m; L?I^{# A0pqS6w-t([vG_d!AgkT$q'[TU^/jQ҃7}:QXwz4l mut%n|ϓ>YΉAR^fX9Rac\T^|/pGs<_4 c'1hjtB"e)foi[+C0sޘ?^JL"I+l"b={-qswVƊhjk"e"и9^:jey2kQI է79kRn?drXP%f瞼3m+{%9Lߖ{U`rzoEs+9xojzKV?/ɘBRU 'uS >oMu爀0M~̠S\;x+[G&1$Lmz` .Q7NCKԧ':ZJ+cglUrK ^Zu Yt f|ۮ?J8{3%vl<_0ajj1k,ْ}jsx3X|4'co×1W3P!&bJUI3z=UVjRsGYody˳wvO;9´-g_M-*QTyݘ},\ %VD\{v#!#3{k&v֎?zԧe읪#S689{heH"vS]&*עEϙ|d˧CLl>g`ghʺA_-" O)$ 1@PSۂ:nh(zI4G$kI#m"'1a>r Nt̟w2 r!Y͞R ڕYǗP%\9O-M=7{ Z$ Nb56zcF2"FUa`l)2owע'&Ozf*ԼR}cgT3!yHSMpfOtS^ tR*g;ٸk4dM(¡A /'ܐYHJur%͌ cDp*D:j%98;qG[BW7oR+b.1.>0t#Rޓׂo=TKi8 .hX;KgG|4'f]%\eK ycs ؙ;<{XsZS>KWy*;&8qdI3tJ}cꕆŸE3Q`۹?VDWgoʪ0f =-mpFiWѫ2 փIRNɭcm hdz#[[ur1φE.%.w`{wj2JAž>c3'Vn>13O|<7z:9}#ƭzy49mW}j-x<ܒ!&^ccg^mЕ:N"->CBC5U:jyYn̎9u욘zz5&i3=2aɏdwh4]p-->V(<&L7gWeН{3XtLIBD|-gؑ[diu;^բ#Ur]I)[3v93z]FtXlէNN!w&oh 2fVWcz1(j&VEpbxESkeH"]GNxu g59urqB~ iIDf.ic>ҼRz5|2VDrV!։wS+U Kr\VQ(DkY'Gj:?2 cg`'8~Soi r'<_0 vY'#M\KW0>o@GR4s 6{~ᴔz1 /}o=TsXwƝ4" 73zcFgfMT*r]!0PS[s\1S{ͩ>Vc7hH/o\uG^UU? 2{C/Wx--WU?h‏V1e "FXΑk^mh bg-H,QM!m)u&_{[Sq݅:^&QkQʥz>%7zfVt[IJp FI뛥)OBх!]A*(&' BNߒ{U,VU(IrW5V ` ,P2Y|/}i[>wG,KXz0Ugh}jn0-i{0W7x9cWΧ.1;j{e5B\ cNZLLN߁tֲܤUziTx{ҬJյ{kH13Z\d`vt0Ɛz0~33u)I{㹑!#',s9rMZy:L"-?/߻3R7:Ut"#[#u̽ZJYw8 7_ڤs>if&D txBo@wKؑ$֎1V(o|2mewM[T9q=guI !B%~kܜ]>oMz,bڌ](Y]#]YS(aAF;.%3? Gj- N(H5y=hjj!֐^Ó74zOd/W|3ZB׃_e6{_.DO5?V4枼=*ln˳1go h &!߭HtVYGW(Y]c9'*csĸ^55n.rRΗ76ӵ@O4m0Xd /C~ɴ*xR68EYo?GM*qš7ʘ |4{FYmaMs $ _@RuD2x]&%6y]%~1UֹθKj  -BUǵsh=-f֜7ā< coc;!ՁZJb./I/M 3I oHj:2%cvð1vه\tUc%bZr逑)pfuepIqsv1z%9,MRէ=R}~ 'd_|;RV@΄l!9LD%^1l5ySց˽#ĵtmVu YQV,'n4 :[s^Oi^ XX[| -HPBRƎ Ҝ4g|}N!'z4(_R`o rXܓ MtV%)cEm9jj |X$[ ][` D^iENXc56l /q-T-p^?jXX`DedCZi[LKHː;lQ;/_Mo4TZ^#`{->P|ßL"o:jm*-*c]Jt o?kai_5C2sObiF43_ƣ:@~ P{M=yhg6ύ}gz-{/aRA1t(\Q|33go_C~KwWKj2og)nዙ={|2/-\dT5Rc0i9{bGJ݃p5J{U/ݹS׽ȋ'o* Z8^uJUhR̴^cfb'۟ { Ԇt[`D;/FMTL_,-ڟsFwdS^ȮU3/U>th JgrIX@vYޯMr׾oOoQpV;!@Nl4G$8v#r:M:9'H٨:kQ*K9r*#k],+C/mu58P4uL2eK[uDr)YǓ< %@/ ƈ3 N ȀOܑWhCf^ߒ ^㻧YXl꘸y{Sr0A~KaOti#'m{oZ4Kn2k Zvp c ˅NJ!mi^v3vKyٞ=F4o+Z3m+f K>/j ]|V9~jwAK{ps}x4-mdAmnsS67ߒmj3eujF:5{:^fm橛|-C~ːUWG$^fo?;s˅Wx4J3-N\7*QȧrUAR7:s _5ng7XF_%jbyJ! m]1G柿4'~bɤ$ ߘ=?3Sbg=ZWx8rՐY( :Ka= _`_KGH}jFcP|Pek~A_-:%Ubo?e) MtUWxcA|0anA &/;r;#V_tA~ ںr:}IDATm܈W$<"I5rGV;?;ӽ^Eؑ!QmlzNږo hs#ك&SU!ИiãajVn>cai_Ή2 -#V7\^EOb6^/f4 |~m Y$H5ydԬ|$yJ=B*z!?3x([qo-(G^*Xge?/#S"᡽#S$p57|RTeGN9^b5U ٔ80>oi!~? 7y0T͙}̽zQ+|c"' Eѩ OVPUvpIXfɮ t2!aAR^r٫ZB %iA^iF[zPxͯYXAW7Vio0SGJ*q!FO3}M6<ޢ]6-~6z՝_T̽`v??1"H=e3{%EC.!O3!:97L);}DžY;4{wPۘ#63aW bAv\<گ*x̴bflkgӀDMޔdrln1֧73}xVqswwQ]!Tryͥߐ?eWA=Ez%W1b߻3-{e7KNyg$}/-)-^1!՚TU$GјUDv(\Ac'vّ7yQ+N]rOf<{Є6ۜ"5m=n[ &*Ux/se!߭~m_95l]2 mU\ly&۟~Ҷ+D=]w4̋ph*TrsO܊ޤi`"#i?,yhz+xb =S.Yb${v@N{Le66=9C=",3T#٭[M\{Ʒ;e!SfosgGѫx lq`|cPJ?! ߻p>}߂V(ybAcgjf|ّW)bI!P4V'd>γ,‘ 骿EV!GMn_Ʋh[w|EI9 ?O6~*o!|289->>䤑y PO ߘ\}^c9CC[;kGSc7JnBB?^ sUtRJ>R` $H%|+T3ȉߟ 9˅1ӷn:Σ71QݨF0J^g%{ӏUz /Q>媿|;6WIf3.'yJj*׭oA {ɮû~7h\XE?ZKFQ TH:aGȷq@%[jtcч(`ҢY޾ao5[yQl3fc~h=mv?Cn/fEwSe(!ʺo[rUaF۶-- 8xpWIflٲWU=SRS)n| [X,e_4{qkTg%nЪUÇ̝{k>vuo}!ȷ1b_ W-Jy07ʷ{kũ?";d Z ѳBkض,?VW^~gߏ\չh5xp=6g-ӧ_1yݺu ˛.]g|Uz̙7^}kVIfoA&߂˯VLu [ܳ_?v ۷|nT Tu>*Y/}šp_ty=np[մiG/\8|{-Vx\".H +ʨ a٫(kMGKUg`[O3l, QZGD[ѱѭ*lF4l?jNl> ϶gwΞybNa],zszV,Ôb+a6*Xu5x#hoza3qia{ӦUǏe;kQA ڵkӢE>}3f̈ʺ%TY)S-<_C3~٪;ew֭_ؼy*foo>|Hv% {0Sm:<'%u+qȷ ߚ=w6'Wno#G=ySJJ_D=}w:XKn0i9 ^O+dE_ TrPUuƶk.~o%^\kĖ*kР:TIK\'֡-5Ov9GG7';sQ[_~Z͏>=nIaƌ+:=>+V0`v#7,ZtWeG6ktzkҤɪU5ol„A *f]t>c8キ8jO;O>9#MA**[#߂m󭒒Cخ] /}C|{Fuݻw:Zڷo۱cIN5+|$ݒ(Fh ̾n^4\M߄X箽t.}oe^!V9WVz`֛];n:MvtGO߿ۆ ?-[6m߾E޻̚u|˜>dvزGydD^T4yVF'GF]we ֭m~],9Ә/`'SfNGv>6|_vIӾܸwѪ|g]·.̼1Qc~=8ڎ]J'DI꣏9aI&ah76cI͛y}W[h<ߪa]#7Dݻw:ȃ6n̏u %߂oARa˶N;͛w{Ԟ16mZ];/94kt{_$t喯 _uKH!K/{C_lߓ7v)t-_>+:l^W4F_xӦM,9㭷ƷoeăFh?.+U\<9a-3y7嵐ClѢ?qYYٕwN \`d8m7ky챽rrN9]޽W8sq| }šmS{CO?TV[ڵ>|ȲeήvׯwثQ$}/l% }f̸"=f\bf6}v̾iӪٳo8#}QM7M 7[o.dJ!߂T–mN:?*rHm9~|+%-]8t1`ݒ(*!ɁR!y7.|ʊ &5nzuibk.9i?.:gK[+V$[oKc;)lYsnWݹs>ATncsð?~ʕ[Ee޻pVM6l~6M ۷2ufQ{W83gZxqAAAQQQiii_ȷ/y1/ǪwѪ|9asg.]a3?5Q͙sK'v=(I [IWkf\m֬bݪ̷*}W=s玗_>hj__$$)T| Pu [OxS_؛n'ysl|+*;z]*TN(+++---))yg{W_w /Z~g9ݿ_Wc^p^'G&M2}wBX>䪫GsZ56fgo.!ߊmqǰ:ɷf͚;֭ '6[EoXY?Fu.Z%[_|)լYu?ߤIϊ]Jgȣcl9IX:O?]^1 vc3~Ɋ-~/y.tKoU6O3Μyok;_~#6ߖW6HO :K-qW_9͛7{-nˀPof}ܳ5$|+*;z]*TN(---..ۅ,YdyyyOٴiՖ\0}99ZlQ֖+F@uP:!Ix9++kΜ9 f֯yWMYr]{e6nb͚syg뷵.]ڼ9sNȈ`|4zt}ٹ,tNzG]кu>իa熉=t94& [Yd:;;;ٳg/\_,**Jk;TՐ!flӤI=>|矿?'7f͚͛7}0`8l$룏^IӮ];i_~ݺu }vکC]תU'΋3fDh 5qi'84c嶯Zo+333;7wzA*ʮ[[ЀTزmc~Ik;e-9&,^}}v{~;=WE=߯˷DU_T[Ee#Tv TlP|pk֬_N|+o?nEέxe~ܬYŧE.-F<.&=w =|12`@.]ڄ1;thsJԾf9}tئ_k׎㇍ߞ>}X6ͫ.yBo1[@#S͋VUVY >|ƍUvҗ~ ͛_-Pp?}O˿jM7׿. Ve׭x-h@THXϭΊ"*κu _|D_+ew7|{| "-qѪaV@<4 Kj}r[Uɷ "jp v@-h@ XuN,^ڟdh[@#UV^t%߂dЀuB,oI| hd\jJu+h[Ѐ,RNo$vK42.Z5l4R-h@ X)^'v̒oȷFE_FJ RA a0Q.L:k֬;ȷFEn| BV[)8oV [ r 4 KY'eeeڑ G=ē;ȸhհ ׭ёoAThjuBqqqaaa+777++C8pñ3xR` ՛*|[A#߂dЀ 넒0KAAA.///g4o8p $ȷF&hfO-q 4 KJKKf Ìv H@I| hd6ӏ_Xsz;]9Wֈ*;c`UYtKHB ( RN(+++---)) s1# :{8'v-k7ظ%$'߂D#]*4v @ ȷFl}MvĦ-q 9l'RNR@4bF4ā[hK:H@eg Llҋ| ^ɷRMiOP[&߂'W-ToAړo+@ɷ ɷ| [[Jj-H{-z%H5=@o| Ҟ| ^ɷRMiOP[&߂'W-ToAړo+@ɷ ɷ| [[Jj-H{-z%H5=@o| Ҟ| ^Uo͘1c9 ;c`b^)@4dg Llґ| To|v۵^;10 HGVVV^^^~~~QQQG/j|+޲erssGG#t>ISSUaaaqqq-j| |dg LlQ$>OOUׯ/))I@MUoa#t>ISSUqqqiii-j|$|+**Z@I*| JKK?nPSU[Թ쌁MT| [!H5@mȷRMP-ToԆ| [!H5@mȷRMP-ToԆ| [!H5@mȷRMP-ToԆ| [!H5@mȷRMP-ToԆ| [!H5@mȷRMP-ToԆ| [!H5@mȷRMP-ToԆ| [!H5@mȷRMP-ToԆ| [!H5@mȷRMP-ToԆ| [!H5@mȷRMP-ToԆ| [!w.Mȷ 9@{>~aMl7> .UoԻ;1+o}̊džG+Uo!-hsdqE_,iqX%v )@*|dUv5a/ 7| Ev܂|Lv<.M@U[)ӳ3q|39eՉ| uo}Dn=ߨg{fT| u^(zwtT| uܸi~}j_}0 |_l䐱Z}Nj??M<nkf k)D(zsƯkyYﺣ|PoN| ɷt[<{˖[Նg˷| H7Wo=y˖-+((X~}IIIhh- ȷj\Q5_.[844n[5(ߚ=֬KUgM@oFU[>hNNβe֯_x~N+]k;- ȷj\ϷgWrZ q@oFUoI*~n[5.VTt\KF\/MQTTTRRRVVx@Mɷt#ߪqɷbǵ-:w2F!n[5.VTqEBuHFoyӦ]V=5%ߪJ8.9novXmZj1|so-'5loOMɷ | H7̷~cOh߾mff_3gxoJ|k{ycXW-|n^@߶M;z‘kYq@oVE78!##WkBŷrQ}]~0H-*oUWXa{B&Jb8nW oL޼^Ϸ/-}2d+84?+T>5a+$Uo}!#M ٳoUiӪGK_z鏿xukצE}|g̘eȣ'O>#zJIОhN=z]wGnSk͝& #O26DoU2B7 >Cv]rƞ??am۶OI̷bM|cͻdKkbmޜRe YgP*tl$C;[!ͷͷJJr`vmWƍ;i]:̘qŵמy}W+fnuy-+FO裥356l`-w4u9˗?o߶c&6jBW^]U[P\0N_ק~lG}|'gWSI>Hu5ܞs͋&ܸ N&:cŨs^:i7 /]Rz ӫUf={vX`M׮~M;:'mÆsGЖ-oߢw]f:>a?2vw;vl٣N<2"j/*xsP#Gba;vֶ_KQtkiq@ɷtS|k~{}233;th7oQ. -_>&ׯСG 'LoijyΡY%-|[I^F'8+:l^W4F_xӦM,9㭷ƷoeăFh?.+U\<9a-3y7嵐ClѢ?qYYٕwN \`d8m7ky챽rrN9]޽W8sq@mȷtS|SǏj׮!ݸoٲ222vݵs~CG{=z[[ ?嗯,-شiٷLxڈ>&wL S$Š}kލ=}5.+F{~y=#t̊Qǯ7( /oXouK/7擄f9_l]~wέ vQIn7 >)+Wn+{/íZ56aذ}{l4%lr֭EIK^4Μ93++kEEEIɷtS|+?wOȸ馉QҥvzM3 RYUoeq,Yx⼼'|;X|ϟYv/`dmsr~u5l٢n-ի^xᩋ]tm)ĉO]wӕ RY Zxqnnn蜕5gΜGfn3wͿ漫,4y#4ݷF_ gM9{xi7^f9SZ]wx.mҜ9'dT0>|=>\RRAlW_=.OhݺL xsD{,J2] GNٳ.\/%G $%M5!C0`_1eذ'+ƍ;Yyf3(STLxJh6l`}ݷgsקͶo] O[70>Cn1cFP'vC\ۿ ͝| níYB{xt~9Po=#{dl;zS-qfu袃[lWLtpcz)|۷ˀݺtiСeN)Q59c~];.j6~{am4r:{- T| H7̷oLhg7n̏-: _>W_)*z&׿. nbAW$<zΝ;|+>T=ymwߝPVveB{?G?p ʪl%ړo馮jo~+77w…9qΝ;gΜٳgϽ[u[lo@ɷt#ߪqUonݺ‚_|:W,JYu* %ړoFU,JW,JY]| jOVVE_KU ;&n[5ͷ*˽[w ɷt#ߪq&ߊo$@ZoFUJ}c| jIV+V$L& ӅIg͚5 ڐoFUo| jCVA򭢢Y;pñ3xR[@oո$*...,, sfee=cG7u8pO | H7Q_o}VNZ󭒒0KAAA.///g4o8p $ )n|KոfO5+---..^~}aaaqَ!i8pH<)@R- l&?-wλWsMU[畕‘ G=ē$%َv׻:yvN;][9v s-| D@c"1oИȷhL[4&-| fs(IENDB`kea-2.0.2/doc/sphinx/uml/assign-lease4.uml0000644000175000017500000000311714206773363015242 00000000000000@startuml title DHCPv4 Assign Lease (Kea 1.8.0) agent "Check Subnet" as subnet note left : entry point agent "Get server id" as server_id agent "Get hint" as hint agent "Get hardware address and client id" as ident rectangle "INIT-REBOOT state" as init_reboot { agent "Get existing lease by client id" as by_client_id agent "Get existing lease by hardware address" as by_hw_addr agent "Get authoritative" as authoritative } agent "Process hostname" as hostname agent "Request lease" as allocate rectangle "Lease allocated" as allocated { agent "update DDNS" as ddns agent "Send ACK" as ack } agent "No lease allocated" as failed note right : exit point agent "Send NAK" as nak note right : exit point agent "No response" as no_response note right : exit point subnet --> server_id subnet ---> nak : no subnet server_id --> hint hint --> ident : use requested address option hint --> ident : use client address hint --> ident : no hint ident --> init_reboot : requested address and no server id ident ---> hostname init_reboot --> by_client_id : has a client id init_reboot --> by_hw_addr : no client id by_client_id ---> authoritative : found by_client_id --> by_hw_addr : not found by_hw_addr --> authoritative authoritative ---> no_response : not authoritative and no owned lease authoritative --> nak : owned lease with hint mismatch authoritative --> nak : authoritative and no owned lease authoritative --> hostname : other cases hostname --> allocate allocate --> allocated : lease allocated allocated --> ddns ddns --> ack allocate --> failed : no lease allocated failed --> nak @enduml kea-2.0.2/doc/sphinx/uml/packet4.png0000644000175000017500000065665214206773363014147 00000000000000PNG  IHDREs)tEXtcopyleftGenerated by http://plantuml.com09iTXtplantumlxVMo8 $P4kg|5R#c%)FCHF̐yGm2uY Ab)'zVhR2Cxdpz|~|rdfԨ64IDHPjGu7H`<tug'g'ǧ絀k?ǣ0?%%[вVdFP䚭jaxcP\Oo}9U,':_܀jзd F:dNyrK_+55@D2Wmhm IDATx^|M RjuRZ[EvkV*JQԈ]5kD B DIvw~7&WR"><~?{榏GߎU*e O"K !!!Ϟ=S %''+ |"&$$䢚7o)^ݿ_&޽ӧ7m4m۶)))/_#((迅P<ʁwb*V@Ó'OOÇʁ-55uڴi/^}< =z$:;:k׮_^(2"?lٲ?Ug$>>ɓ޽{LMM/Z!@AG t͆ TUZuJe?ک/m߾]_IMMݴiS% fz˗/lٲMqqquʱw<7{5zh}}})>fϗG+Vhhhsު+Wٳ\rGY!&&E~^OܹSnll,+VÇEʕ+ӟPp]#^-Zطoߎ;LMMGQHYF7oe%5pɒ%/^ |ƍt"z^UVw^fMΝX㥳i3iҤ[*tS׮]mۦxlllOd„ ҡ"}yÆ EMc*U PBU\cƌw2d8ӧNtkO<9sLů`oǎ .$< 7nԩSGh){:;;C//i#GJj=CMWC>@o\;;;qORJRz+>c}'ONw&W_֯__T>yD: M6M_?nnnM6rEψ#;w-Z믿 2\k2 j\'N:LJOYmy0|ӽ{>}?zHܹsEÇ˖-[jcǎ^qcΝK(oNƒ-[֣G+,YE$:::z 4(VX7n|ygŵO 4hP*Uu۶m+Ž;>3qzrg{֭[JFEEM0/(R8erb(^dlŏtbWR9!yC//~͛۷OEZ?T\Y;qw`uùܣ.ZHСNZn -vMT#AJOghĈ`ƍrOLL)-Z466V5jq\P`]YP+lR XEe|x߾}Ynz"ʕSF U۷oKrM6=z_|!tM*ԯ_?Q`gg'y^͚5#""xIjlhy$-vSIfFsSSS^:m4U~ׯ_?sx˖-gѢEҡ$b\UZ(z=*]N 7>|(6))|*ǍJ~*;R(>}; &M$]W:_Tit^~`ҥ $?+m7,ܗe6Lڵ'e(|٩S')=a-H/">'5y.5Z>}^~Y~ 111R|(S'MULJ\b޼yuرckwwwU#uDDDʕMӈҞI~bk&Oܮ]/hѢW]ddt;~~~K?C==ˇ*ȏg+ղ~͂(SP!^ Z~ݻ{yyj0U_#GT\.jsSRR)RDhc6g)SUi?fϏ[*m{o_{Z>x@:tvvm۶U(s,ݷo*m\)WʬL[6h@?מxhwҥ+U"_fΜ)]ZlQPh֬tvvNHH(Zhɒ%.}I#Gw+fffY=zT@wQIy@f?Q\p3::Z,pCZKe'ڡl/6OP,?Tis7o. Ξ=+yx/m!.sk~6bovH:tHS6g1*N{݉q/osNև| \k2 j---LpΝZ^h乷n*\0a/ ۋ,֪MuUi^^9*kvY^۷={J\UJ9EM&MDǥѣGFsJCIi###%ҹS<'+9w/i_\s2/^,Pܗe?\~}E?2= ^`3e1-[J۱t;vXxV__ԩ/&$$+WNo޽~%ySSӍ7V^]YF*%~͛'L*WP黹… LҠA1<JJ X-dO>v011ٴi~whJJEqۗ7Ƚv٨pΝE7\*TS*r_ڗݩSۯ[jܸ(4ikKOƌJȧ(ܿ_|Z#(\V'SP~?rwwFVNI⤞@Ѷm[QekK4OĴmll/ʕ+KD,U &\k6m$FҥK#Fvfٰa;;v^̙3Wc;v(_jԨQ(L0A|غuk)Wnnn.#L"O%HTZU"=zHpJK{7533Q4c_kׯ 5jH:EWޥK 2Z={liUtСrΨeHH5ۋ!C=ӦM1c=zT@`rAGGGP?*]V>yԣf_$B6mQ G=x3DD𔔔aiUZ(=ytƍ2dYuţGջ(hXXؿ+1'::Zzs b;(>>ڵk_>&uVfa,?3ґʁWܗegv7ѽ{w>,]T9ɓ'+VQFf;kf_xs*ǎ;s%~'OT<.ҥKׯ_?11Q"O4i<~xݺuɡ6m*Zh"E]t|/+PVF+ *\$}}T%JH;!?W^ˈpuu S`O:'([[+V 6l˖-E@@@ y.?@@ y.^/1":ٝ-12Fv 8[i9B]ܕo7 \]lA{[t5y.<'! EssIye._|5y]8e;GMs/Xicc|5h~{U4+{ssIy9ٹ)z@ y.6*㤤ٹj.ynN49ݳgԐhGGvcs7%͉F"&χ6c7=;g_XXX||r9<7'y.<@&tGozj8%͉F"&OI"SH<7'y.<@ 7"]ܜh.\m`.ynN4\dy.6YhV}cm[?ZkxǑnV-)Z԰sffkּIرY˖ 4o#Evhaڼy3jIssQ4Lܤwf%m֬^vX8th%_nYۂgٟy.<@̲Է޾ʏ?OJs5KӱňxL1dnc\~b۠~Rm[^.ҐffgVZVN|mAMZ2k.\m2suZɯԩ+W}GyZL"ժڹߢE;HMMۙT/OoZH% k({w7+jo>tꧻ;X5/.ҹ |@֥L|tL2fw:os/ 12FyݻTRYZcc/۷\eʔ2e`RU?>Ҽy#?JBUu|-ţF4xѥK=~|_|<[\E1Oٲ*V|oŸĂW* ͤI?HœŜ˥faj*Ι3Sfuu.1AF"&(`A _WVJsgl:t…'i^ [鑾?bCQ=]{tj푏YjyP#uo5[} jwcò熅97o^D /HC#F|W\kg,X0Z__?GR v݆ 6k)nX\B5kh"g^6n\[ˉmޅ ⏯+>cƏPz5zh->)6m#f6XRbE*UzoΜYwIuQF"&+yn˖UccgK.V@u)Zp򟥡 y.<@5kکr\L}}s%<7&pc 2us~Zi~R͇;y_s{ر}K0jKyS'*׫WC41USOYqZ4/֬^ӧ.ZN۹sSrO"s̬$*))20(tR}Ts]ڼ]ĉr:KoEZZbp6#ox^'Zwhrwy1Nqw}[XXe1by+-6m? 33={Dۼy kɇ+VLVT3gMM{0vĈ … :tZA___]d훊KڵH'՛뮊J߾W\fЇ~`kNrgV,IjGWi#a#Evhyf[ob sss33}ٳG\׶Kw؉?11ʏj/~m{B'&^IMk",[Kh)y.<@nήf[oRf1[;;cǎ9::j~U 7s`s߰׮qk6\dy.69}gMs'g{d%Ռn??x>8 8[hnEv͚C<E M~s̤0wϞ= [6Cn#Evh_}IaMf&d1D<E M~s؜9s&m3ܜh.\mK+}Ye9s]X~#ͧ<E %NNN{ٽ{<7?6\dy.@ٙΫ :c9s]Ydcccnn'k+ TD#Evd˗,*6Bΰ0ߘ~|ڂx?s-YUuFEE)WƤ<<YG %QQQaaa~~~yXXXXg||ro,12&]jmܿ`9&ٲ-G>.!@8}%*ceW{C {> +齏Ks=yxaXٕ%ȞBerzc\ 5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5U5Un*\( YO|~1*y.pHWsC~1R ?#.pn?`R+幡WG c#a #x|@e+vNOPy.tC! }xrsNbDt{m,o`9_Zbd!@:!j.ʟ <Hy!w|LWB \#!w υH<7^˾aaaʟ 0\CMs/Xicc #@:乹ؤ_|qLꌏ4oO>R~*o:_-U+'ff葃~^mU*Չ[ebke~<jm՗ݸq(2[Ut9C“& 50(D7A t>v7oR-$ ڽP!9s_nynT1ff#υvH'y.-'y.#@:乹sy.!F syn.6\hG tssB;\C<ڑ \lЎ</E^{#VBe|\3mt#υvG!9{p%YӚssB;\"12&ĥv~7p.c.$FDe췐<ڑ謔ħWnn8pqc,کXY~0XY~|ڂfhm'υ:%ꦟcVjc!WƮ6ڝHy.-y.2C ? 8|.&Znϻvkɱ,H uqe/Xw4m߄/nOQ{g-,V۳e'< I qv\vպ*Sm?4JYflll ּN:yr,;.v{zz**(sԤp[/XPoɩ<>8s|%n*rzPֈ7\ STPE1Zbz(+#z/XrwueutsssB:ul*wwru.vFEg Oy/~۴CUv[BݓbbJXXX``/˘%˶;7g훷Ùoxś_6A"ɱOܮ^y.Kn8|&.VSxQ9dy.\ұ+#?OZhԍR@sGkqnƳ1 srPl`X׬ߌ.h;z{K) j贔ěv۔,5)Y9 dy.۔ӕ[=km6;xGqĈhe5 {.'96 :o$5)9fK3TўS[zDY xϩkm|xU9G "wұ+#U'}e)𶅞ץa#cc+ ~`rmfK=ZۀY<rXҳX +uvҐ$)&6*sgUvgMmG^<ӕ#:~%M@&Dx޾ס#:hedr()-N PVyCb3^P _uZ229^gl IMJVVyTtwry.#.Xӵt l+wu9=$E?WVGdžC#>1H IIH|z/n<\sK }tkm,. ^+7S2zӶrכ+(s@J~ʭ?5˶rW]{LdgÅĈhe5 =tۀ) 1.\&{K`edrhϩk-O'%%S HAA ނgTQVF&^۩)joKj׌j/K \_$FDw^l`[sfZ/դXe5J_!Y-. h;Kqfٛl\bÔޭ۳hN9B 2uc?9P+#džC#*K䶧m+wF9B '>4<tٺL#wF޽y Px֭[ϟ?_DtJfɒ%Rg@@@+TPH5kǿֵ gV>CkSlbEM>bŊ?u/mڴXLzN<)u֩Sg̘1Ç/SLjo_s3Ç۷o_Xڵkۗ<XBx S*o_ ?psSGY8xyһ𼝚~*T駟N:{RRRׯ߾}WN4RJRq:u~G{{y-[Vh}}}Eٳg)MJJjРAr֬YsM[[[qfV'ϐe6SN͛7/_~7nlٲm۶r1c,--۴ichh#K* 65'NprrWy.޽o,ݥ@D @tgO?^*ߞ5}!I1'55rʍ7|yJ|[ZZ^^^/"Ν;K5~~~zzzg̘_Jr *M"4ͬN!õl61CV_nlٲbŊٳs,YR:C ^{{{I:$߈*W^/vb䖸'} ry.yWJ|K޷[\њ}Kufٛl\b”9… 5kTTݺu =O>R]v۷6l u5$rܩ9:hM]fϰvZ9?UVbEqz+'%%ϛ7O?ëHt"y.rQGt(ߐD{}j?Z86ruD{WYnZZZ/^|Μ9/^=}vE".]jdd$o ׸IlTWQM]fe纸OԮZ*|ٳ-[,TPttT\vmqQ9*իW?~ŋ'E 9}p^k\|s#8Tݙ.Sukل'ʯ "##?c=="Et!88XkݺJCe˖u֝4iƍ5۪ܹsI&4+W~LfV'ϐe8֭[gdd"-m׮,U.:kժ"-m۶m  KU$ë+VLt~3g$E{t]nf|<qνW~Z` Utf}֦.볅$%%Ҵqqq)))h^%!!!00P]Y8619.A9<QȣC^3֟n3`j8dWo$$*uݮ=g~\{LtWe5䞔S-Gl@G sgեa⭝Z7%<-.$=y) 1w=cf#L <[m"|SSR#6(L9<<m£^liʵ\ٷV'* _Ymvh6GsH'9.E,̷u'^9'j\q}e/*\^Dݸ}*FCLğVH{R-avպ?|C9<<P? 8|'Zn^g¿q jiAݎCJ<sBRLljsgU~\ݦy/t|Bx fnP"!Ԕ/߻[m.ݡ@+#FzL1}DGj(ؒu\x s:Vco}΀Klᓚ >qh;ɱ%|,1":\{L=g\ˉQϔ. [9uy y. ?IIH|z/42݇ O zˮj7Ƀs9%1":\{L?~'ys'/%F=SV}6S"ޚo^oqac,t^37<:QE\c[[vry.D{}_ 22ql8wE^,+gLVً\E ȞO˟.'Y^nϻvk$^*{s+gٹ޳u?Top[ׄQj9a<\^ًC ^jRͤ&Dx޾ȥZl6c'b>RVt6MًC X8&(/^Sמj1x/pCuxX9\B UԍgM;y/.&nW۹޳orjRse5r_*{Ks _bMN(2vݾgio߸txmg₟(H{^\g9h2~hqB9qνWƮpl8WC'{,ꦟLEoe/ry.'P{ܠKa#}@Eqmklhh4Fc4[QQ4*؃v޻" R\ޗ& {o~h`gw㙽Νa\yƑO}VhF]7-()Ww]4ʎ3j͢ge%yz>wYO%޳i>[ Dns 8@Ij@UCH檡_G/|i–L =EoC ʲR\aO2Z T7d P;nSz+H\i 'CH4$kO) qsF^5D5JuR LV`uø*=c@EaNSȯ,ҎsSBa E*ǂM|ǂ29_DoB 碪gC`jwаtq^ <@!EU[T6p8|>i*{* Ys\TE幺NXXXxyyx<HE]{N?-Dod!vsQՖ(}1É{ 9Py~W#`P|y^!$y.C[ .] <@!EU[-%Rہ+</==~ӳ!vsQV3sI%}tv'_,"]{u+V y.jyn?γt$/3V y.jyn9"]$Co B >7-(zҘdz./Kk]vOT]} O94乑Vm^** Æ .uɭ*,%UU(Zhmmڴ۷g3r e99l8o> =rgvb3ی79n7/ڵs.vK^*Ԛ XC5zeҶmgVR< 5\Ç ЯewH-iƬYgk{|5Xm՞ JJ&:>yJ].Jm)&& 4gP%яV%9nʉ`o 6ǦM[^~RS?_EJUw>[S(ύo}&W=V-E"]VQ\jn2$y.K+##s]Tmܨmے/..Y2ks7lPmժU>xpleee}|t_~4wCMǢii05-u_4&_Gvj*uVKeG*JqM0}*e,W^0ʌmUCφazL{Oo׮\!vWܺUO>>jjYnĔ)#3j*?yϏ @=!& Ү.ymWVGWbeI-T{y.rECaEe-ky⼛7/lٲEv[֒r8v>:vl=Vx8m6+VvvOj̘3gNpu}$Ph3t,}.5}kLt(e%MfK-;V[s3qj&ga{F`nf'.t[qf[\Ѻ.)Rygs線jYE^ίMEh݉t$'s<@%ύYrN.Reew{۷|*ϵ%ݣT3v-k-[j<.^l{G;U>=)SE۶mDmE9^|ߚӦ=~,g=|dp;V[5߄Y8po`VӱMc~qI&8.ZgUح [xܪy.F'OP*kjٴ ~$3p8U=PEj???r e\iW<"hժצU{̮|юD{]uEWX+2fT{~'yߩ#F pUUujMѣ޽''%deegϞ,~h*+qPjjʢU[j:xcM6!EԊwj߱ڪ=%^^^1666l6XGu}kU7`|^ [7ǙV *K}x>yKNt Th|}.0p͸q}4.PCIi@NmE^M>yDWWbx<$'s<@%~VVV[::'bb,Y3f(Փٴiu%d{۶%^湤'uϨG^6nTԩ#۶m#>A;=2y֭rnZZ[jyONXKKjժ?[lqPcOO򅋎%j~.mVy)D.. βٗkL$~mڏRm}ykll``O> ӻy= W7Sp Uz(UMQIs[GSۗ/+oߚ \3thw~gPE8ݺ{zCT􌌌ė\.4B ?:hзTX&++;t͛R]˗+jՒseLM7ajvr̙$ddǎ/]#:#}*ٸu YҢ ?dF 3f(unK*zrS^y];|QuV;ſEynj:VN[hA۷ɭ MpOڪ7Eձ\G\IՔ疔榧|%&,,,889(E "s<@!m*);U>ycTMyn-jzkyEQ/\A,y.<&5弢y. e\i<Umc[ *E "s<@!EU[s)s@y.H;乨j yH\f!vsQՖ\)/c?88lR91rz$ɩ \f!vTybaVó WIUQ_\-Azko>l3X:MU- в?HOOp8R91rz$ɩ \f!vTBU[J3s5CMgƮ~Q޲<{^o#:(^^^)CN9=rTgy.H4@x~T[-o5蹜a_/ܮxՑ_k0yzһP޹_yQl=T Ylv+|VjE^zdUEߡf%%%|>+JʐS"'FN$9U@=A, 3.;~Q?7l7`B{t5$]ǟwlO}P>w؊⬀fA;.qv>ec[fFV!  e\ƤNK>zjq#5i4{amg3 {N7>y/[dyQ[5㶓l.\x D3ׂOgЬ e\$KoLRoe҆WF3twmn~Yr nՎi~\f!hLhK{d?gIgl:guؚt]$hliV^ˇgmTMMw7W8fw&y.4q**5K|)}uӄM]y~Qo/]pyY;C4=~VX^A-B *K|w_ج 8}ےl8'g*t:/2RA\r3: [jf5oclTdD<4q;Uݴ4viۂw]}bYQ\J\f! E|K-X|ߺS&8iRBE~>Z%<".{-=l;|q?/\|(.PF\f!`RΫܩ,9mX km* }IPZ:xg~#l*oU;f7B~o| }'s< +!G:]͒SX?YcOCFy+IˢH^M.3ϨW~3u/VGs<~&<4\t%`?ze;in FX!x}FCkCbxZxY{Qb}B %)N~!{[>[Oצ8%>ʍ9[2}[c@PZ:xmݦv欰%hǖYAt!C,-?:-ٻXr N7^(k̄AE}v;,z@Pη\|eF_p$kϦ3 y.IEaqgVmT]{CYv}\4a–^(. yh%fVEv.3ӵh2 y.@mrc#.smc)p>4'4> -RM&v1*|h~ǂ?.2n=nrZHoiF}y.Еěp֟O*[- 㚜I8lʏI4{C 7-ڎּ(oGm}7⍝rbO L e\2_z̙%:kguxJ(|s͐CL ޼K`9>wŷsM0f3#=,@]2}\3@.EE}\@@ ~g`quEǦbYdE J?@0pEXr ^o 2 y.4A ooŽ? c(YAv#SJFϮSciX~7EyԵn"L@,DT'Y{nնAռl5':e5cҲCz*@OTn|WܸD!=}FoVO s<m=m/:MNU}iNh4}$M4R(S]Wǝ׊]OWy6/6@2 y.4>eN~NY|-n#+@]ۼ3Ds-b9Ζi)+L!~OۏZav@U䓳r}4_y.T+Iˊӳ^d=ג|+K㠊"~~!C} +/tm}n]W.'4Z(B RGX!Hw =rajǂ1 %AM軦J{"faZ?Ϥd+7a<@ e\ 1=d)؏^zNkPM;s̒%KLz'OR]'N7oÇD{;w͛ĥK\.8tFugϞ566 ѣGD]m֢a+74iӄ/@ʲRo}PkTT߻w}9rd={v8phܹsdc> 3wؑ&޽{G&\v'g )ihhtuuT6ّ,a\0Joo eE>R9㟮{7ZYY;w^vmѢE-ZzEyn-3ԞN0B :O =|7LwX[~[󢃼ŷsiҼh抇X4as<_YN~ߺSc5P5@]o^^P?ON葌L@@.F>OO846--GC---<js;555ј/dIyq>&ż{m]N;úM\f!zvӤ ,9WQ7M&gGFFŋ\.:~xc@@iOK6LLLHcǾ,}_ ]vݺuh'Oɉv xI4z͌ӳH„DsnI?ƮܪqRH e\OSx6j=l. Ѹ+(i۵kHx>;;SN&LohժUtt"4XdI֭;teyf>|#m̙3%ϭ\`/ΣwHҬ'[eFƓ_k<˩S2 y.|6ayE[p;cV<y`VB`dddV\񊋋544']xQVV󅅅m۶ݱcx޳gO2Ugصk9NAA92ϺuD;.X |>j$OT~[Fhlw2 r|71i7|y,ʣ$-H B uUsաG +@ r劜dc̙AAAT7:uvڝ8qI8yX, ek!++k֬YENjr[#u_U4'U.0Y-%4IB [MͬSɇۜݡ$r b? e\47x5K=kHmSBㅅ ZiyMdd(ɕT??^e'w_Ff%mB[nwo@. [w]{f0 y.@5nɒSp!Lp h`I^V?rmˍL{' 8nM 1̲"e e\<9s֝7v*-W졲 \z_kaKLu߆3Q73}* ;@A,]Nhty]TuWqk^NX }HJy~ѫLM; )/( nm&_\͕iAIC,M3>c~ta>&(+l`S)pviFUh=?em묝/J0!@0[Eqio6?-6\3NϦ$->F"zٻr#x>>)| imu/L`9B (EG]7r%1Lp 2YͶ×8(I[z>7vwȾn,>)B,FyA-`y*Hu⍝r @}W0 +`4%9I֞xn׊.3#ޅ y.Hܩ,9mX VE4vzNy^!$[z? +-Oh2 y.4*ɏ7r]{ҼlTԵ=+ @ +zYǣ4Oǻw->m#w\\f!"aa':MSpUI~t}4 \LߖUiFڻck=aZnũx)4s<a71v6C콞'()F"+ij>>1ԣNx-{j-gyS2^6g7 obv*s<@r Sby.Wdk3\g=|'ܭ8%>?C,UnxlWq+KN;U=nΫh h*2|8m^dIE%^ozuzBoPG/-|s<KM8~Ol9Ϸp+/(&$7硺>JXe%>>ye۟9 , _ y.|Ls&od)u5)kOT/(#+JLK0nk ěp xI5@,[M$=;Ng&fb3OoE1Bf4#'x5ӮBR伊}bU~ 4O?!w=\f!&.%0-)B cǭa)x싾.K(#vx 4ť ^y@UK|W2+3C,Дz/פ֬ؒ h7r˃ r8h[&fgyˏIw49| #w]6̑z"ۥ89>B MVK 'or_e7BkEcX%As Z Pq3;$ ЌtԵ]AWz-9ao>:B MSKWj;QKv%W3B $O_r>5b* ɷˈ *,8~]+ ,">EY@,_O-(.M!?oXȬLZqJ3r@3&aK}))NA;/;[c=h[w_cѪ y.@S7n3-3ՒuuMgD{'hrbn.O wgw 7W{.<`=hJ3sCB,Ф-܌L Ŵ~>=`y*t ,4WoՎI~ Dtն×=<7G \f!Tn !*.˸LN*l @F~pC)"z7444- {-y.HZ*7 Q^FmvYO%7ԬLM=t4+ oi_Wp&o|yV'7U`㪼a*ߵ'y:V$8`\f!4 QTkv󋊊JOOqEi- JJ#/=35{f~t쐨7 =pgVC0B isC!(+kjoz74Nyq15ǯYȢ c\f!4 QT{ഁǣ_zhOjߙT$>Yřw͉8="{4B isDyӧO9NTTCSo±PL+?:!桹1TډfyAP2 y.!mB%ٍPs*ΛIn/ꦉvҼ"hƄAOXGnV;м0!>\f!4 Qsd_,LS_ķp|}w*o/8~ 4DߺS!{лs<@7SWՑ TsAdDc9wX{@  9o޼ѣGSܷo_}iiid_vh̀f͚եK7Rde>D'2 y.o+Vm4VN_8p@999޻s`Z۫W޽{aGNMM MLqB iC [l&Ё)uʓ>UI#slWMl<޽ 9 ђxm6sӝ ձc':"m~y~AkZj(K+乍TvH>>oJ,=fYYw޽ݻw-[433 ,+88X46$p5<@1x̘-}8xp-]߁nɴلU\am嵪F$;I5CA 3I{x<^AA]ڶmTZu݈O~bӶ9OcǛa(S9W>?aiPBMuØoN9qr %ϭر-Z|оWTM6n„dfꥢo3f(i]TЛZ\Uϝ+/Xsr/NG,jE PWnd/9_oߑcn~ln.Xv9QZŏ(*Kk2-QJH*%rMrE***"##U ,J`Iݨ40/ޢU?Pa-35Jc_tBTPB|b\5HWB?P<\/E'Ktkb=4JKs A gM~u{OK+2>6֪_s U%%oZ_v׽{Gdee]VsVk{͎a,%I.B)WYsN 3S.O*;d]]}[J| dǯ#.JSl5jw*MxLMW&O{Uy, 𧟾[{!ϕB^v&HrQC)WYs*MsLNM8<_Pdvq:*5n3|6j5^&Yי;/";F\Ч2'?2mV@uAY9 ܣG7ݻjjGqEEϙ3Vծ4hl yHqzЂ' z7@SH܂^Rk^*70їוX6]}Uyv#,cwA%)aCe vd(2yqJ& Qsd8~Omz7@cS4"~z/)+4cN*Y%fZNf@Q\r)KDݬg6K |A!mBˠDs71l/7r @SIQRe> =yʛgם]}Uq {KNl_FCn Zg6ބC 'synC\ /F"BhB``\h %ED5<롲rq_<^m7rhe7W}.d^RiVH+4lvmeyS+:(OG|P1ߵ'+=[Ёk*70/2LE?BrC j~r? Ќ!,PXK HvX>4dߍjmZ֤d4q۟{H+oNjVj&)F$斔/7~>wYYJ!, 85+',&9cq^7DϪµ3ȇ \qp4VjYiY.eKv]Jgifn!.(ns"|ޯ933gmHQfR{׋%\<-]9G.!vep<w".\ãvkŕ(;αr=V'L\P[{}cK;*&{5}{a&Ɩ}<9+ u{.P[ 6h?('խBtug ^ dgHׯou\J9 XD7E -=6!RTKsmF-<ZRvOO9DgwXvVX`FW+2S҂owy6#kr]n<ڒ .\H8V#}sv=Ti줕W~0tbhxVZ@ܫS :hw˄x膺saR#c ?}<O+n/͹0c ] ͈ORjNJDtd5N9 ']n<UAwv{_]?%kί~CٖխO_.-3 >VfR(^Iy7`BFxr]n<&-;#3ڝπB6sߠ9GڎyFאURӔ0EtC]0̔K{|vܲc&7XSն>J˷q N?dpfj32olݿi ;3K bPy.VH\Mķ6޹f]G;?=zewv{Ǟ ɈOR 0yܫ̔;\_|a|0EtC]((i [+KOȻؽouQz(a/耠xb{5S ][/^\^PNȸ+ u" 1go<|M~lU*Q '2dӽ (ԕ2*m|Ό@XD7Eu1Wn94wC-;hT`O~aڛv|/ݏU toʭ,I@XD7E!qC\m6_[߁3/ͳyZD ^=p. ` + u皭訣gBt Ի=s|Rz <jE)|qVt ^#3%M9 pW,"5ٙYqB8{]^Y?αr=06pҐ?#S"Pܫ{}o-*EtC]OVjZ٫XX́OqiێCҕP(ܫUFb:i3_4Lw".\S}:g'8y7;Ut ,Drxr {5#I>7O>=Hz4L w".\Ӓw흇X׉{.-yEWv?VĽ%>__ [04Lw".\%G`ooǹFC x[W3=ɹWFh{bbPynJyxB[%V#kߨɾӃm {''Ljvg\ku>3)E9 ]n<׸Ң Zŧ}3ݴs9{5;#S&{j^\)EtC]<޵v'ikjТ@qĽZ%-ܲ/ur w".ܧuȹx9Mb ה3ړ Ό_ToŜ VNXD7Eą9 N\kuto/`o׹H ,sܫ\qI7*PlqW,"}Ԩcgm=m➺77xnȪ]ѧ3[`^W+{^6whsimzLrw".%ܾu7鷧n7S,BfJrSjȼiq߽ z\_璕fbPy2S"/ͳyBN_ ?;ighh jHH^gh5,r慻bP03~?bߜ3=dծWfg+7@WSH:;iSնǻO~I9 s]nKsyn[=[âGWӉpw̜_wâp4w".MQ7NC- $yٌv\۴h~pW,2<75*6!)2R|i^-+=Ɩ}vO~iXPYYi?-ϓ0*Wsh5̵VǠvI yW$膺Gs&|;U\߫eڵ7=M-G膺L:ύ:v~sM;d^-%"ϫ[4.r7+.4.Sseթ {g)E.^4gW%+f#>rȍ?%.sc}?vVr P^_/5_>~rțPyn 3ox0)^MuozW4Pذ膺Ts3Snli573R[5]*,~9kj?I4P`$".耠 8k]ҭ{iE[#ފjҸ4{kn`W%+g^ nܬԴ\6DO)iHvV"=OI6-rQʡ1ɹFJPT`NH0EtC]E&E}/͵Uw ³ȝ[ɑ|yjGٖ~*7-B\I9HVZz覽۠MfJr )`膺>cՈ;ggd*I ]< ݫEzr(KMt&Uڤ=Sݏ4v6Gڎ {\1  nXy:bc(gnvn-ݫ(ɮJ_xܬH;iyDqP\fŞ" u>Ͻ~gJ,(&,Ҽ:j_;ܨHD U޲iwP($"O^Sh(\#.ݸqGpp@`膺sâsry<C)3J8;NG)s`<$"<ܠE[OO((0湝:}faaF*;<7++@*- >(׬Y#MJ:?Os|*sS0LPW.y˫_%ݺP<72򰥥US)T͙3ۻu*_\gA]z^,ZSj/XXXfm#}M7hP…RRNJ5jT>QvŨQ=QQҾvv' Z:J*,_>WkL4 '+\ n+"膺s(RsXt\b|rӦÇwi)V9s嗫KJմ;u꼜lلRJOر ƋӍ-[yj/V6}sO@,\qn<"U<7$¢_SrrtVTZi>1YsھFK:Or^^6۹纃%ζ3u#sSիʵyly3<7<C\&"H$<;WTKgyņ Hq^E;T6%K3|-[ߔ]]G*>X2u9ޫ{ŠtynXKK/DqO@,\qn<"U]JMzESJǞ\02Py.EI\uKJ-<)'@ !. uPWCO[?kufn BWO<C\&"H<y.LD7E @"5RxDtC])\#y.LD7E @"5RxDtC])\#y.LD7E @"5RxDtC])\#y.LD7E @"5RxDtC])9 Za{V-*02Py.EJs)#y.L@7E @JM< ߷M[iҜBז#U.Oџ`m\Ohh@膺sP'y7;o{fy9 )Tjl```XX@膺sPYiPm<~rVۤfJ-8P(e".\TQ@0ǡ绻>즜LRc LD7E jBCC `z4xnr)Tjl\\\jj@膺s0uÎuVmGO#. u`2O[XRӔqn<uc>W:3=Nr@". u`rO]>b9PDtC]hg~zׇij . u`3.ũC\&"@}{wsF\&"@MA7ڏfO(2Py.HMԩub 4A\&"ege][\๩Q1i&LD7E @:zƽq_O^Q0Ie".\Hҭ{>F\&"2S/XXZ9 膺s0\ku9%]‸LD7E ^t}#s2Py.7xs8gge)+e".\ )+=#hS5Kc!2Py.gOoڏrS9". u`WnzٌSǕs92Py.O%=6!eNլmJPN(DtC]RvVֵk4'52Z9 \膺s(gdH B\&"$ݺkkon;`DtC]Tfrk^+)2Py.rk>=$ݼ`ֈDtC]9膺szᗵU\Fz#. u:trrC\&"hELO7;P9y#. u_jT5_[㜝|膺s,+=ʒmNլ.MMPN@膺sf{9(02Py. 0vO܎) 膺sf%=.̄ߝۖ 4<92Py.\dg__Ef{". u}s>pSstDtC]b 3)%~r䰨}Ɩ}902Py.&ÇYigw?3` e".\ u^1x[.ǺNL". uLZc6it#q{^< XDtC]ӕr}oӢF-tnbGvF6!. uL]Rw`vfVȟ5ڝ@.2Py.D:O$uz{6,\r#0>2Py.u?aoRZ-C-膺s&'=6agO둽G.u%#>I2Py.o0LK2-|;.G\&"H NݢS.;y}oVwZlסLƅںhJMP2Py.FgR*Ho6 膺s0<7hO{gx!y.ee".\0 9ύ>MY;lO hqn< <׌Ks'". uaqyi <<<”̤ye".\0 \3.94iOhh=lE_˶)O 6{2Py.y.ݸqGpp>(#c%g>&De".\0 \3.w %De".\0 \3.ҟr=iIhhhTTTjjD-!. uaqi0ϕ*Q{hK #. uaqi3'=0z.4LD7E Akƥ<7HxLD7E ynbٳ{22N*jٲ 6FuQZk e ynPА[F?i=8'@ 膺s0 7i򖥥#JWfbYY5m֬뢬' v+WvР.SEPm6kٲxܷ߮6|xc'' 膺s0 ؕ)SBϸqco`6}GK n_y}nG8o[XX|]']M}lt5hP޽mkZݺ/_gO븸;cưFꇄzw-[f޼=6ϕ?qO1i@i$9a_W\R G{N}:/,Y嗫^=5J/ڷo1|xw۷R:[cpv7؝Xͮ[HG۟=B?846致AjՆ}̘5joذ{Oy<4tx^xvk\޺5iӗfD)...>>>D2Py.FA'6YXX̙3RJ!Cc΅=zqco/w^|W泱l'L_lh/yKϛ4y+sL2ui~ޝ7\C]|Ѫ~LmAΌH~ZzjJhu.~bf/K5UvsgWE5&5.gnذJJYԇ-[@kDtC]`smoaaq,)E](mn myݻK(ѭ<~e.osxB/X}ƍwuSz{/S1u(6g]".`Ϝ>M~CG~+Z5э+~q\^ˇt4uDGҥ>䳣Xn;?3~w.[uʾO=Qo]eWs;{vrJI/+tVY۬\AvͽZ:_*T=믿s|6VVҋE~z2g\KS;8mt>Svq_jm4wy{{?7.Zd%[v~?ɓSLI5?%V܉ .DEE)2Py.FAܐW ~:OIߪUU+?&?6ݿ?<Ԯ y󍴣iWkR5\F<ƍ,-[FtăvիSڼyn|Aym8쯿hiiwO8svE?+6)6#?쳣V϶t(ұ]uS +7s=={K?Ȇ kmGk\>СwV_:Ƿ?z† .IP0;e".\0乙yJ*y钣G/;wܥO__&m3`@Gy߾^=3Vd!]~Qj&oUZI:sf{ŊϽf={TWۻ7ZO ;`ii嗟(6"K?8>K(ѥRXVy:00Cw^'''v.2n֖tnI7;hc_COwoy>=ONy 湉^R;}@>z7^')iu a(xI}߰aæMv`vDtC]`s}ҲJ3]sxqS?4bnnn˭IG!k+MJ*,_>+SCtPÂS-[Fs)ݳcӓ^t)ifVT)SW|2eJh(9dGkԨ~A+RUɒ%&MxR޽sd*G8JZ̿׎V\;;͛7odžGۼb%?Ͷ0qWe{5+STyڈM׋[=liu@ Z<Ұaӧ\w-ZRJK\^%G.ȭqn< yn=N䨗<0Ae".\0 \3se ". uaq<D\&" 5*<ċ<dO\&" 5*>>&F:%ĤӓNR:UfǞL@7E y$@.繿o`Ӥ9EVGLڤgZlmkӨ70Fvj㪿족qqqQQQaaag锤NO:ITg2Py.G>tӒ[V6C7rژrȬX@DtC]`xE+no3ؾBG9y@!Cc". utrC]zO<䪜.*B9dxh,Ye".\0<=DZCL׈ysEI Y㡱f膺sxH,mO+-:^94又n<L]۾f:U0cMzI$24又n<LW'6=6A964又n<LQ|𭿓ܙʸ^?".\0-9Inխ/Z6%\/hPy.bʸ^?".\PE+z@#X膺s@MrT%24又n<$<]A GD7E E-)I`#"rwLjf-I`#"$76A9]I`#"cI,m/XcfI`#"KlmO+=&^9m.^?".\0pAsrܟWq+z@#X膺s0o<7'ɝ:-̓\ GD7E O+]󜪶=7O$24又n< /=a97eUڃ8崹z@#X膺s0nGJ]v?V9 \/hPy.<;#%ʸ^?".\(䰨Q +>3Ԩp,DtC]x)waQN;~yjdrZ^?".\OʽG/v|*e)$F u@R=$'Dz GD7E J)сc:`uzb|p,DtC]?9IOrܽƿq,DtC]$rk܂z@#X膺sh]jdq%G)?w=z|g 5k婘3gvfʔ)ẽϟb [ɢE<=='Oiy988<iӦ_K;ݳgn3Cz@#X膺shWJInay&˗/׮]bŊFZp_|E9r ~k֬9a„S֩Sz~~~o񆅅P t{N~-ytGH4n8ԦMCɯ#m6fF uТ='7X[[?/^ԍdeeH/:t M龫ꫯ6n8;;?ilڵ 4Vֿu#yn^G?8pn^?".\ڒ]Z$oZXXLo U-[7ߔ(Qa|ۼyua ^ ?".\/1Krы(ɕ[ԩSʉ9"I3g|OYZ ./RGzƀ^?".\,9<_ $txxxXXXܹS9𡧧4/Nl4Vzci.\0#T\yĈ7lvy `,DtC]SrXT9IO ʢʕ+geex0&&B ͛7ǍWTB+ѣGҥ˗/_q{ݎݺu0y܀^?".\#ֽS%~O4L+Zd?o]vOg *|\r3g̔x˗뎳k.i_U70[￯;p,DtC]Aͻ;VisvԨ崁dgg^p!++K1%\r%((H??۷oKg3&4又n<@?dc6&v?V9 cz@#X膺sWq?)a|\/hPy.'!w=x4 GD7E 8IzwLjfI!U GD7E xrd9I̵ i`#"`n3ݹP GD7E t] =4֗f')64又n<)xݧ.ߘiz@#X膺s!ǻOv$&`#"`*bυzsA d&(az^?".\9{X9IbT4L GD7E @M1guE+Ke)aڸ^?".\>tӸ$w=In1F u(j9Inǟvd|{V*In1F u(:ܗ ݁$ p,DtC]B_J;Hr GD7E _0WW4又n<|'XWIrҕ(^?".\$?]$׼q,DtC] )?lIz@#X膺sƿ\ \/hPy.LJIĜ >ylw 4又n<c\urBuE+Ir5`#"P[+K핣]~dej GD7E O1g:Vn Nssvے*NAk^?".\LJqf}Ϻ['Lw¶(4又n<@5$ ;Pz;pS5닳g$o\/hPy.\׾J?tSնF uP ^%?>rcgR]@F u踝>=`oў_Un`#"/{~>*Z;VnZSb; }\/hPy.PzL|gU:vԣo~X鋝>sfW/j& :n~;.Gz♼ GD7E 3)ey$@Þm`#"υܠ% #lΠ"u_0 ?jtXpC۷/⋑SJy{?^Z5ѻWbcqejn5kV9s=Ce7^}kp(EY%->]b{LU2ٔjqU.]n)|; Zߵka:ȑ}ӧPe\lд]AA3,,3sL0y.ܐ3y_yxx,^Rޏ/_.4t^lW>mzF///ۧ\7oܶ#q۟SS'GFTbWJe犺[6K馪>Tző+(Oaɒϥ;۶-R۠A>ܬ=bI4x* &+?֯_ǧRopa/5a,\!gg{xx짔RW ovExc;lhOl~6[v{ R9sŞ;uzJUac=`Ɇ s#*P)v5c:m{/G/Yύti-63g2ŏى6 :,\7`F7E 7Lcs7n+HK|BV7sm4S~'?Q/ۯ(w&Õ?hÕm׊zeio])W+'gZsն#k3\Q+V|-4DоsL0y.0Kvg#b◄jq̨oxcAu}i|&|}h͚S|WZܤ ]OL\/[hp?^k!m`svwNہʬZ,1m .WΫGl{s_f[pm`Ds"T}"u_0 ?jtX(2/_K |xl<:V_wöoG3Ԩ؄\Qaa?zzz֪U}ѢO_+G*Zy_(Wk„A۶-wW"6ӧ}} Ox܊+wHwqqxݺ5/^p"u_0 ?jtXp-YgJ=4y7uru^;n;H4p ۸Wyzz6mzװa[=T 嶒֌w+_P|O(.ֻw///1^ }v里x=؎wԆ sԳ=tǯzGl9n@ǭ[}݅Gm|}5.Ɋ>/͚5b;{5oXIjyIQ"υ.%9vv lcT{6'oDZ'~њ.Nv-JDDG/fر0v +)ix ii[짜,>w׌֫LgfF%&6v8h_n &Gn<rӡvA;oΞ3FȽpQu)lKRn &Gn<%(Pea{F՟ isQ{ޙ7ge3;2Qn &Gn<)Dҟ]sWVn=px19i%#-Ex`,膱sQ$NU57KbuQJsD/5a,\Z֙Vl=Ц}VV.'M !-Ex`,膱s˧SN-߰kW^Y6ڄ`,膱sKDbЖz'"ڄ`,膱s_ҚAu}uPksIQ"-faF'"ڄ`,膱sӹȽu|R' W9 $X cq] &\ $`F7E[X-ҵ]"vq1FIQ"uJU5_:,O%,ȀmHW6ERCʛ &Gn<)'~Zy(yq hs6Ljk\nOyJ?/o :/5a,\ll5()$RJpk #_*YY-G˛FIQ"Ͻ q!`ͷ@ qUny~WTpîg67`F7E{m&Ly(-Y9+?+?ڬ`/5a,k[ȀW`^ky4`/5a,kU:X ]JH^ʿSA:Ysi _0 ?jtX7gȣ@ {8|y0L0y5l<:aFyh3FP 61pƕ{|/5a,\=|K@gs2 mEw_s`F7EBl&Q乇g~{6(3%Gn>^~-0_}9*ln|b\{S膱s5e9k ϥ(/[xbGʯFs qxyySu8~ "] cjJް#Hy5RynёRE,q %ozH.)etX乚7pˣ<ܾs<*b)FN-j0_2a,\My5RynёRE,)tji2$0'_2a,\MM:4Tu E[tTK} )_/J0yFȣ<ܾs<*bI. cj݇Qjyortq?mڻ3U+޽'uynё皪Ν{_/JٟBD˟RF7E)QP<رrz>BUǎOn~\TJǎ`?RO6Wn</C^=xtVey@[taU5ms+W/3#7W/J9)Q|kygêU+Uqݤ;jmX9ܕt8n]~Ci{v|J܂J% 9MQ1#q] x'~XxCI'ː-*xoߦUV8vl<9S8~qcYk 2%ۻQキ-CZ,=5]K}^[d7d?$$$**H 膱s5zjG]ynŋۆ-i*ULKۢؽ{G_2G|a޼I-[DF*T([/}=;sM4sV1rdo(xdmPlc'TMySGtSU[b(Q͚gCfqxvvZ;TVFj#FիWZu‡A:IY^+W_ք ""YÃ:!OAř6o*7f͚ =]۾Z=jɩ+u9++`+[9?߰,MW¯ƯŌ{ߝp5~oj}pug&lxnY_|__+kr5;Jx=@//7h|ymoXfgoٲ}ZG@@quT2e\ܰλN6kVs绷o4T/_ޫc;?-O>P z{7ڱcԯ_ɃW==KJ^ϏqYYZ[ Ӧ=VGA>kCwlڴv&5CBzmOi{e{j" "f׭aCcۭ3P6sL'K?3.I cjr>8ffFUVu%JzLX>{6BfMHNճMjjgm]JJW:)㹹*V0ϭ_3<,~JԡC1,X0YQ}󏑑?^j'vn7q-Wp̘]7oܶ#nm4CwPlS|Yvbzq<Gup@ܫJYn?~R`%=rZsh}yVE밥:ucuNNgveuW:WJVg~[W8S! obx.40vҿb'9u%=m4WfPn/ݷm{Uܘ3G+ :z`n oo˕oPTo˗lj۟޶r֭W⋶/&n R?)>>w)Z̘9|Pa,\M'xg_Q>N?UG6j@m˹֯-6[SfZy+nw*mGj׮1fLٳ?(dq]͝RK~.=G!;Auv9:j=_s0ZsZwꭵynNΟB ~ӹjD:'֮rW:WJVIIIb{FEE;iӆeJZ6cu_4Qk}zJv`a:@fV<ӯiO:Wm__?Ww-ω zŐG_Xz咽oVc_;sYo3ߛko#" ^2\Qw?[+aժƏBy≆v^>GesIZ+Jܐ"K&$X2{<\c.`ۭ3ݐ6S7} .TuaΝ/K WJ0y&W3ynB://<Ԟ=˕ e? tN`7l#FNX#nڋ/n+_m{[Εb ?%#6sW~NM yu]=hkhII<==vmc(۴hDwP's9OVJqux:.OjwZw(qR->j3`Ye^ܕ%VTk__ }C?&F-IDATU./~}-D=aC9OOƌPSz߂u֮]%'*W.0"3swx[cc_ԪUY fd7wn'7lXmЇ=ܜ!>sOm7[l&r[C:W]đ=8|PoP^>xWnIOn&q tZ>^,.\9&*}tX乚\S(vm#+?z{?.V^e]Q߿k2LZ5ݶ-j1_3'5p,{|ɺuk^mǎb7fbP9r4QPׄ bcm[ +q720pmd_|1Rv[H F!tR.::GjU7*U衑JOߙ-x7&&^y:Y;Zy;6{oĉOUYzjZʕˋﺫcNvCN֕b+yr嘨 cjr>86kvOǎOJߊߎQ~I6HNةSbiqUO6վ}jի%馪M߷`^^^WύRT]ʗ/'j/Pϵ8p wunb >> ~ݻUgW֖zZ;Tod'S~_CoǥVK) F!tR.:ݵkY&w;%8W =}l_:w~oZ=jɩ+u9++`+s-ZD`>Q\x krswO+33q`[;9M%F<ر.^vga+gv(UB:K:<8b,_aOr:֕R, yCCCCBBW\oG<^t%K:,V@ڏJbxWvrqO<-92ë$0WJ0y&WCIE^בfgg$%%-ݻwΝQQQ[lpqkÅùX+ᇎ!!NjR%OUjTvmIZjc[EFW "6<T\9&*}tX乚\uynf@,PvtkU,E `*>a,\M\rr< :ytR%ZRۣ"} ϥ(/s5b\D<T]a,\M\r"-:\D<T\|S膱s5rs). cjr>^xnsRASX7K&|A >}ʭ}dd Rs_ʯ6)))IIIGb%N*qjLfd?2)5tX乚JRȲiً}[G]Ò?.{v_?w?i"jβ: EGnꎒB"Zka95^'5a,\M|~<^761L0y&q4!ϥS۱Z,'^qQ4L0y&ӗɣ˚bAFݶt< 7$`F7E>Xٴs==,yO]v!6N[uIQ"DPtV%E?W*O%;A;G~(^F$`F7E>N:Py|/ejqP]/OQf: $X cj(!>? M<0e~`CÙL0y&"$^NZF9ju9-+9UF$`F7E>(.'ȧPd7U5}si4^'5a,\MEk'(e%ƌVgepyN0 ?jtX乚[U7{g'ȥ3N 4\L0y&kԽc-B9墼4\L0y&:ڳR 8!ɨw><ג#O: $X cj>1#( =nKN< $`F7E>:d_ySI /-.W7vbA5/_ uIQ"DpW?GQQ#6yriba(^'5a,\M)+OxyEv6"&Ц}7X4 $`F7E> |=y'y})N0 ?jtX乚 kC&386_ZJ761J L0y&BoRGGQɟ׭m3{DjT<$`F7E>y3~Lfiy%ϚbAFݶt<$`F7E>yqs7w%Yrrnuk(N0 ?jtX乚77ɣ(u#3wb4L0y&'eMcɕ'`KYZ,~)\<(O: $X cjp-tyFM<0e~`C^J8#O8: $X cjpRX"ʣp 9i笪!f䴬TyE$`F7E>'lX ~f{-Oz: $X cjpơi?< 3rڪZb?!Ox`,膱s58#(\إ3;L 4 I&GnwbPGf.dӸ^'5a,\M3rڡoʣ(k.}|pîfXrrihuIQ"DpMM9,lJ9ՍXb˗L0y&}3VΚo'PFFxX۸ɥV kuIQ"D/y}GFlj=$iMTx`,膱s5;0e~GFöox|PX~I!uIQ"D/뻉A[QȰ6<>(y}Z9#ϚL0y&yeu\qQ): $X cjБPX(LbAFݶtsX6^'5a,\M:N. z#ya=6; a׭_'OL0y&|S9(LƒsdN8|R6^'5a,\M:v_QR_-=pDL0y&[sU< M<0e~`펻NpFvSN0 ?jtX乚tll5(u~yU:Č*O^'5a,\M:bܟ(pU}f}cN9/O^'5a,\M:v"*Yɩ1#!v✜ y-: $X cjбf{wMP.%9dj`Mϔ8^'5a,\M:~&?+G4\<=pJP]_-ο%OYN0 ?jtX乚ty*ZQ@Ww>2s%{L0y&nbvܰՍXb˗.&Gnm#Ϻ.&Gn@J5/Սm2:-@y`w z)<]Z5a,\M:8= r2b?VQf%% IQ"D#\[UeK`F7E>@\)1#!v✜ ydp!0 ?jtX乚tpz5]:s:>>qI.n\L0y&pe09_ʒ`F7E>@\_w>2s%;G.\L0y&(+.m>>a׸9+-ytp!0 ?jtX乚tpzlI}hKѫu; Ě/O_/.&Gn(y}<.&Gn-j;aj|:%f4q!N!O{!P+nL>=((H-ܹs=<\M:8=!S2?7=SwAVxǏ/uA^Nr[qs}N bܟ&9KYFP]΄ק$|E+yS{]NJ*5i$;;@cj=8<6SNo߾r^lA5w"f[j%y7nT5k6tAըQn[pmܖ:<~G>@PB]IPF/Lt#??N:of||M.\ -K-ڷo{QFկ__پYf Xfĉof%=zTQϼ-[֬Ys g5j}衇6lo޼-HNˀ?uڵSŽVXŋ9<~G>@P}!>ՍXb˗ZjmР#>sرcxe}*;S*=v-0|G8>Zjʏ^rboVROJ:~G>@Pp !5*6{=OjPv/Fۛ4iϟ;wʸzv_#ݾ}Zj}ʠme'k֬mo߳QMMIT٣0-ٳozꉃQ繶{yyyM8Q=uCy&@sوM6I2™yغaZV:a„>:o޼.]l^Jض;QP=I"Zllʕ+g͚0-͛=Di̙wx:ޛk>\M:8=_a-mx|Phy\Xa?Mt… wygJ:t萜LǷmCL|b5jzoFմm;cy\ՠA3ghYͶcS\w=n8[r~R_0j\߅`&o m'г1,Y/[7Ο?WAE_IYlrrrp'gϞ7222 .deeY,| n@PbBjTl{yb\jc8a,\M:8=Flj=$i_9^.10y&@ _a7@PBbpdXmx|Phy ŗ?nҰaMGw0B=x`z0Ƽ _,+xFCXr8LBQNuGdB&ΉM/va4z.'`IJ 3oϥa߫6r<b9PqBv;}yit)==<H@FF^PtSy a <0=1.?;}c?Ryc 䝽 H "%ßh \Nw!&\ѥa[fda(_ھ};깒깜0BȉK ƥٝeas@O܇?깜0BȺeQ[ 0pڛ{ b[/44޽{)))F""ßh \NHi!dމ ykS{aPz.o-[@o$"9) PqBzxt9nz_(*à\{t%#ǏV^k."z |сrR&4++Kٙv޿_e[Ew[`-Z͍da4z.'`RܾgG8[OEC)s_KU^?720k׮Qw!<#-Z?tfggO6>SN="##ٍ>|8sLr5k$߯*%%孷";9uTegZZڛoI:g̘Od^J./^QwСe^>3|Ν;wgXe͊s28!lRz^D\AzIII&6~xcǎ|aP^frg999tQe3,L%'WϟVF{qzAsy(TuLJAB&VتTbRz>/g{_z.Wjا~ ZPԯ_\]xqyt\)h \N`!< jIKaB=@(ewЁ|G7n]* ef͚ӧO_fɓrO:6lH\rРA&o~̙vvv#F0)*5 Z*٠{w^t)[ڳVM~ LJ\{Ũ=_رcR?3,L%깛7o6)[>8znVV[oE.۷y5\ir^zoѢMtؑZn:u갗Zp}7m&*<(+e a <0=,2+ɽY9 tXPJۻw/xUԣ?yO>\CBB>8N>ɼ<ŋِAzB6YϞ=s&/Oa|֭~lܭ[C^co6H [ٳ'yhj#*+e a <0=,r*̹rKþWL\#S*ݿ_WјҪ_LLLVVjgqqqll,^_'O*? ڬYaÆ=y)}||<}4˗ݵkP3,Z-Az䜜r\\{ݻ)))LΝkRYc[i|rјǜI"؏Fm<.TR깜0B7v[>bz.TuF֯_O*ݻիWW^ e'|ҠA~zJJƓf@=W0B=x`z0Xv#*H͠\ ڲy޽븻Ϛ5[n_|-*+5AI?\)h \N`!TZnBʵ]|_aFYE\C=W0B=x`z0XZ2qK#;.Υ \C=W0B=x`z0XZu_Lͣiwz.衞+e a <0=,ȼ"צ9)/(\C=W0B=x`z0X:0`)1@=@Pϕ2Pq ^} 6qb96lR깜0BЋgucGFÆ \C=W0B=x`z0Xzt.ħ/G=]c \C=W0B=x`z0Xzao%z1z.衞+e a <0=,<>/]1z.衞+e a <0=,(v^g;Ӵt zJFCXr8L Ah؃n-_7;Z$za4z.'` (fi/ ]q3 \C=W0B=x`z0XD_I_dE=zz.衞+e a <0=,S"fWƭ~za4z.'` UafU\Z{) sD\)h \N`!876cs^R*1sD\)h \N`!y;&tXgP=s !,s9a`JFCXr8L A|?닑OdXyH=7jhm[rXHMoF !oH(Ҋza4z.'`+=лsߎO:{ 5Ek0\rP\G=wbY 66ԋ!ŢlJ+R깜0B'<ۍ:~F[ \=\EBCC1^JJ --A҅mz5mh }CEٴVs !,s9a  1O-/z6.C\4'mrqq!x tI-H5^ )eVZQϕ2Pq d(엹?(`=zLӮUD=m܂li .l[hCkHR,ʦ+e a <0=,"jq8Е1+\-U6tarDZCEߐbQ6m\)h \N`!HRqn~c2S+cVdijd8f碱M[e k$R/i+JFCXr8L AŠ^^}YH{!d>nz.۴uXHMoF !oH(Ҋza4z.'`$ -撽 |{AtJĠip,tarDZCEߐbQ6m\)h \N`!@#:UdJcTsئŲ@҅mz5mh }CEٴVs !,s9a y3ٻܠ|`[QEShp,tarDZCEߐbQ6m\)h \N`!HXAZ֍?wхje 35w~'*ߓN玿bYH<;v,ؼ~5Z6 bӦY UQ5m\)h \N`!f/TVMxHz-:٦d~R*ކgm7Sm:\,$]Wo~՛r-DŇʫqqgڶ}mxguW5vr5gw*RqKN%\ņ׾zx?wB-ICR\V9܀Ch?d:)شVsY6l 㐐FM^Zv-{:7&ۓիW255e۱cd- z.'`sU駭;o6/??W=$VF=}ֆ=g#6lgӦ !ijlժ9Zz 5oﻜ;Uٙ Lo?S~ .]:L3RC_[7i=WW<mrsڮ6GOqϝp5ZdV/ꑻ%xuoJ<#EkE鷼zx5V< WϲNƓzҸc dmٳp?OCճSQ\PZgN{""mK>Kر{Tuc˲U{D_^^@Zo0׮;;;Vݻw?}txx6 B=x`z0X͈ʯm6{ɫ?H7ZlF.KBKNR99r}KvZene7T6~Um>r@g!GVCY\6=4z(3Py \^޽cÆΙ3zϞ&*\\,TQ͒OΚ5RsD!3"m߫TwJ I3UNxr-D ړۧj{Kݕ=yFҗ7x=_s{HѹRȲN4UC+8m3u'=ꍲ;lذ؈{{m6jɓ'6Y++ի[l!lƍ/^ۿ5~w[  \N`!7%M=4 }Ν ^=V6DUy{..HqޢEnt :~TV.6mGL'Gk;QϨ(ުޮ]kOֳd7n-[63Q=ɓ-k~iҥjԨn{hkk3.Q;l:\,$]W}  9Fv{ʔa;3fI;iz˵M>/ݻv-ҥyĕ+m+{$=ߤ75>#GmC굞zŵc\5\"s5)@=WO\A(&Wgؤ*Q>iӦ~ann.k׮ڵkY^zuqqz.'`@B;kwfs;wnW݉T{|;WRwU֭s@+>o լ96S~BvUI:U~~i+v/l@j6N6?xԴSo%~$M]]6)?ِ{ø3dH'9TմuXeI3=ڂt6mՠAMJK\u'\MoYMJx&dO؏ͲM\wqyi$˗OVh|Fچk=kTes?WC+BYڵsyY ?x䴉:n-PՉ訨(e%xU~42Pq *;&ޯdc9By[ff`\T[vvz(??}7寵-''ɓ5vi| 3O]sMq!bY"ƕl?yjp|6O\WE=m:\, $]ئ\#ц֐z7XM[iE=W0B=x`z0XVƟ]hlbY 66ԋ!ŢlJ+R깜0Bv^_J\4ip,tarDZCEߐbQ6m\)h \N`!Qƭ6g;Mrsm!+wj%zSg#)bY'^vꝆФk$ڈsR|qQM}&lJ+R깜0B̜?=p9H=@ZI7\#цِz7XiE=W0B=x`z0X"׵yIfб [BdvX`v$ij&-:Ѱ?][;?{{{77ุ8:[ZӤD-,VBDǻU!:Mڬ5;+ڎMDS3mg,Sꭉ0o7DǬC6q&}OtlrSWNI^ECzk?QC:mO+R깜0BċtU\UŅ54;v^_;`ẻ!}!OR@@gHtۃPݤ?阁!sɾ@= =Z'ۿx[<ݡn+萾,+7hxߎ8D '<6o eFh;]'r:3O+R깜0B0rM\ᨼWAsCCCхq?4W7m98m9 2$$/%%ΐh+鎫945sa#t0?uCA!sh=s\kC'8C';::X㼿萡YձQo~6d!O=z`ɒ9nOrç䏎^rꤍ+e a <0=,cv-һωg1^ˠ"<<<88ϐsrq+ЙO~0/:cdI H"233 @NFuNx<ߖ=?ז螞s]D sv^m(f4m?1q紫wcN6i:f|]=\zL:jйtLđb%l} : 8}dtH*Vs !,s9a` "쒙?tLDc|:C:Pɤpi1UdX3{Nn;o[:czu%-KKw]Fjϵ'W[wΈ a22nlދcʦCd׽h:="Hҕ+老>J׶w.|lV*Vs !,s9a`R#|s%VT=姤;P/6Vޣ\E@?<ڼ{ 367|kBA@P7ﹳݫUiY,y:jK% o(39 >O`6+/) EQY[|<'kҏh$bS/N 3*[6GH~Jϩo_aF63HIBӮECza4z.'`Gʥ6!Vg1@=9tiWGg..;rs΅1\/3r~5E|25ՍԀsȸCǀa.En:&-MT[΋Xzx4K*r:f5O+e a <0=,c /õj,sy]wؕt@1?uَc{Ef;Sܦ NR/ߢ{u)K#*Sc-<ۺ(Ⱦ'kv1{ONԍ;z yѣS#i&\7[_>ur@?v)ɼހ\Pϕ2Pq z_w?M} )m,SnÐ{t+r1:Gl?0@=W0B=x`z0X(;7drv-vb9+cVf1#ϩMgkSgб'ѽBȉM ˁLV#:&:v:42{ 9 Aaz֥a ;μGądЧӄ1QMHt@ JFCXr8L d܌91!.|Vya70U:f7KL l|tP{|P7r1Qyfy *]NqG?_I^PHD$/9닑RE=W0B=x`z0Xג7_=F=W]9FW8K r`,sFn5t {=?V PWm6нʉKgƹoNjגּ)"ȋX":&UqF_\Xf?FK#ǽ%yqt@Pϕ2Pq (ɽ2v#깔{eywֺ̜] VRaFY+Zk엹428J CM-Hw^K^Px}6= cƉ]c1{#7B=W0B=x`z0XB˸݈k ]Pϥ|k>x$zhM%qtDQ,>{ۙK<6u=^ÐY9M cF%71|=&HL<6|:/8rN\O ?t ^yCl(ͧcrmfqs^EQ1Z> R깜0BлhVCn,mC=W)_tK>v yItLP1FM߄ + 2߯M~J:J;UGQ P] ٵcG:fHsid2+ 1CB^N^G$N֤_W01s !,s9a_ s;ľ߾}ysj^Q?Bw{zHrvz 91"AN6ώAy-Os Ur#'ƛ=$}d.7{JFCXr8L An>1tP/Bs5M*t@D7Ƥ OEIc2L.& 3ɻt*I&^c@^dž4o򵹉ἢM&4i6AВf&om4|ɇ5 iT ayƤ_gLt \ih \N`!腼ʘ羛`hj痑qu-[l$hW|.g]˟ Q=X🎖G\n;Y=nڱ:ݶIB֌zyxl{tL6[#@9o?_mitL+gjk߇KgΡcf㐉jwĽ{ۻ E?0B=x`z0Xur?I4sqq!J;'ju;}7/RweuٱkN8t_Y;@@lwo5P1(cVw56v-rnPS,vFۿgno:CkI:zӛC~?Q݄tٍ_pq;!oț=/<<N8!";{2Ȏm;;D\Bk?eC˷!]y\2vtnAO:~9tH7rh5t ^6LnۘEtZi_T_ kJ~k:6z6KtN~:p32sk$oț=/!!!33~/("8Q깜0BЙ@YcWqJII!oIII/YAd>{*kO1mܴߥ;RnKlF-Z5fQ/?ݨoȶtL"{dHB@leMOYs/2inoYYw._cPw#|? :myGeffY^ ?0B=x`z0X{ݵ;tJ褿n8JJOW. ]0U³ af}^VCn.O#K OӽƏnY1tL?(bNya}KN#)bS:%9Wwk10-Agy~juI:U a <0=,hjHV#:F(;׹^$ =ܠ{ȱ:.SfK4nS. ]PKǀoKt((widv{pE)۫6H c uGd ty7Df TTa4z.'`Kc#s#`t&L\#kSb:\f{藼(t__'EC: +=x3m% cG iihPR.E?(|t(}3.-:<䶬Yzx4*Pq (Pu] ӳ!Nѽ07_3'.mHϼG@ ;\ZpVl9|V#&TLfȟH:B+x0po&б(H͸:eI\h? |sp !,s9a%&dj68d܌qk1PQRNIޗ>I|ڵH:-f 9ͺ΅tȟz;+R¢M\YDo?QCEQqd? c`Hٻ4[SBYqIqlr_)TdGFCXr8L AQ.Q"q"{Ud?89qtXͽN ïV޲d{JZgAV;. O f10>,o:Kzw]OE]7ۣPk:WUaw:eU a <0=,ʑG5 g\ǹAkS7Ow9`h볷xxV#:KEd-6lyWm6x|hYӰI};Ё2 ӵ=4Џ8YcW(h \N`!TX~iBr`C!s{ R3n,ǹ~SW{M,\U tx#oj8/G](j?kMDGuNl•qd摛 Ibo%(h \N`!TBAl.OLazw{w]*#YZ{_}k;C_m04)M-w+[+#W|sϋسrwis/ ]p؜r_ɥa+f[pn-FQTLup !,s9aPQ؆.ͧ`bY-{q|ӕ1+p2bc0QWT]>fiWwr󑞟ȼKo,z }v+bNRh7*Pq BD,u J(vNDB%y_5|%{]$=< [QN MO^J h5ewE#V_;˹n:+?%ݭ@p !,s9aP~wrTL:/9JtåY[բܨ]`6WæP\{7c$[jMWw8eɗ\&Bߙg>BAo #RD&"7+!v4=a+N[.8Q깜0B(i;,s:pwݑ^(eIgP¢Zf\i\Nm9Yc't@zYg Ec =;Z*Pq By$z6Ȏ }NByڼ~t@Ix"og>S8 HbĜ٭('Es!?Zxx'6`8Hrwя)5S.EWDF]f;:p !,s9aPfiףd >qn [* 5ã2:ɳ!]swnb CiBȥe8U+ CM-=蘚dk{Mwm#)-T<$G$Sei~JY~ݦx$35z&8Q깜0B(. qr^(3yac\+¢0sߎMbA'~LJt4&5ݿr>kN͆;tڴMzLXMrD)rҵygMhcYC@ Ta4z.'`ʠ0#;v#Rܦ{̮N^8x^NIVY;uB#n x"Ś$ˮ,n:PFnKC/-@_ #35 %?іЮl58Q깜0BxE|go .7c[*,zs@y(;|?ѣ%C|Ȑ%=8ϝUȋO}:M ,Ȧc WؽՐ$t^U a <0=, DKF,EB$"a+G,qڴMa0 $5\G:9I$+^dQ|?1`7/)#u`ЍG[%A56 Gҹ:\ °<>LG@8OU a <0=,^NhcYE@tv Fm%jmQ{MF͘6 4%AkS }࠹Od^qɌnk/Kvgv:iک=|;Oߧ~WQ%,W_}^Wƭ7NsG,9՘O!VC"\p !,s9aB.`6KoU*oI=DZO|Œ|طo߷ze˖..vr%K9wމkҮG竓+N !\Y7w>QH姤Gml7EmFo!(GsHc'_?*?0\-..ꫯ>SLB.tQ^?jժfffO:wLݜl־}޽{_vm̙M4a T2vX\ի<zWI05CE䀇n ܯ۔볷A=YZ{U͕5ܠȓJ:"O|((ɉ\(AnhnnL*éJ05˿.ǹ$('tEY/pٯ J>#=X^}@:3@qG'ߪ4vyiIBt ɿK#4!t_!- cu5[+so9ػ(Al  AUQ*R5HT)ґ" *[HIB !B !ݽNgg.dfgdfuf/G,}g] w+ήp窱pSW\333?g644|kiixT`511Q8p*۽{Wkb {\\{~m (t+ y˂;ItfaoYRzh|7컌oJw p/ѭz+?+}|2l.[gȣܠ[w-҇uʂdٵ7$ǟ~Ccv66bAɿ/wt&!~~~gϞUdYO=O)?š~z]s/1\=xW:*r zύ;$ ?5! q^-++޽{~"""믓FE깕{~7CÓ߈[05A L?,wn9>>f /ݧWy&9u04ʢzo, yJӲ4$/UťܮS~"$Wx4mem pca.#/}23eyv>~d$:fvmdӭ˟jFDDƍ{oʯק~Z@6k瞳WuqM SP`!#g 2BjmdS~"s#٧~U1wpyIpDz.<%/"7.\&shz;!U iKUStZ j`z(}|]z]e=l T\Nxƣ׻0͢e?מ|c)N )ʘm#JR%6W+,llz$pO\Ԅ4ąz j`z($ȕ.x3.);o쀂;}<{ز4=HiZfN\/EuәYAtKmgߔ"ʽq7]F5Ԣe9HrG^w oR9ע/ _PP@ LBea+''rYt+k-zJM**S]on;K7꘢DnC'k 86B%"ၽ~oE )λ^~[V(j )˾Q7Uwѭ#깂BOrE4Dn9~ca.c,?V<*$[y|u~_w޵ CQzW˧УG'?%Et4>2U95[Qiy.8>᥯BL;("ɨ#7#7"־/XHC\ BB 8 53 VYPle?@$mN\l-+Di8۵7wޕ=y֭ѭ*ᔣm;t42rH2uWSPcve9N $E=5plW'oL Nyv>y9fD<*1깂B?VYyUtۍGJJRyv~q{  %*>ATA?t.wߘ@rZ:u뻓>h4Eg]qk,#9c;i< 7 S~RL)r. 3ޤt4rr8Bt"˟Z! q+9 .Fx3de_;~ CKRja*Mˊ=dwB\evʳ.2z.Iã\U\q~cB輝cQphuWƬLu;1zY [2feq{nޜxs=f85ez4}3~kpщ++;x'˟@B=Wr50=RZ߮ > X[t+4*INtk7I?NҭO! q+9 ,}fl[Azǭo~nGn?a bȭg m_3+rܳe?q>./2kdYWo]t&$`]ߝ4mw{ %)gNa.=&F<~9˷2{xnq8<7o@9mH #˟:BB=Wr50=Y.J }\$qpZe?x jUT>[Tߍado>|W7λڵ7Lq;^Qiek05kom2x]q~!+gVt/dBw@}ZVI򧎐P@ L4B;t+Hzf> VVtw 5n=ίfqNiedtGé/z6]Ȝ59unܣћ4~kV}TvH`J%2:S١Jxzw+JDR;!.s!P@!֘}VHѭ%nmnTYT'd.#ߘ@ߥ]G'?$kcWwQO\ǿ9S9vfWuTsV_n;IQ's1)Tx7w\;@smWO! q+9 ,"pqϠA7ӭsmQnD흧=,ЧKcޕěvKѣ҇953#U5[9'___A|T9ۉtkoև鉧ȩ۾HWFJהeAE͑B~G[FP@ L½ nWƬ[A5z9? V+-=Cfow:apE֦_ׇ#eD5ȝC-/]_/ɏpv9¼[qTcK=Ε_2s_EA&Ê,,$xVIIK-<| ЭG#HC\ BB !xqj [@B4cKot+譼qwUރs#b4-n1-7r9dluR&c[beH,Ul&|ZqW3 Orz[B5dkHAUIu!9R?AB=Wr50=/}2׸ Y7慡%)tʢ'0o~mqDλjtJwAWMεhqJӜrE[q*YдͤE^tcoU7udK5-$x__ϭdVF.{>;\&Ѣv,(mgX Y! 5!g<wvt+깂}!]7nI*)n3n2| )JL;j?~uAMHGHU|\RUq&9עSy铙WDF2gyi=;CE~c +k.ks$KǮR~[FVeuP~T ҭ)MtSZ 5Z\vHjnG+^l>ԫaut+깂}!kV(ӭP. +2DG߹V [\nzG%<7$ԝ1U%e6\3+zYgZ>ݿ"ICnM"f>yK3ޢw3r<$I'(6ۮa]J̾ 6/ SkV%2! Cu 78R7hJe9dI裶ppZyVu{*)/44ąz j`z(^r}wv^KB}\^d< PYP/lў27:yc)G ٙ5$b||ԶvGXlXS~5txuIA p˧wpYz١J۷,"Dm?Iu%A ϰ:JYEe͸6ƾeڼV)ͺmKĪi= $gNz%]=ǩXYY\& sY7?w2r:1д=B?F+K%)|G,S}$8!.s!P@B W vѭ Ud2݊nA7ӭ yy7b\njG#VbkmlEn˚ HVQxuO-)KfGiy[),tWg\kj _q~λplB!×?;RJ?D$;tUsnWq{KӲP+zKѭ<2P@ L !p҆3t+HT@U^xօnGv#"?ߢE_ejde9עc[M柙?wԦgh"k_@c=ZԵ{[koqdG?nǗ?4ąz j`z(^sAͯ%:dT$)F܂aun>kiCUI\.l闂N7O'[;[yX˧x |G|!^l 'm 63T5 <>~9tis3 hliA}ҭڭ\ȫdmH_깂w!fھ8ndO*է75vӭ TYld  YyUtb[0 PwރJᏙ8!.s!P@BH RU Ū^&_[Aܘ<6/_ܢV|aKum[ޟdv}6K [9Nh]OV߈)T5pAUUn<ż2jsZޣF/zS?HC\ B߅PԎSt+HUNX>ݻn98 >6˯ m^l;6l48;.lɏd:9>'ep맣7AJoZ~4D9hvu&;^깂w!x >r0 Rvp *y_H*Uz.6luҽtYk'3329bKu jBJFA3ЭHsr5n?4ąz j`z(]/}UtwE 7єCQX&҄z.ܔ܋zzzp{ㅪRIO<Ȱ8n ywҨm'7[E$./^깂t!-'q(]~ӭVYTbt\Nw7e=k f5{"}Xz/2rVɨ,(v=5%j[?4ąz j`z(8]щo~M\;Lɹ#Z7U=̙3k UVq#`d=-z~1/'n䇎fDP[5n ?4ąz j`z(8]#ѭ a!sweKJ[٥o~[APրi4}si'Ub2D8><;N\pґn ?4ąz j`z(8][A®]lMJ]g"VF47s%]333I6rn;Xr Qѭ|ސP@ L 1.Э adNJ[1MVs5&z.Rt=~M/%cv!ӭte* =l:o'./ i \APpǯI[A?CJ[ .ѭ bkMJ/%+~~[%a+ $P$q&Hӭ|ސP@ L gIڥ`$Iyty^B47s+w.*U-==GBl>./ i \APp<>pn s~cBdU\{~w+niд[TxEbse5\wrɱ%K&Y[cU.scbݿmta@J]1[%kdk9-#ĥD/\^깂t!{. WVis|uLѽ4p>zYfׯ7^e+WNgU.s{lpl>O/%Ҵ,NFtevyFt:n ?4ąz j`z(8]N/@ٵ7,ȡ[ͮlܰ0-hꙓ'7߻ngϲO>yݳְS[7m7ēkUV^fa{ug۸q3_S S&J=Co'8{ yZ˩uq:(<7n6 ܃ViˏNmgH˟zCB=Wr50=.Q /[ͺ퐊B1cx{cTLF-YϥW7gvymOXk&-Z<ѹssk}ZkUgwҫ5LKNAyR]yA3{0vzmE1F E<)w> ▱Tu:n>rVgKE%V[-)?ѭV ú /\^깂t!rBY>OVVNJ2s325k6d'lr;yr៷ilo4_.T-+ ^jK/uh?|wCƏxʯ33{nj3=d2ujKJ,۴mjٲɪ dD Cy)mݺeٲe^bhsHׯݽ{ҥѣBß3gܔ)F/"|7-, [C2? 3nr&=6L'!?CHhO<6\r)+([^c8յu~֭c8"h؛&Ɲzy~^̒Ul!Uֳg?XիącǾsOm7P{O>yRu+hїn;IoL[آe?/\^깂t!?G!ӭ af>$HY]W500رÄRn71heĉȞ(ۅs3f|uO{լYʮ^=z봴^K?owĉNNfy6ܸqc=BIأGw;U$;-ۻZ]s#?kڴZvDvd >…fg/իAdpy].OWVY<9*qr9˟zCB=Wr50=..`!)K=]glc-:چyf巵s.?c& Qgey7klܸAor[POZ?0Ul|DLdǎ/aQQj5Oc35N@Z~ڵ7$9v젊PJ'MwS~ B~'Ml'ߢsK}mNF8vېS=2= [lgvu-)~czqE@4*9r4C]F9p`={*zjYfw|!.s!P@BrP L 2s?mkWk&$8}n0<С}߾>֠F=w7RSR.5kl/r#c2ŨyDz"4/j#_vSm!#٨~(q7* E'TOFUz.:<<<00г[[[Lw:rkΏ^h ӷǚujgm[5i8ϩ~؜ToPk=henmz찰~מ#B).^ihؽUʛ0}#9:uٳ־,d>!.s!P@BrP L 2sUڬY^hsxǨ(G7 ] ϟtаLL&*Uk=lFFڶmuК O'٧7o|VXXٰaN˖-jsw0!:;VPO^'1[v͛o/:H,lBcqq} z~Ks6eJ=}|lk_v(q7*=[19ݻ?W;Qϵ055=wܙzʹGM0[5_-4|[ !?w-Z<ʯf#z~}vзo^xᙛ׼2 ݁3? τji \APphSL\ʲWfz`ʮS9.4=z?E/WS봴ÇNO۷BSBC/+W:J8rGeWbMZ QO^P?kr0d'x\ZR& {n=أZz'zn^c'm[$W;qÂ`aG_߅zFG}+4+WNvilc~zIQ=ʒBzɞB\[j%uoz>B&,3~AB=Wr50=..`!){=Wefz32R[~#Ȩ}WQUTȶS[YYݻ5<u|zlH 6*=ZK0!"}6vG=fffϟ?wٳgUu^U~&PGȄ}&P#HC\ BӅ@,d"es[NnݙM[VV[CdddxxxHHH``yU^s3? τji \APphSL \s綳oջMPWΫ*%YȄ}&P#HC\ BӅ@,d"ebk\!Bu^U\-B&,3~AB=Wr50=..`!)C=[n XUCYE=WK8 LFP@ L A60%XDPր[sP,d>!.s!P@BrP L 22s5zjC=W!p?@ q+9 Nm`J&zA&,3~AB=Wr50=..`!)C=[nMYU"O~ Y'>}\Ǡ_ LFP@ L A60%XDPր[sLMMO$r`AC^T8 LFP@ L A60%XDPր[sSRR555=c!#G*}™LXg54ąz j`z(8]\ )B&R%ˌ-<ۈM͖ސL#BBBrpp1ȁ#I>zQB&,3~AB=Wr50=..`!)Ssak))))䧄zrHᑃ$Jpg p?@ q+9 Nm`JUf)7]ܸzʴ㰋<`=PR۶|]|ioOA5Mc333SRRϊ1ȁ#I>zQB&,3~AB=Wr50=..`!P՛sfmjhdM<9pҋ/yޡt3? τji \APphSL@IgoB*|>̿iB7qicL^bm-Nw/|XPLXg54ąz j`z(8]\ )B&7!ey8ڏYbnFCV 5Gm1vnMcAg~2aq 깂t!p9( ބvΦWEE}IC;dxnc>uCo *8 LFP@ L A60%XEeaI]/NsJ5>Bfon;šKt7Z!p?@ q+9 Nm`J LB/VQI=S\zLUUq) g2aq 깂t!p9( WD;Q aԔ?~W,JLAa> LFP@ L A60%XtYn&_SK5-VqRZĪC ]Jw|f!p?@ q+9 Nm`J (CM!.s!P@BrP L 2]xžQ*':2f]{Uݠ0YȄ}&P#HC\ BӅ@,d3 τji \APphSL@GG'7)ytJ v<7$_Q),d>!.s!P@BrP L 2]pK]{tJҴſؾ8,r1܁AG`> LFP@ L A60%XUUR2wy7>]Ԡ[: oOK3 τji \APphSL@DwޟruO`4+cW9<:ᄃN}ޝ`> LFP@ L A60%Xr£ : +eEz mt4 g2aq 깂t!p9( @ӓW"Vr667ݧR\ڥ`y{. LXg54ąz j`z(8]\ )B&2r/<;aX)A.ekaxފ 3 τji \APphSL)evxy ezvTSSpm#+r3 τji \APphSL$pkoΗX)-7G>.+(B&,3~AB=Wr50=..`!h*\6.G q}g}p0YȄ}&P#HC\ BӅ@,d"p tJi2?j(ƛB&,3~AB=Wr50=..`!hT]6lJnKJibY7&p<}B&,3~AB=Wr50=..`!h<!vWȕJizU7=u3 τji \APphSL8":ݡRĒpͯNXS@A}a> LFP@ L A60%X\~#o~]Lw-U/?`e{?LXg54ąz j`z(8]\ )B&аdW'o;<+gX)pzm|_dthLXg54ąz j`z(8]\ )B&Ѐ*r .zmUi9ݧRtAeAq ?YF&0YȄ}&P#HC\ BӅ@,d ^K?*>;;UKuB&,3~AB=Wr50=..`!hy]G2;xS\zLyx0YȄ}&P#HC\ BӅ@,d 8"]SYP?ntg2aq 깂g!ڵk#W&_냤Uwٕu1c_>55UեzԖ-[dMwX@`!R`t_WI-<{Çoݺ5:s挲=66[ndիWoذӾ}1bD۶mɣ͛loX@`!F]*brI*)6qD2u=FuWLד'O~3220Dk]{$SwB&,3~AB=Wr50=5TgΜl011!ן?Ι3|QVVֱcǁ*GETߒҗ^z?5%x6m(wkX@`!cv]F޸KwH*Y+ZfffGz\գ^7U}=׮]S=!Vc";@,d>!.s!P@Q[=Wz#oUץ'/ ƏzxbK=jܸqO>>5E'[nU>Cʻ~G>g(,d㋓d T+?i& n*ەwU'4y,))YȄ}&P#HC\ B DVQit+sΝ*V=ȑ#t˖-{BA.eI畏0`uxxx}N@`!Ho~]@rޥzd_ѦNJ^&M4y,XuSپjժ9s4oN "bAw'Wi g~2aq 깂*.|? =sZnME*n|qrzunnnV>_.r<..棚N@`!$5ի$ON6mܿ릲}UUU#G$rJEm;1JBpg!p?@ q+9B6VQ]N0!!!!""7h׮݃Լ566nѢEXX4kl򵴴رc-[\t)&?P0%Xϧ diYt4sJ |f!p?@ q+9̀:$m`J daxnCzR\~uO~_W.|f!p?@ q+9TV<@,dB2#lf]tUJk*%a&{|f!p?@ q+9$SIV<@,d*gqD`CR]E~ko;$ LXg54ąz Bx}>@z LFP@)?;JHI.`!I^%jEmtap#Geet`> LFP@ѭyA60%Xjn%*CRxZ2V)|f!p?@ q+9nc V<@,d*1MzO,(;+/EoLH2DwH3 τji \Aj%++| y.ϓ\ )B&bɨ8)GR8cް$%g2aq 깂*/2mc@!.s!U7[$$m`J2{;¥<Ƞ;xB&,3~AB=WrZOX#OB$m`JE8uٲr +W778q=;g2aq 깂ʱ؂t+$qyrP L 2^컌,IGBi+WU%eI ;B&,3~AB=WrVyNu $.ϓ\ )B&UuTGJ;_wet0YȄ}&P#HC\ BJu = U\'hSL$J.v7%H@g2aq 깂nx֦V<@,d"M{{+;@V rw6*.;8B&,3~AB=Wrڭ<@,d"Av' +{n|n3 τji \A(UťV?'.ϓ\ )B&RS_}\7japNm;ÊG,d>!.s!:x \@HI.`! V`G'Z)T/!hڦgVB&,3~AB=WrJآ={ѭyA60%XDRܮ:vXBw#f߅ 0շ5W i'o]F**B&,3~AB=WrcV <@,d"EN;6e9/StU+v]#_$q[3? τji \Ajʻm, m\'hSL#d?ӭ gRg(KʕB|Ixv)t+wpg!p?@ q+9@M777@ڸ!.s!"پ8(!.s! [V`pyrP L 2IEn_`P ݪpg!p?@ q+9HSa\m;ò\\'hSLx|дMtT ڋr˻QB&,3~AB=Wro"d"" UA|"shҤɷ~WU^J*ݻwo֬Y]\\"VZv/V A.Vʶg5~xMٲeCBB,,YkРAR={Ɲ5?iE<8Nɦ@ "dBCПOM]W9s][(Qbȑ3f`*T\sd׍[nw.'vo߾==?Sm&**>q {JGW\J-]˻r/7}KLK.l狛e`>+Ƈ_LD3!ߠ*HC_U%̗l]O6bve9}ty؍vݢE ޡCŋo\򫯾*=y^&_dWf3|iu? 2N:,M8%DȄTgyp&"K۶mUb` HvZJDiXɧ~ʮ--+|ϕ,$ݞ;w {?0T^k2E֌\%lx5_5>"d"" UA|")6m)RVZ Qtڴi?Cr͛7n_|wy'eTرqzYhf͚ 2D3/%l)Sȷ'M4jԨrիWoǎow.ԩS|yy43˼ݸx"ҥ|/fȑ{n^;w.TPzz׮]޽{2e .bl9Zx7ںun߾]*+Utܹlt=Iͯl}A)pe]YΟ?.ǎ˯d&M؏6sן5]%J`>X2S4Ƈ_LD3!ߠ*HC_Ur0 4`۶mcWNueWl|n &N(m9~ʕ+gfM֫WM6aaaVQe{puuӧO@@=&&&3k`f2YQvͭm۶ޯzƍ!u̘os玴v{̙8o}ן:uiӦ-[dM2ecǎ8c=T!dSN 2!`_FyDv˯9s> k=4t =hP#z_?GwX6[^HK/(hTs/xQ,ټ h|EDD> 4\E,0vd.^=pK߿]lG̺ m׮իW ,8c v{ůs5knO6H"mEeWѣTgkQzJk.{Yqҽʕ+'}oÆ oݺeԩDDfb2>bŊlo)N鍕Kį}A)]RtRm2t.3gNLLL2ej׮\=d?.)eի*ϭXw}+/?%3X}Iҏ[O>4g_܂\!9HlJ#d"" UA|"4'{a|ZvRZY~}0١C5kQ˸\Yr9;p@˫ݻ.~{O*<Of4zj^͛7>r6SX>Xʕ+s3&kԨBޤTMF. dSN 21?4>ȮʤXNn. ̙nxyyL=*U-[xy ժU]ƿ6:%TOJ:rÆuo߾7,JJeydV-5$ɦ4B&"oP/*b9?~]y{{KsZ+֫W/.^>\P7nXl/ofݻwJfl'{+}zѣ.^hy/ٮƍgk.Gom_̬7+ѢE3&\ܱ~:2H hSBLnsGbccgV)7l,ӣGvˮ6imuԩWt;3߮ըQCm\%--y=R'亻C }ZyfΝ;*Tx؏Gb famNuTk֬ǮY2ߖޛlhZq|r M8%DNMYzL&ӬYUtnkN/ wy??\ɴ6[Z5ilpw}U (ۨQ#yV4h@Iy>СU.]׀۶mnCZ4n.tw/ԤI-AdS!L7 s9JWˤݹsGR||AK|UzܹsgϞ/UU uVzz:2ݓoˬm*ݍ']PBɦ@ "dbh[cD722ɓjԈ]a\zU,/mܸ^5'ws+Ȗaúwsa6m`wҲ1յkCr$ٔFDD> 4\Eb|nIJ&}-%tд/8N@)K|˿kgAl )<2rMAdS!L7 s9}}>cǎܽ{_w߳'+@ q$dSN 213VW6Z0k8$IMiLD3!ߠ*HC_U j3" *'I6Z!ި*<\$IMiLD3!ߠ*HC_U߻啞 KCH$-pJA%]PmS60 I)|&Ti 󹊐Ifis7Wz$IM8%DĠ"Ws!_$I62τ| }a>Wr ;'$IM8%DĠ{XWs!_$I62τ| }a>WrǜB] $ɦ@ "dbDfɧLWo+s!_$I62τ| }a>Wrb]oW$IM8%DĈ\_a>AdS!L7 s!bL)~u}$8I)i_a>AdS!L7 s!b._( 'I6Z!#b6Gυ|Ar$ٔFDD> 4\Eȁs)ZN+@$ɦ@ "db8k _|. $ɦ4B&"oP/*BD` ڐ'I6Z!ù.rC=ی;:žs I)|&Ti 󹊐%[_6$IM8%DpNL\t|| i>1Z$ɦ4B&"oP/*BdvhKf3!9Nl )!B&fMܜݏ.- [[߬3 yxxGFFO AdS!L7 s!20+7UЌ8I)9]e~(` ūj Wxxxp 9r$::HH$gBAU09:rct~hFr$hSBLn-䫠 =wCy:L 9|ll,$I62τ| }a>Wr'_@r$hSBLܜUƔAﰡ?3Lϟ?rHpppc`τ= I)|&Ti 󹊐ɩ>e&^@r$hSBLeWm櫠ޑsҍ=|سOIIyYc!9HlJ#d"" UA|"@@ͻW 'I6Z!c W* AdS!L7 s!v4xw7_|Br$hSBL $vw֙f3l I)|&Ti 󹊐ŝlsz qdSN 21l1-$I62τ| }a>Wr0qy|qdSN 213?<, Y4~6$IMiLD3!ߠ*HC_UlQF $ɦ@ "db ×Yuҵ͵7įІ I)|&Ti 󹊐>Ů|qdSN 21M5şN/vэ_\ͯ$IMiLD3!ߠ*HC_U -|/H$-pJQ' 4\E捕޻w<_|Er$hSBL"vo~W۹YWY"9HlJ#d"" UA|"`\T_Fr$hSBL"CM-[w*@"9HlJ#d"" UA|"`\;>n_Fr$hSBLW$I62τ| }a>Wr0}_Fr$hSBLbw/U1$ɦ4B&"oP/*Bt-wv@r$hSBLN/]`c$IMiLD3!ߠ*HC_U *b_p|l8I)Bzb&?ܗ$FDU#9HlJ#d"" UA|"`P>̟ 'I6Z!CvUa0|=0 Gr$ٔFDD> 4\E6Vl w 9Nl )!B&pyK vr;#uGr$ٔFDD> 4\E^_ 9Nl )!B&prN -uz ߦz!9HlJ#d"" UA|"`D Ar$hSBL PK*u]Xss+ I)|&Ti 󹊐7B Ar$hSBL ![;*ui7C*}$I62τ| }a>Wr0͵{;zm'I6Z!C{Sbu Gr$ٔFDD> 4\Ep%xoJ$ɦ@ "dLi^O4E.H$gBAU09`3$IM8%Dݿp5z vAr$ٔFDD> 4\EpX}* qdSN 2q|7 j3wR 7$ɦ4B&"oP/*Bs* qdSN 2q|;reFylW I)|&Ti 󹊐l|l8I)8S?u||J?<7Vl;8$I62τ| }a>Wr0SZWfT~ qdSN 2q|a~9?׋`0e+=#AdS!L7 s!c?P3_["9Nl )!B&o\Wϔ}_0H$gBAU09K^gBr$hSBL߶9t՜a I)|&Ti 󹊐\:x:_u$&i׮]KNNƅ _7?E4)uT7$ɦ@ "d6Vz[|om$ɦ4B&"oP/*Br%'&.d̙|U7o>+V,_}ն+a,[>yۓU:Ju!9Nl )!B&l2y71g+ O!9HlJ#d"" UA|"`,GF9;c5_'M4o˖-{ꩧL&CY s\ alRpH$-pJK|om$ɦ4B&"oP/*B:hڥE>|5f3_ʖ QF~c~mA#/O (lL$ɦ@ "d^҇|Հ2R.-WN =$ɦ4B&"oP/*B:䧋n`7ڴiSHZj 0F|VӦMiٳg7o\}wy'RϿ3;v7n\Ϟ=-~2eJ64kݽRJΝS(QeO_Ν;?k׮f'N>|xҥk׮:gΜʕ+WZu͚5=Ff7|SpbŊGYjUf֟C5jTrիciK-XK7`Qn۷K<ڵkݻw/S 9{)))clI&qXbw0aB~J,ɞeˤұբe] 4H|vd;[gH$-pJwtG|hozLgJuV/ Vryׯ_gOB 'N6?~<2vߦM#G/_^ڀյO>IIIu322ʔ)3xHgC\\ٕ+Wt /\vsUѰaCf dNlٲzJzڷo_JQuaأܺ+Pڶm7n8SKJL>}NbAlR* s|;Μ97ndO@ڳ<}bb ] (0h OO-Z거բgg.>6JYF@X07{֎8I)8k4ʽctjR~#V2. oUic:0>>bmժUjj$ɦ4B&"oP/*Bry- sqqYdT\pa`K{vѣYڵ6~f߬n_bņ w 2nݺ?x {ҏ ,`?JuAzgKm5knO6H" -wٴw+VL1gI=ڲ)̊fi%v޽{KO)9?p@5ت:w}K?\~}+\r7+}֬Yҟ4J-XYhʕc'gIX3Yi%˻(P`ܸq X-Z}bYWw6jٳv$IM8%Dyo~qu+w'qV2/ ~#UoեK/ ` I)|&Ti 󹊐D,?wնT;w|رcz^,ߟxGx/}fֻ5===-:f~]{a\r_~ 6:[*(kԨޓL?iiή]\_gñNTΊ*jB ,"6 ,Xd (>LG+udr'V sgH$-pJ;g~w\ߴ|U`.SodUڭ[77v I)|&Ti 󹊐Dkvkڴ/_^hQGoVƍ=_t/}h5nݚFEEeZ~}RRŋ\!Cwe3SZ5O:vo wY-~}'/{Icyv|||#ٳ=Ղ J3,˯\͛~~~}\~]XXjQڳ3r,YꁰanqdSN 2qp$vo*uʗV#[D155]vŊ> $IMiLD3!ߠ*HC_Uv]W^}|ƍ˖-ڦM#G͛7gUO?4یԩöwn}\\\ժUُ ~oܸ!Q6tz?rg.Yfǎ76f^ ]#*[}'$ɦ4B&"oP/*Bc6+f&ފ `G$IM8%DTrUH2$͇ގAdS!L7 s!z-_!9Nl )!B&η7OwIѱ[9w)5_@AdS!L7 s!PmH$-pJ.:->_u ] BH$gBAU09|l8I)88'RRc5]|$ɦ4B&"oP/*Bf3|l8I)84y]7"AdS!L7 s!Z;!* qdSN 2qdI)^EU#$ɦ4B&"oP/*BjP 6Cr$hSBLY݄ U#$ɦ4B&"oP/*BolDr$hSBLY+:꽣*u$IMiLD3!ߠ*HC_U Pɑ* qdSN 2qd{_Cþ߽j3:$ɦ4B&"oP/*Bs^WfH$-pJ#K|ÿ|"Wl-~ 9HlJ#d"" UA|"`8'Xr|l8I)8TWl>>nA@.g"U΁ I)|&Ti 󹊐ᜟ> 6Cr$hSBLY+jtv4{_4H$gBAU09Nۂ?WfH$-pJ#?Vwj/ԴӖ+ AdS!L7 s!ù#$0 6Cr$hSBLYK[^$I62τ| }a>Wr0ғ͐'I6Z!G:rs| I)|&Ti 󹊐$ߺS-_!9Nl )!B&-WH$gBAU09d,؜aWm'I6Z!Gp.*߿McvUGr$ٔFDD> 4\E|˿|6_ 9Nl )!B&^jY1g\iYQ|)33~Ҟ ~#9HlJ#d"" UA|"`D[{,_ 9Nl )!B&kջXV\cJI: 4͜$ɦ4B&"oP/*BF*qdSN 2q( :)xn;9yc wenYg;ڲ2$ɦ4B&"oP/*BF2`E>|l8I)8?6z ]P픘mb-Lirn߲m Cr$ٔFDD> 4\EN||l8I)87l}kg(z;nC޸[C[qb€jXVq7e8$IMiLD3!ߠ*HC_UA*qdSN 2q@~tktsI답0gӑfJI.*0~d I)|&Ti 󹊐]ݽ( Ar$hSBLP+(o{ad l$I62τ| }a>Wr0Onm؇m'I6Z!uoxj卵to|s!~#5Ȏu2~d(ɚIDAT I)|&Ti 󹊐=!qdSN 2qL7n{>єiX=~ 5nn?U:7;ϯl$I62τ| }a>Wr0"SJ:F l8I)8Ϧ+&;@ 68ߛ_ƶf [][Çraw񋦴t~SxAdS!L7 s!PM;|l8I)8o--|˔ʯεӑ^Ms]xCUO}l> I)|&Ti 󹊐Amy{.U$ɦ@ "dB>Pi 5t-ޮO6)G:vьO8 $ɦ4B&"oP/*Bzmr8I)R&inSZzynzg* g>efĉF.nw? )#ٔFDD> 4\E|<-|l8I)R&Z|C,1 H$e$gBAU09T_WH$-pJ(e"瞝5VZ,msQ$e$gBAU09ԉNNZWH$-pJ(e" Zk6͝k1 )#ٔFDD> 4\E^{|l8I)R&5"N#000$$󱱱AARF)|&Ti 󹊐AE|D 6@r$hSBD)ksO``#G 7JdS!L7 s!{*qdSN L0kE]9f_pppdd$PFilJ#d"" UA|"`P7 j3 'I6ZQZ+Vxzz??z4HH62τ| }a>Wr0Nloԟ 'I6ZQZ0 @ilJ#d"" UA|"`Pq/nӓ 'I6ZQZ0 @ilJ#d"" UA|"`P:U$ɦ@ "J`>X s( 2MiLD3!ߠ*HC_U *zƊ*qdSN L0k`Iɦ4B&"oP/*Bz;w*qdSN L0k`Iɦ4B&"oP/*BxK 6@r$hSBD)U󹉉ǎyFFnJOr˜9Wm{.z=nޖ?=S[.C4HH62τ| }a>Wr0T'U$ɦ@ "Jr>ȑ ruuuVF,֭_oԨx۞=yr}" ,R}ĺ\0JdS!L7 s!2{7`$IM8%D2|nx{bŊפ˗79_|W--]쇱cǵ^{b@ARF)|&Ti 󹊐A3LU$ɦ@ "Jf>[-WɋJ/;|b[yսM:ݽPŊer9k9. Iɦ4B&"oP/*Be6x 'I6ZQ$ܘ[.Ԯ]%*[ĉ|nJʡorr^}֭tjȏ۱AkWnzm*|^^Z ܫWE&sE裶{ǝ4iH/]ouo'Nx.>}m6q>u]fUT)SKŤCtc%}G KO7l-]OrA駋+WEWsJdS!L7 s!±5M8%D2q>...SWIˠA]F~=a[^Y+ͭয়v\_ruu=xpn;uj.ݾqc;ܹc}5hPb2KN {lO>DJeV^c]|2/\8ޤOZhҤ~FFXns>k׮;aRݫTf0a`+s(Vhɒņޭ[4X*EBnC~voY|.@ilJ#d"" UA|"`\8vAF)R&9O...+VLWq˙3ؖKN~:{ tZ߾ڹsKG\'$;Pᙨ-ҏZ7ȫ{%߅ԩ#-rK֫r}}'ҏJsps+y\vڵ|ؠBzl'mʞ$sJdS!L7 s!±5M8%D2q>w...g#+W8lX훲-,'խ-f̚5Z/WQyvs-?$wȐn woRnĶ`} K˪U?J[*J˗O\ ޵9=óPchgBAU09}|l XֈWu܌W^yͭĵ\\\-,?7Vύ`I߿ߔ~W1sqҥWns63%-W=Dj֬rvиšիj,,en^3q3܄}zszI)^EUG@kG> 4\EpkdS{Sj_un yr7պ.]bɗ.:p6[u*޶m#v{Ĉҽ粥}%K ]#}چ 3Uu)V/(\r>wMHH==BM4ͭq \^III[/e&Sa\Z.[YO;,X`̘~Ǐ{߿\~|.[Z|E_,Y2J.>4޵Dz-ӣw# UA|"`\8vAF)«HTf8|9k>sZv!CI>[A ֽS$l6o4n\O۵kv¶|'g%?Jh5g矯qlvc޼U4*[nYUTjK>xp@|\lUlsϭUj}7ݑ o:||IC\ҬRxŋ |AZ"NRmӣw# UA|"`\8vAF)b}sϕؠWʟZ /11;7LᑑrK|^ebr(JL `]*} Z.{NpW\eua DD.Cw-|ov|;ڑτ| }a>Wr0.; Z#hXI|չyjlJKƤj>1sk{wwz5E5!лxp߳ӣw# UA|"`\8vAF)»DD~0sf0a7|*5.))VQ\r[[,Cw-p.jKUG@kG> 4\EpkdSņ[KͳpS2G`>ש!лvfk>|;ڑτ| }a>Wr0.; Z#h۩sz)wa>X s]K l>:=zZ;oP/*Bƅc`$_k$-|ʴMWM31E`%y+hgBAU09}|l X}Rt,_un^OW Z0 @Z"mNށ֎|&Ti 󹊐qɦ@ $F^mS1a>X s]K\Z2`*_uzv3!ߠ*HC_U >HH6ZlzÄs.2->s`> ޵ٙk|=:=zZ;oP/*Bƅc`$_k$-q|չmxujL5ւ\0z?5u_uzv3!ߠ*HC_U >HH6ZlߧwBOU綡۩1a>X s]Kӣw# UA|"`\8vAF)bGU[m|՘ܳ͸sqż-׷#\p|%|;ڑτ| }a>Wr0.; Z#h[A|չ7mjL|.c-ܵDPa7Cӣw# UA|"`\8vAF)bWos:KW)-~̮pi~Y9fxxʨj,neUk6[iq yxxGFF@o%QL7 s!±5M{;Ws\W/00cxpǟk7GVNw֫?ůp#Ŏב#G 7z~;5w# UA|"`\8vAF)b1Ws~buj|!!!~~~ʵE\k˾ߏy3ڊu;vؑb󱱱k "2Rӣw# UA|"`\8vAF)`I+6UPK?ȑ#ƱiO>-XV.Yj'Jl.F;FHHH]_r:_τ| }a>Wr0.; Z#h6 U綩FsQ|bcc###ǦƟ]cY9ſ^OJ=zkN#v񊏏OI{H8{yKU wL7 s!±5MGG9j6*ݥ.ٚG16bY”ؠV@k[A;[ۨB /gBAU09}|l 81qIs{E.Un8xs]oZMalm'%.!v-*;|&Ti 󹊐qɦ@3:|չmmn vkbt=};1qѦH~@#v-qn߇W܁3!ߠ*HC_U >HH6Z\6ܶ|+ O6ru?/kq/+qĮ%ӗU wL7 s!±5M~_un;w}]uD*l/[]^}8gUW܁3!ߠ*HC_U >HH6Z\ m _un;[ WQq_̌\`I|5[m^~b?T[||A> 4\EpkdSE̞#o m_^W._|5K_v>I8_3Wk 'JOL@@ oP/*Bƅc`$_k$-OݏsqTvPD, #_-<2jN̯pn%Rom(ՆBJ:τ| }a>Wr0.; Z#h ssz`/|sioȀ|6R&h2`I ~t-qȹ⫐ҁ/3!ߠ*HC_U >HH6Z'zoW[GW^..>{Ņ>UHJ=} _(]KDaq Y(B> 4\EpkdSټέ9םO\~g_}BO競dN`ĎR&k †W! _gBAU09}|l 4t;|Չ vi/_H]uFR {~叒kcc杚BJ:τ| }a>Wr0.; Z#h_U'>s*ťž=U WUO8{_d(]Ky?|P:|&Ti 󹊐qɦ@ z=|:#_>_+^|BLb :ɯp&%|P:|&Ti 󹊐qɦ@- NؘyZWR&xh7]^ρ'U;[퍭Nҵov7nUB@oP/*Bƅc`$_k$u N|K*^}]>ʺ ?[]V~s s- ɷ2f~d!sL7 s!±5MF!^ZWة:>n_qTdyuU?W9kN̵DKkugBAU09}|l 4:ݼӖU'vG'<.*->_h={:zxucsWPGZ"_Ux́G3!ߠ*HC_U >HH6W;3*X=櫂Wu{oBL5gut8g_*{"_[x]Ux~nؙ U]|" UA|"`\8vAF)'u+q˩\OQ?v;3+m~9d%`w"_G|D> 4\EpkdSу~uN,on>/_WI֙:ӖvU~-4%&דoe$+:τ| }a>Wr0.; Z#h*Ҍ:1-g`JKPMX~5lUqiƍ ?˯ ƵY#_ 4t" UA|"`\8vAF)nS'Ugh߸EލhىNWH-f`( 7wl9:τ| }a>Wr0.; Z#hBWvf>`3yy[#s;=G|˵⹃_@kn8:τ| }a>Wr0.; Z[S.\~:_B+:_0LMW(tԻ |l\~(0Z5/*XqL7 s!±5+,qQ[~={nmЛfNoUǓy}Ӌ]OL\įP0mڴ=zhѢo߾SLVݻwo~Ǎg?~駹s?23g b7Ǝ;vӧ{zzf„ p쎖ݴiЖk GW7U@@/oP/*Bƅc`$_kyk yv( _uH֔sf3_lQF~)_U#磳3Vz6_WiO^#oŜa*,#9We.@S|& HC_U >H֤6mHٳg7o\ݱcI&5\r۱/s_oL<9^ڹs秳t5:'c6nX~О={˞O6{Ķ8qK.]v9sT\jժk֬|;OTRG*4kݽRJΝ˦8ec…֭}vѦM"EԬYs5jԐ.ĭ>oa)V~ժUl!Ck_df55a„~,Yl2iw<P[=v;:V0q/Z(1ǰ w:FH=P _ɞW)5mA'WiQ]^zy.$ف9;aD3!GHC_U >Hrϕ?'w֬YqҥREۯ]m.'Ν; \fwޝ:ub)S&##Ciom\~}+\sGVˏ_>3FlUw*ϟ)Wۉ4˞eG|cY>?=P͚5SRRX?Tl*==@ƍQƃd'R:v8:JO܏|8]Ub<=gHF_[(Ltzcq +wyb`y`5VŰ0V\wJF`;vl͚5Ŋرr/^}1͞E3fRudv_s_|r.|s>QF{o???v *t h]unv|ʴݱqұ(=lDTş(lzV?|LOҰ3ׄWsbߐSq\]ӷlۘ]qƲu[޽{l ѣ.^|=rl,ӣG6-oPNzI3qVF 6sj HU>b JSJ@BQDIB4H$R($_| dwfs_sym8,&D迉1*Z\/^=ϝ4V5N 6TC^ B k &vR|A޽,XVŖ%t:7<<={ԩrC6l0+WBYyvܗ^^^<Q>疖rK/}Sq9w|}kNd qrrΞ)ԩSG{={M6˖-O7n\VL8sΙ/U*o=>ܤ^}ՐL4C#+#J%?*#(o*/a ^xpџHJp<qD>N;UVqgϞ]h6oٸqc~~>7jРݍ~nݚ{js0|_hq{iӦ8f̘>wNOMX\>L/^=ϝ4V5N 6TC^ B k &vRN}РAssx KQF$N([hY.]{뭷]7n jѣ2[(~*端zu\vlll~/ 6oޜlVV֋/}٠Akؐ;϶ۤIuqW^)+d3++wyg޼y+Vx駵ߗ /Pn]{~/p[wt[nZ➤iӦO_.o{Nh:}*NZ^ 3jBQJCs u z)\k*MFHĩvj57aiiݱcyr 6ر۵9/7n%evСb[g:t(hW^-@B杨ӳzHeqdkOMDׄ?T!/sʅ{M7YJJJZZMIfffs$LNN.--nGӽ{ {Uh߿k̘1_x+juŻTz?=gY֮/&@.X~{hM^0~Ѵjr"^KSʍw8:|A]M%V%~+k?L܉T4UKag{Z5g5y,0ǟjC5~ Av`r19) |Ço{QV-PT*۶mUCٛhj.}s4haQVMIf}\jV w@.z so1pI I#5a~B?W\X;i0טWZ5iҤ%K\|Wٵk~ᇌ]*&Ht*`MA7XK~4U<3>[.ym>.r|%b_441_'( !/sʅ{I^d^s45U!q;Fs{ilN=*oLf۠V~F>kEA5~ Av`r19)Ћ<;45UחZ񪦠_VmGS1|zx.JQfeQj&02'/qaxp|M(:(@L5&'] d𵥻h Q: U]i|חy1{_IfM8#Ǿऑ0?AQP y+uP.4kLN lihjvħH`w&Tk _wLaY7ag޿8CKag{N)<N ՐPHɽ@_'9{$W >vf?MEկ7Ҕq=X^_ofQES1`ऑ0?AQP y+uP.4kLN %|́囹Iw4j MA\;μv"%h!MzHE:` =|%b_4I#5a~B?W\X;i0טKq˟~GS+b6MƲnui?eE9Mْ~aL^&N[4' Mqp|M(:(@L5&'u"MMR5+;-44'߮q?2''2ΥۛrjcYFSpj;h:p|M(:(@L5&'wL'Nk4;ޔ^A46Riʜ3] 6&SS+Lph1 '|ׄ j \Ara^crRG?WQJc!4)x uI)s^4O +9*M?#izISF>kEA5~ Av`r19)#wijz%j^+iut-*(w|U~!ˋ>Dn;<'41_'( !/sʅ{I])j WjqkEA5~ Av`r19)У;N}#MMSۡ#/o:$So{lhrg( Vٷbi=Lr '|ׄ j \Ara^crRG)Ir{Ӝ{4Jt85vShjnKyh=Zm׸_iN>8ic&OPTC^ B k &=*JthnASt)i7i h>4\;e 0]O=;,=0H£45z9Q];)I#5a~B?W\X;i0טSKkno)TFsawh^3eDN<<@ )x;#ᤑ0?AQP y+uP.4kLN {hq|_4jIyg4 $^HtN|F{LCk'|ׄ j \Ara^crR_s~ᅨWٛv)T][5[Ԕd݈vļL9LSwqx{o041_'( !/sʅ{I~0ņƊ=7mŽDC/땉+ijbb4ʽt\XԘij\^87: '|ׄ j \Ara^crR_AܻNz4t6x:Q@q?oASS}YE^O`MM '|ׄ j \Ara^crR_Ⓧ>P{jxѿOZES}Pr?mbҜ|.[}OvɆdTLjwĂD45n>Z*F>kEA5~ Av`r19)л3oI8{4|Ͽo{zitX$uQK})-Ѕ|\6'ԸE|kEA5~ Av`r19)лm>[_)c1๛ijڮ/O绿5! @ _wԃͽ[4Jᤑ0?AQP y+uP.4kLN ,+)ǖD >r1OʅEm?gp^{5?iUij%'}*I#5a~B?W\X;i0ט]If}I7}zM5ќj7TsO2V,}@q๛#)< N ՐPHɽv &\_ԋa4*;ޔ^A4շ_h.ZbtZ\x@kSѠ/aKvMع>)i OF>kEA5~ Av`r19)0[oM4)Jphn!m3#te̜̒d`)#n,ql]3L%Y?חMF3_GIp|M(:(@L5&'.)kܯ4s)TMc4jCs :`}oCތiPkX*V[14*8FkEA5~ Av`r19)0sxO)Tec0|?ZoMSFlۜs!5N{P8ic&OPTC^ B k & C;MMI/CPR3 ÈghjdMCkhێq ] Gh UF>kEA5~ Av`r19)0'^$'U=)={|ISIu45Js JMM5sgYhjL )TN ՐPHɽ@ :L&#i UxUhj0ھ2.~m?؊M^^^tf?8m-MFr(Nˢ9TN ՐPHɽp\;κy&LijH#+s_ѣ~~~tfb?J;./|LS41_'( !/sʅ{IL՞&(5ӡ`“Krjc);og45>s9bkkEjJMQc'|ׄ j \Ara^crR`81]|CG]RJsTV2C#^KS~s%xҨ%45J{N ՐPHɽpr;?MMɩ$ÔhaqOFDȠk\ :t:thff̨C:udMᱛs5{I.Go[YYm۶~VwnKK&MpC܊hsnEݟt˗/ӺuF @ᤑ0?AQP y+uP.4kLN -őhj]&4߷=GSi}oJgMjffiy+e˖zիڜsO^VܹsӦM(UvΝKz׺Q:;nV|зxlXP^ɨP؜+¤4C7Ŕ)_HdΜ9܏?ot[m۶#FhҤIL#%%֭v?eqҮ> N ՐPHɽЊR2YTG$EQ?8: ȟ|iC\|l$sϝ;WNݻQFիWTw*ǽ &r\eiٻiƭ[?rW,[6Cvun׮=5Z5{hO5a°ۿ}6KNᒂ3>Lfaa'ƌxК5+>|;7gC/^œ_^nT|Q[=w7cK:ϕ[[Vށ}7ڶmyw#::=zQsr ȣSC8ic&OPTC^ B k & ww&@V6)UI 8ך+x̠Jو#ܹ ;vheeբEsVcϯ^zgϮx*ϝ:u$jnϘ1zΜqvvǍ ;飸/bS?o.M~6ܨK> 6h۶ҥS||i34ݶmъֵk޽{KzNNX}5z]}7uwuG׭[gKZ=fg϶;v+WzwzIBPl~Y;ӵܖ޲e ڵ ]\\ T|Q5hIb< 6TN ՐPHɽ@׾u}o45 ./|DS -|Abv7MV׬YӰܟ ;v쨽ħCmڴ^${YEU>\ロ띸'nvVq<8[v^zG=X~'Q}5z*$KSi oٹ_m۶nyy~>2.n^ FRң3BԪUkԨg=n:On'$xpYK%/I@?WDenذ[D?֮]/ϟ?_Q;8_ʼjI#5a~B?W\X;i0טH 7d/Dh <_ʸzr9|yR[tt;SVŋO^N'''H?_~;U~nzu_}EK[.** v,aq6ckanv[l6p`kkjg[fghܸы/↸'*F>s:ujgfjm"P.u犏ʬ»zߥ$t+ }%~uƍo{ i޼õ٣KBcʮ^}l<;y =I#5a~B?W\X;i0טHS3 '-^S˲>^L^%9,J۷dJ5|zŋ𯷠{xTނ() ׯ{˖,{~ٳq/NoaϷyVm۶q%Tljjx={k?X=qb#wxÿSn4#セ.޽+tCZERKz~4~nZZZ&M__U477ݻQEխ[7::j"1\Ep|M(:(@L5&', MM"ESXIS\dqaWy{{ٳ'666//o 4{n?\ѣYf7n(3~633CVkLJZjKڶiX؉MJ4r}t_&&zrO5|{j ԩ}6_mٲw#&ѵPcm{n:~Yy?|'$~4[ )J6Ř1cbbbŽ=ڹsVZ%$$hSqXYYկ_?88yfnY7nܘ_XXo>n'~7GU퍿>I#5a~B?W\X;i0טH# ;:](Moo&M<ؠA33zu\$''s_>s :t6m>5o3Uj!Czq0aq?j*Mԯ_O9IC;&ONKN~e<6\VϚeKt [~&7]nݻ.Vͬ[βeӴ3lXf̷o6(/98lӊ\ؗTɁ~43"?N1+W~.]̜93-IOOg;uTPP}css:6lhccRzh7Bᤑ0?AQP y+uP.4kLN QBSMG˟~GS@]Rj̢(5ȇA=nxvx (+STߧJ?w~~>qⰺupǬYVlΥ\ujpڵQ֬yb=GXV-鎊99N}w]jW_}QoY/֔wf۷w^r?lH~PRYK\ 5nh˖~U3+.}IJõxV###ͨ ީ˃24>5a~B?W\X;i0טH)Wiʺ̰HlTn7V MV~n%GJwN%My36֍n(72>&GQQ;κayy~ ;m<3ӗ<ž$w}I=ϕƙ.=K}|fMA<41_'( !/sʅ{Id 8=Գh \_Tn )-YհkTr p+ 2#hj}/MA<41_'( !/sʅ{Idc\^&μ_^ 4=4K+/=ϕƹ8\nUh ᤑ0?AQP y+uP.4kLN X}O~*'J˭,óoMX~4|m?3ß N ՐPHɽ@J׾yc><@S(woij$4?M|A?Ҹ`̓3~45Ry\:ᤑ0?AQP y+uP.4kLN wiʺ?ES(0u]Ӡȟ[FSk\i\dqy3]>)T N ՐPHɽ@Rͩމ9nɽ+3"/MFi^S!9L5~4-;IS#pgh ՂF>kEA5~ Av`r19)կ7pL=?iM#^F&|Aɫi*s }+ nOqijL\sP-8ic&OPTC^ B k &%{tLK =ǚPVXȔd:0/&\CJ#hzЊkٷbi ՂF>kEA5~ Av`r19)F>찜t]Nm?)Mύ{f5~4B%4[ar34:ՂF>kEA5~ Av`r19)^kҔaFܴY*,iY-'ɡk\i-~{4[) C '|ׄ j \Ara^crR KagOSy}|;45mxIScx1\Bύغqc>㇊8ڢ++ܴOS][_rᤑ0?AQP y+uP.4kLN d8?I]E ijo_wƪa1EWa\C _{h*7~3<h u'<ׄ j \Ara^crR ?XO6&ݢ7Ih*ܔic+6tLXK ~ȯ{=KjO-NSYiJUvKTN ՐPHɽ@FӔ]Q;O^z#MMXaRc5j50bxyy9::=zc沣MpUjU844411j,p26 N ՐPHɽ@.o~wzpsijb?}{{|MSzyy9;;7U-WᨨTZzfnvXOS41_'( !/sʅ{I\"5DwhBG~+WFORy}|)_: P???/#k4U\m '&&fggCt bCSY]C!B ऑ0?AQP y+uP.4kLN RR]\BX.)=T_M&[$+s./~/KMMMLL22~E* WU\h~e4s"j'|ׄ j \Ara^crR #;ДQ./~OS4U+W^v'M\inued^.| ;\41_'( !/sʅ{I_4'ß&)bWgmraKv9@S41_'( !/sʅ{IBmz,ЅKJ (ȡʢќkM^ؒ)%{ISpWMfp|M(:(@L5&' <3(45=)=މwl5$'26Q)]?{:Me*,kܯ4@ऑ0?AQP y+uP.4kLN diWnД99\;鹱rﵥhLwv2I =Bᤑ0?AQP y+uP.4kLN dhMdEtuQ{׉ѿ9si̷O)OABFTQOXOS41_'( !/sʅ{IPNэ! )LphneD9ALɣ+D_7+DC>LwjcIS9\i F>kEA5~ Av`r19)0%y *xJRot2c?YLS{YALTub:S§N=;,/MAp|M(:(@L5&'M7V)[(^MMCuQ;liʖ~TEL@N=#+0~r˼?M%N) N ՐPHɽxdߊ=0uI)`sipyniZ;|~7[ HpkEA5~ Av`r19)0* NSu2ij8ʉvstiW`xh$9 '|ׄ j \Ara^crR`T4*KtW>&]iʮ !Nm,cξ7%:MSVTB}&~@p|M(:(@L5&'ֆ#Slhʊ i<ƱՐd3]^[q:(3rh ^oy?90wऑ0?AQP y+uP.4kLN MqZCs tNm, RhʴӯX\ɥt96M6..Br{4I#5a~B?W\X;i0ט)66)+|ypƏLzM45 s7s+.oJϤ)OUTB'^FS41_'( !/sʅ{I t~#uI)`B肟ooLh^45M6?NK~TF i*Ҝ|;Ep|M(:(@L5&'scД ܼL\ISvijsY>J}yҠCqkܯ4RI; '|ׄ j \Ara^crR`~ՊOpkMٕq.ĨK.X~: As,ghjӲ<ߙg1%q{S8=kaऑ0?AQP y+uP.4kLN MaKvT%vOxt7XnXB!6)t]2+Mxhdx׾ys~aऑ0?AQP y+uP.4kLN !š`}Rڕ4e $Z;0VݾH )@4*:=ijxNa(N ՐPHɽ@An[ M+h;NҔՑuKkQW[@*.)߇J '|ׄ j \Ara^crR ޢ0(SoҔ _<K0)SHQMpyc`zQCos<&]:fnm8%P@2ӝyRY6t8ic&OPTC^ B k & ]KTߚq6M )T˽?N;og#* M$/ˋԐJr]<ऑ0?AQP y+uP.4kLN 09[tRLבJkEA5~ Av`r19)PCr"Ҝyc|FpM兏s'3,ʵ_oTkuq}ӁY7;bS!Bv׾}[|H `{^-Mp|M(:(@L5&'u{.N[=M+C64}+̽D_i~ʭC\ͽ%;:wo}it @wv '|ׄ j \Ara^crR\"?R%8t{K٦1qc!<ݾkEA5~ Av`r19)`@ʅ%t@!{^g]vM4H0S} \z{l.XZy+ #ɫc2C#ߚ@SN ՐPHɽ䤀 Gm?ASȋIt*йٸpd$~< #vc[SۡǸ0dþs6#Ɇzuq 45?$p|M(:(@L5&'lȺvhinPVC Si(bTtE>y3,S8R~3MDS8gh 8FkEA5~ Av`r19)`U7VBxr!lϩ4emsq=ztuDl݌~޹5!34*,o:Dᤑ0?AQP y+uP.4kLN w/ɡ‡t@ n]4Uʿks}k4!8>zG?W\^MS}{x6LTp|M(:(@L5&', [=z=M?4U HsALc~~^^^QQQʾ41o:PK-MA*8ic&OPTC^ B k &d:u=,5j5PJͽ~ntQΡ[+44411@/u{i4t@<}vMA*8ic&OPTC^ B k &DkEA5~ Av`r19)`O3]>S۲u ҆ 7~yaW \9ջ^|ES} bsgIp|M(:(@L5&'L=M[֍hT!\^$'2B?ts.߸e4շSF$F>kEA5~ Av`r19)`RfXSۡ%ytijfJH¤4%t#~]c!TGvMSN ՐPHɽ䤀U^[s].%80lM~]ؒ7NSYh I#5a~B?W\X;i0טAcb#vc囹 [#|2 \9;/V~zu~S4iᤑ0?AQP y+uP.4kLN vfgJ%_5M_=<@S]fVZz?J_~YO~T̚e'ZRY;~ ?xw>7*оɀҜ|:I#5a~B?W\X;i0ט0S]AUIVyM1r⥊7lذܒ%K۾zntƍ233mllF9lذe˖=xVzjwwwZ]q2b?74x֪Uˬ\ݺu^z*&ޏ=yX n:& ۾_K͛v 6>}HAMsLϲnޥ<8}ٻLp|M(:(@L5&'l=vt}TיgGԈe\};oo۶uQQnCM4ᆎ9ͣ^|EK,Y|9w֭[V|СC5k=jƌ\.U_wthM{V<=thO>ΝKGGkEA5~ Av`r19)`F{XZyF{A3WL:v8e풒9s昙㏺ӧs7ڶmۿm>bĈ&MhLIIyu)V;wܴiSR~ȑ:;*&OW~5K\'sY3T~~="ͩ_sϵb?O[oϕS}iW_Rߊ '|ׄ j \Ara^crRלH_HRA+~^-+&e7xm۶Qmgn9Fttѣu̝; /\@5jԨzꕖVĞMIUŻ!q*KM6n+Bܢŋ'?|unCF8wg۩>?0qFdaW|{WtO?φdg_ lYتUsnQ^y=p%w3>Lf\wGiKh7N6hP;dH'sNcN׬ +v[v}{Bny&mڴ^^]s%HO޽$6 7 I#5a~B?W\X;i0ט+۱FԈvͽ r.}ӵӧO޲e ڵ ]\\{k\]]_=JիWo)'s\{i3Fϙ3nqrw6Z[[C׮/תUwЛov~IIgg۹s)wҥCosϵ:p`v'ovZԁ{pҵ0(Q/ʲ7/;L:gn۶h ڵk{5lؠmKNѾaY{>}TWkqs9ugmXɷ37Y3ٳƎĽBn9C\իןr/g7!s%}ի/nU49ऑ0?AQP y+uP.4kLN LA~|chZ .LJQ*Lpxfy+a33D?֮]/ϟ?_QF6mh۽{ǎ=#kC}ہ_>tvcXh4Zj5@PU-|pmcffz=ի\jկ_?{{ЊϋCڣΚe5lX_js}|qٶm]t|+iVϭxqޙ3֩S;3ӷ'嵇-sGƍ^|cWx}b^x,-{_p@wac3{vZs%U]Wߎ?o=tw{ϕ޵ow%M֠p|M(:(@L5&'&BUP)F ܭ GBmQtIHiiiM4ҍj_(q333{{gѢEu֍(#~nt+HL4?)gh~[l`ݻb?}w{Ww^k9JگU~32.TsϞ=~=˵}:rdOuwxqƮ]_6+Uv&6D?Wz.o{z\Fᤑ0?AQP y+uP.4kLN Lg=BV#z)̳Ǘ45Jg&ycǎcƌ ;zhΝ[jufׯ޼ysZ6nܘ_XXo߾ |oU\?W֭si١C5\_ܘGˏyne^]j`G*ֵ_G{*9H{W[l'ɭW.ڃ|+Ԅ tVUg:|*ssr.կ_OQo\ϕ{׉4ՓЅ9@S N ՐPHɽx}}N452bJ 耑ѨvKrIBnt2s̴].CIDATbg6==gԩSAرܼN بT*7֪UEܽngwy W_qu>dH/9㴏zl?; ۬5-ڡ3F?mki ԯ~]nێKqO^^]k[jfݺu-''TNK\n{a;aI?]\sYvպ35Uv1`@7~3I4k҃ndF>kEA5~ Av`r19)0)!VG̹hjdbMkFVGF?{D AT( "p@P)P(K{PR(-ޛA{ oߓ$Mr~݋Wf}%>..Jwv ӥCwY֮9iQBzsr\ǎJ\W_>}zkӣH{oY[#.]]:bWDWOL9h|3@f֫ x>? \cyҥɤHرc#ODv!O>۾}{r_ 8#y8i?w}|n&I:w~ڵ/[`TMnLդ25ǪXE(P?A`5؅\܅!fPQ9)7ˏDn;S-`_66k3ҥ#0vaݥ͊a))_ۂ̇\S``IOw *+AXZ#}LgנJ6Td z؆+;B@lÑ%'ׇ`?W ~jx"p~"N,kB`s:p;AF 'me9fLL}I׭aJsqQp).e鷴`?WR xOܚfSiE}MR~\Xu3֨⡸Sw'l6+*xmA;$s5]?V s+]:4ԑr8؃; T V]ϕ ]bB<$pz?&gk[UW[PbEW"ꎳ`?? s;0Ul?0EFYׄ *.ʅu.\w1mI!~ʶh^FȪc La5baX; T V]ϕ ]bBU.:&h,TkxOܚaS~.KQpW `I})b4ʢ&OP)X va?W.wCTnkTN ك->SaK29 -s`?W]Y-ma)b4ʢ&OP)X va?W.wCTnkTN 񙨢EAp@;#Z:3ߪLY.aNƟ3:ZoO(P?A`5؅\܅!fPQ9)s)\/y"-p4dQjL+//M0g~l80UY]qk#jb4ʢ&OP)X va?W.wCTnkTN D:dY-[kxOfT D+0g~zH$+.R8LFYׄ *.ʅu.\w1mI!TȺ~}Y%`D,[_ 5vc7LDύ;s*Ɩl; &TgJaSpQ5~Jj ra B̠r[rRGn;S-ॿ)ltS]S>sqς\eXL SELpQ5~Jj ra B̠r[rR5yłnzqpmDl> SVUd[=DŽB@ 0666K&o!6U.1!뗬e#"",c6dTe῞~tH;N,kB`s:p;AF׋ҶŠ;WK`4ʢ&OP)X va?W.wCTnkTN &9.AvLV q){m`e ⵑxd-; T V]ϕ ]bBE% V=՗6Xtex$C#I׬`$]M-{O#/0UM0EZweQ_'+ցp! *5*'2qM0e/ aܢ.L$2|08Ԫ<> 0UD :*= ; T V]ϕ ]bBHj {WdÔ})OH)⇪ΞjcliT"@^ʣ0UMkKa4ʢ&OP)X va?W.wCTnkTN !urv19+W_ZaisG,*JtlL,IupV^0UMށ)V(P?A`5؅\܅!fPQ9)% ]| zy0ռ|p2w"(taL9.rE/MW MWYtYTY T 5Xuc pQ5~Jj ra B̠r[rR)(u/}Qy`Jit}i0ռ kNF_c.QpGG RYG[a K`4ʢ&OP)X va?W.wCTnkTN !-="L#./;xESi\g)Ug[j7;T5ˏĝ1)b(P?A`5؅\܅!fPQ9)R[$:q`/TÜ^X SX:S*ټ3OF%]t{qVepQ5~Jj ra B̠r[rR)+;j(= SM_PUoĞa +Ue{`l?i;3pQ5~Jj ra B̠r[rR)K\WoZs `Dqhgs`I'hLDs&Li!*='6[eS.:pHN,kB`s:p;AFjLkoOHp@ȃZ6ɏuL3}R$lz@JJtSo%ŵuobHE(P?A`5؅\܅!fPQ9)Cwa3`1/>:jS' Ô.7?:nS~Lȵ0EZweQ_'+ցp! *5*'P۔F%ZW_R4Q-`1c6dmL )ٍ߸ 2SMߙtHN,kB`s:p;AFjUǘ6)tfL5ƪ|">q:CM:-pU$D"*/_ΐpQ5~Jj ra B̠r[rRYM^^yB:0q]y*859c`xyЂ8RƦ€p)&j?T5ɷl})iE}MR~\Xu3֨B=i3yL55َ0ՀQaxƮԊ (˷%b$۾D\5' pQ5~Jj ra B̠r[rRB\Wooj0h}WaV5p8GM6\(//?zYFxC%''KSRRrɓ'ڵ+;;Ǐ믿~$N>A.ZBn~ر?={5 ' Lnnna+`%%0UMuvjiE}MR~\Xu3֨B*ʰpw|. C~\S /;e?>66O>چ N:bŊzKrMHH۷oϞ=o߾{{қOGGٹ?Ǐ /W_}UzHKK#wtgAҶm۷]I/{n{5]Mz+*U~3~Oi Sĝ1Zz#pQ5~Jj ra B̠r[rR}꤫PUcZ8n_/, )Ⓠ: zzz;wb\0aj:V7??wރ +4J-Z~{hdɒ!m8L|5yV=ƪy|">ZN,kB`s:p;AFR]ID[ev:d;|Z5EV=&$$pɓD24u HiS~ ?6s[sKʭz++%Q0U y]3? .FYׄ *.ʅu.\w1mI!GnSMzaVY6N)t踸'OCW\iّO6Uϟ??cƌ7~_CܡCxJ-4Ia6t96"4<> 檉|OweQ_'+ցp! *5*'Z Hʄe1j=LՊ<؍0E5o޼=)[AO~ Ըn۶MgNj׮݉'еk^xᅍ=YGnlAAA+{7o&n``PYYIe˖5pڴiʒ JtSU"6U{˻&u]&FÝFYׄ *.ʅu.\w1mI!NfBkF-0U?חLyX|Ν;YSuqB|ܩƯ5㭘7)ID }&E{9~6pQ5~Jj ra B̠r[rR]٣zuL dф 0U6)G `'Ct2s_ SpQ5~Jj ra B̠r[rRiB[&J5rP睟 ^q4Lڕn SyJȪc07A |a2uc4iE}MR~\Xu3֨BP[$:2/eҜ`n?yݘS+,|stuv\`ǁTD9{` weQ_'+ցp! *5*'?kL5I 0Uj J`uȲB(OړLiÊ;0U{Dl=S}(P?A`5؅\܅!fPQ9)A0U|0´MO3v)⥤Gao:"ze8~:GC4(LN,kB`s:p;AFbLCU;J&uh3u8y8<˱Nc'e9}9844ʶPh"*N,kB`s:p;AFbRe [`&n?sz/)⥇{7d-9tU# Dp"8TfׁEn^I*8nfBahd;2 T V]ϕ ]bBH Ug6՛:\TQ ZUe$"IqgOy%)f>fXzjO˲;e{M["N,kB`s:p;AF/m%2-s_ S2i?7D" EK 3\f=J)g=y.j@]aE]FweQ_'+ցp! *5*'+,tSKôU]tHeels^ 9潹WSsf8ckCa,eA$ڡ yzf/~nK-M\CC „>TֆSkA7ʔl8Q.Gn;SD/iE}MR~\Xu3֨BZE膬

LKER&LE9nmb?~nCuõ<*4]/IhDbޤҨD#zN,kB`s:p;AF6:D7^ﬗH^Q\[g~.Wy6%%NC@?7O*rУؖHfd~j(P?A`5؅\܅!fPQ9)ѭH̰=rYu׃8%KӖۖ t@?7h 0҇d{Jυ9z};LpQ5~Jj ra B̠r[rRiCtzJˁiKCc-ut.s[4[9:pD6p@cb?!7 4ʢ&OP)X va?W.wCTnkTN !-T_Ct}HӖdZ{{O Sp+b\Msl}WÔk\SM ^v8v(P?A`5؅\܅!fPQ9)Z΢w8tI$uێV?WFKW~nE_dijCCCh_caqZ6ot?7`Ǘ`)E1V=Vg1wqeJͻpnnu(P?A`5؅\܅!fPQ9)S]q団<[7Lraߜa-MGGXeJv}Y%:9]16>&;$]0Ms߃j1l}H^ʨ,J$(loɒ%ׯ'+رcM˗/'jkk{9bi>a„Ν;'''KݻA$I[kҫ.)uG%L?N,kB`s:p;AFf[χo< Se\4g]v2[j1wǫ?۸qfem ڷo|M>d7WR_,qc~L7Emz©&8=ζH[tfgp_V-pR%bf7jed))72W_}0;۵}[̗ZzYWsp\t&7<|ւ;̙3Vz(/{cO? 7뭮ii ?#rrg4]i^XѮ])S~nUOGi)h[jclCMeǏ'3ʒ)ܸqV$^nhX_[3ۚVs߃:xp-zFś^=ڗx{x\#W>{>=[5->J+OEC܀{5iK+VM3g Ǐ/ =<<Ȍ8y$4w4~!!-,,"""_͊㬺U&7SWlclID<@!iw07kB`] \IQaZ7j 6NĹ)BH;UTÔCcopzI# !UTtC-`<~q ZzxѹuS^O~/zڐݹ3Ks4o6+~nDRƏQj_Zg2rUQ[ o=eG.]݄BpppDDD]VZSS(ۅˣG?QW;r29s^[^M^cor`*mbjz/?^śY3{I=MC|3O^\Gi).>L9œ~naaaΝgΜ4*=ݻwug咒N: :VĶm:t萘VjAC?f4;)⓪s)?I*/@Uer^}Y%PLu`'nͲQs++މZd^NB24lX!C&Mg\mjΛ7^/($qnfH✜_,:7~7Wei޺ ^;UwUM6Gi]#&gobz_@ ppp m壏Nr!+˥]vhywܰat(#éCX҅<ʼy=׾C_I׮]ȅƳ.^Qǥ4EÔ.Q>buӪ4<!M qC*,6)0_vdXT-sd:O}yrZ/kgݺ>>{s^'zݺ5^^乊\y6dT1}[u-b:nѼ-7}[!?޾}Ν;&&&mZY&.NvТE{ΝK4xJc矿ҥӉ_קO/߇ּ!+ⰵkg؜OLҽ~қwȑII]IG_G.\rw֬Ƴ'[Z}O[ޣ-j<=Q̹RMg}zM٢z>n<ŋ:uz^z M;lTU`s\aC8x\)ŝL)t*hIyѓs1~n S`j "-! \?Fw;ZZqh,BH5Io759iEރ%lK~tސ+({kjk;B]D+,xOzT_/d1ӳMΝ;皚x啗^^=*@:T^t#C;vh:uԨC~!,m߿Z=$Iί\[>B{hO>۾}{|ʺo]vg5=ױcP׻>m=JOIޢ~ޯXTbq|||\\\S'W;5A7=?W*=F9| H>Z s~snIO$L)9fCL*`60E jRpJR "/v0?  'I#0]y|ڣcn?ywRʣd' ).އb'li.Ğ4Zyi~\-îLLLܹcdddhhjJ64l##MڠǏmȿC-. v!C\Z|JLIӑ$/)񖽟2OSd}S]u 7U\/Zb5~y8J=q+L,6Ŷd?#[[$'$tg(eW \uݺ283v1f}FeR?BHs*zڂC{ª؍/1ѧ(8Im.cR$z-`Ww|)bV 'OAƝS|61LT$e:}1/LBjn0q(=4byƘ,dW׶|.BP&DX؆S0HOÀYRR&=s o0/iQ,&g6YJp? >`^R4oG&!Kɷl=~鳐]6Li3GYLt\ 5_lV=ƒmh5mܫ9!lI;[#e!/&27sb#$\O#ڣ~`jID<OK]_ޤ#WA%Z1cG |#E%u].ǥ+!'z;ÕrsaS3o r sYT禛 mLWx&eZ{usd'kdty Շ) vd&o[aR\/VyF?( ydojeJ6~YL2@UV?\8le9΁xsTPSewK_nWM,hע^O1.YGt k oQE^Hi w'/;fj ;}.>馮0O̡Q Spyɓ) -s 8wLӪ&e;Y[ $߰qb^Qܯ€gg;dZy9~6$;Dp\$[lmA {o:SDҨĈ-眿^HmÇ1BT{G9עvt}j"Ҷdb(~e{\b`ƓU$]7~ !D)AMOُWǞ4je-L;e;L! !'L6yd YYYpb*~.c*\;7ryj=E{Oё9.AVg3pdu{fRI$[M߉AB\"%]UZy/F?%]Dۅ6 Y^#mxk=:nHn\PE'ʐe|w[j*SkE {.: ,[# 5^H$]ȇ[Ԣޯ/uD|2y; VDr$gTa B>'L6yd sΜu7E )r6w@\W[SKk乇Zuc;xK\[Gպ|Ϛ _C!51ZTe 9|? i3Kpo:-h? o?7DĖs0 WtZLqڶdvmzO$o[iԪ=d&0cԹW%Q#:}>׺>1}o~{Wq:t /ga᎟/@H{Tds xE'/&'n>Lף:/bYǁz 7-C 5yŖo"j_.])ޞe%]8}9Gsh,;߸SwwzTFvG~&qF׬{܆.e?sG5H3v&ϖSj%߲ql/yV? B*3&/]-{_XIgyoxmd^ ׋ 'T#2a;} Z|)R!7O1-Jwuʝud)7mDfT v*h!|[ƺ&t O>P]}8W=Y?]&?= LKHnRH)=0(~BdRn69tha^vJqDi:dY~&JQeuo^CzL[R~fq;m&&;CNX |"ϙ.:\G./ յe1?KfdeM"A1 w8{–cvN0w/S_%€zh E!/ZiܢT#z?-tL+/>̑W!{̴0wsON!!RmߛoՊ6N#x"I)E"yYϭCp3{̨?QmWҨ, Nu-:\7?{D&ԗV+N")'BVӅWGv!҃3W J< F CdO Y}Qeߌ]_J6 KH}{E矅? t&CiT"WyLg{῞&>KhW |lr;ƿ]::mvEEEEQ(*n(*n++!a=aF'SzuB8>>xG9y3Yvbx|Wa# tmr53JǢ^.)BwGl;mIE'Cjחn;@[@P3PϞ35I//f}4rYL {/D|x>Ў) rˑ}hxPp(Gccn#tS͝:sӣ4էYW \z!ؘ]&Yw:hg4"[CO{7Aixr8jU@{.cH9k^j.}T"9+-vLᾯ1n]ik,}6E),-ͰNC'or7m۞\~[;kt)AOiT3j+[-;$0Yx9Bl ҆A0rJΤa 4KXP}h*N1c#Fi8j D^~zm;R {)e6'վ4@;&Éَ@TWޞՠt, bvݳ-1 *v7}XYIn"I w:!qi^zt^thZ&PWLm ]>t@-;1>lө), ?g-Ywm.罱>Swh ζA~qQPt,Ǽ&鑺o}2hNÄ 6߲/c.?cp{˶o sRt7wK5WYR[vRs 9]@teJm.{"P]a-{/,,#tkǧ4u啒vN}J>E JvO6ly%nmYM~+TjMae2#eAqsZ^CA-R1JG% T<6(.q?́Ȳ~ҫ5%aqQAе}_u貐L-D!T [s ,\b ^y_W˺އ]/+\ES0;%˶LDWt}qg=%-պB<QI0z8T~; |$,[A9~3rBy4648~; ^(xPPyoYP?/`PC]~[QJT ?^axG*Yk,^+Lߔ>fA@IJ+^Ƭΰ5gM46‹&`~:52H}vFdSF #^c?V< \| rnMgj dC"f[5}p|[[$]-b QSv%M&΋ꍿy]U{O{y>1BN&]#J-Q )d'œ715g'`vpCaA9|0a6L `Db}HJ6`[#)N@-@KS wQ:; ~?oogsWr箲**~?;yU@MV)wV'^඄m:E)e(.9nyjUvAK \W6\|n0w*VEYLJ<+0H "pVyy wH>\, OH+!o }f>D"L.{V)zp =@jG8S>ĆFjӶp4:17`L:|+H!^KtdR͝>! UA842}E+yhJ㥮QۆƆPs$p\s`4ֱ߃B 57<'o|e1pv{H \r!{{)K&_X,&}a#Gڪf*zвCtգQ"5qcu{Z):O#?g( pC7HŦwV`^\᧽;_>LQ""ˀQtHz @%-\֟yo,"'/Yw-ax򦔛Jy)J vS/ .>D NF./9z. ]/?co;k;7 z7F8~3cܺe·cd1Npy}UZaN п<>PZPYܩ{>3q|řtu![~t=ԢR:)Gd}F۞@ѭH yj<]AMԁ[ue; Zsʈ}JvX+̀VnPasjjsW ~S^a}Ү*K#_ia#RoweABHa 5 l\.RKt+4)\tl wUfAvNu=}nl3Ʈq7ࡺ6H(Mʢ|BiJ#/q8|9c-ph "`OU"YpZ>]H2Q(IcCl'?G\:c}<LC^ C#m+jP~svUe泏nÖ;`t4d&wjI֟uVl~={qBVS,TA:0C)[Ay,,,:Y{ ]#"h ҚTxKS c܄ODj ]1v '˝iRtS]l%.I敥0 SUn3mQ+wri>UIacniK;VϣP 2ԽF^B8dż'liSwҩ6GK=2#/>uImk KgP: ><=Y)i`BicjKF$f9$\y!`h4IaW wogo@uR %^*Iz~] dWF^&]Q!keV>U(MI8o3Ӏ~fzM1Gy>F&߰g;qivNe}KtҭxMj5s+Ru >Jn]Cm]mӏο,Hth/\u0 ^a@3)WKUVAa@T(Խͧ}gp[]Usjwa{O2¶8ϫSLYL< fR(%ċE18]\c? `zMҳyGC{}ZF}2 )B==I}uͻMJG$=,҈XAAII^jчoRJ]]y.$čÔvOӪ{?C QTwvˠ%;Q-ߜIIW~sw1w˶9쓔:0qSn9 p3y^A ̇a#ǧהI6Du*>ybsF]XDPǧyHu:ꥮ'a.罱xcunV yw㷳=TBy,/"j?)t-Ntt*r #3lDg,"Ǔ^neXnqnV]F2{uېGntRi1DJqe5RbXxZn>3ş*JbGL4 ~bhF2 P%P=J;*eВ%Jh_'F1kKrW \|(8}Ҟ|>h!f櫎siL E0q\,n;዆5}WDW9 -zWHc a{܆-N@txnsj*28"]~[$b ϖt(%,:950Cԁ <ƭs>_lmcmV3s;dyS*3?ӐYjs^ \e/\G/gkݕt) dOI>$`J{ttjƆ(Ẏ̌?gIn j9זR^ƺظLv}qئSRXP,řZjb*fpdj]ye_Kѫ0Hd/Ca@T[^7  6-< yPxnsHdq]4 mF(=0#1.D5mrD{'j0G]~[䭡/Yk 2#JG* /4}_U4fuI%xHNnc/hF Arg?:L>^~ٰ\R(4jYmNm8S 4Gy56dφ:XqPUW%U5)팚2Xϻ}8s:XJ|jKʳ|"v^^eTi[b.*ktKWW7PGӼR i Wonp9)QxnsdpDaN^"O/ i= 3"%;_n?$|yy{ >װy{pK{Mvg( 9^h"v];u/ CRBKR:459~sw9׶2Kbj`a_(B1񛙤l }@2>D +?=fz'{ԕWryJz/ho|,&}m)0KEczs~)/ Lb,uvr 筩Cw#SZA%_>U) C"$F#)R8b23Yvb%0 [|OI^LyJK2 9rS SQ+}&{M,GYWem>M4}4 d&w |ßR;!չEm:2h LF)4Ki:KR<ǯw7׶ vX)O\cIcuO _@ч€(`CM-(LWc[1B8`t5b EAPjlQqeAi!xMҋ1M#`ByuyH>Ǭ%k<;aN>POՇ҂ɘ9q-\-V(.gPUNK]{-RUMIX\˖f1=mݧSuBVa[ڲ\X;>_ibLtC(1Y`- וn;ɧyʔsPS[2`>s}|vqӂ TfAIa ;>Ö |m*&oc_-/?bckCep&yw#*u+n+F*!Kwu fJhմU{@>P J{05@֧<ἵo`Dڂu0s /r<Ʈv'=θWE]yeW(8|^/gL+6 S[z44\|OVP/Tz R~q-ŗ%^?cY_8}>vnvS)S^!4˦~Uo85L @55# HbMP(O\!I-0n;nhW3uy:&d$}U8|ct Ė˗P89gn4됥+`גގf&x($b%6@4 /z/ 6nÖ?6NX“}@p|JRW _ݶs\w7=}‹RU]eE콩m{jKs݂`j5I/bLW[ʃlGߘ|fT~wAqsqUTEğlx|1G|_Etw=vg65a%Jfp=:\*E6GDhfKj ]fKBV(k 464TdGfى] ^amY>3rޝ5Gpmm{{-d&wIW̓{0 io.FL)(-.'?́ pS6ʳC |1|T;k"za틂 By%*OƝGzd:Ղ5q#QY;|o歱}.^Ҋigh*(A)R}s] " xJ Tr[ v{I,aN%^`T*PYvPŸټ;Z>yqH,͈O̰v?!}@v }@^-Bi 3vrF}uޏ>tTZ32 /Dq / xN)UP[QzOjRxI.0}t}J@q- ut|G5Ðb: Xϑ݂2`bV/pRh"0VQthiu ;n㷳 Α4}՚4Bc< tCu-xn9ci=?.m32|bƀ%lscܮ[%x{PWQUpYR eZEQ8ʾB2l=I^_k XTFh&(xnS_]=f󂏦9 X=v;QHKGQ(6 7d'cxRm׆|ju<lƓ'BN.?h*X<82abx)ybJ5?I?8XKTQڒrw;44ϺOxNXudY*$cs;{|'uߢJUd&w<>cݶ] αR(F}3Yc-JU_Els/vAAIm=n0*b*KcDi!C!N3 G\~[(e.( bL1Gnfى[_;421 j{Vof}:=ȶRtV'C7@.AXn0I/"0%sۂƆV+Zp×3&AğzyӊBP 2o@`21_UߏU!`ѾPSOo?npNjpםU*bV~2WYg^\r8!rYMRޗ!S>#`@b4ƙZ+s ׆ |Zk`eF6C7#F59I~ڻ.>v?PeKnA9k?&{/:cAt<((ak!H""El_uX;W= eh/ OH+ 7xf鶳VnQI :U%2Ri?PÍLtJRe'=vo.#WLJ8o]Reap1` c1M (J 2 yc#:x Y,&אd'3m=K#WR(JR_!ItMf | 8^zLؗD HGuBNm:<&Y"pRxzX8ٶ&bZ}+ -Rn:d;19c:19Ɔhj~ou7G:3cۤ˶^p)'慿!dOb '߰=g^99C>/֣73%2^ E).\zslG_~BFnu r,i\퇮w772قǧgHwPm6oe e'u +_aLچOR%kZJwxcpum/5_pB*}AgA~1& ]#8 *P4M xn8$+ X`IyMrO{w IWxnA3[BP^!0+Ra9.)7YGDuf&AL9a]ڻIw pGuRlLa89o.&75]+ gO”j6$ =YLIDAT?PyGcw1$`?URx匠eR͝jhl#?F['F6^ 們ūDE(^ө^&XW_-kux~me}L@#d;L†qu[}|fl})ڽ4*O: jP448ɽMu ]Sstd0Igp=b܄1 ƶwԫ?}s@Ec+}& ঑eMQ:'4)I?goiYfh*0|v[y|-0P(S\!pOčw%|y4gnL3  fWwWY5qZoRb #VZ,8i8?C%NuJ/5I/opo/gP2ǠS[VQ,g2 _8um\Ts2VUhkۇ;3i9]r}1k'(ђ V(9Nf[Ɔh(praNeى[;ܙa,vutD.YwByQ gՃ~XDGoxKDOp ˍڒ SnIY<4ҥ jB7$VsWkӵƞ֓o DLL1~Pɛմ6H{w~cP,ReIviLƬ?)CU4 ByVhG_@WR(NL`ċ]·Zr[chj+}Hk&qgea7ce"F 8}rHclӷ2iؒ`4L!c|e ' 4{-vP$f q3^ƺB7<ǯyGZXL kvr mGSt+xN(/3&됥/S2#/ߜ]n?/}7Fy@]y%ċt m {ܼ8$}q4ԖF&f؈\)0j B̄1!J,/ ᥮G煑yadv:p ;mpZ$/l֖Ud{m>3)5譢@ep= FڼЍ&Vn>v /yp1$$lDQ,J[gj⭡O p됥D캘|O zV{P>x-haeO`F19Je_ꚲ} ; e L`HI 91!\JdrIH&W]!\ꇤA}޺8Nb'ᄰMp2 hxN~$Nx( Ea /B[*BAA #HD2˟5/ Nj/qI]`&e+]r>^I5%J쫵$&'ɖ:^^Q{d'`Bҥϒv47& _){aa iydi-IvB޶t46zE|~t84>PðiBQp?)SjQE`d&].VޝxS>UTA$74lsSnAISK.y%=nq}Z Ԭpʸ@ ]].}4͑qeťzo=rY}*IɦqۈݗpOL0xH&Dο.0 |:e$<3 E\D~a7v%9MF~7l](1a:A b4/)L^5#W(M='{4X+慀 t?)Dւ&}U)L$:bYTs'\t+78ӷ:~7PU7{? ݼ5ϗHub}2%-H?3 Ѝ&P@Qp &WئS>yٙx맽VݧSu Y=Hb[5uPi.PcC[ͧIm/5&4roa@TMQb墾,:9[xnpo.hI𵦇ZZ;i.icj;7JS4f5yjxma{w>|K]su NVY$& Biox.R̬ ~;BßojTOߊG[1 .CZclIBPچoXB,3K2 (*p#yRѦj/#v^u73 t>@db&Kfr7d5-=)1t lIPrە'dىX"^j]F@+VR I[jKC2kmVO2C CE<ԤS0 *FwBօp]4Ȩv: ncRFBhx.Tf)BL 0SeRfod=(+fluǙk.ؤ;3I$ BQzj K!'AlrL‹RT7|0V4 i^jLPً=b2|p;u/|L͑nxSg˶Y>xmچo.h!x):p5Oξ@\ԻB҂kM^|'ts-V2"v_T۠זǥ>PKQP(' Q)tjjA`"caeŘ6oHm3o_U#itHIz{ 5,g#Xˑ{hd2i5I0nK,:ًw0IZS̑!kS6 羯a楮XS43 5Je)׉gdH1:(jy%HйJCez. rDq[w3ks] 78Z'KFPhrY2Fqą~ڻ|w}d LrY*lANN1GǼ>TK2'b煤+\gL[3$r.a>ʔ|u {B/BWSXJ|JAFɒn6j%s4S(7uUPy㓬;sC)h;j%\9 LNLPϥP:0,J(l9L07bE&(Dl_k2Q`&B\ )4lT-R 06hBPu؛"32^^ޒYxbQZ:I˳6\W*Yd@o }26"?5,=8ԽEI .#? X`=0%KKapig2)yK;4UW[{PQP ^ykTUv}Ufe=~cx)JL8w.wF wXGLKܒُY,;a;{/s 77/W=(MvÝ NA gT]F@<'o;.;G'xܒԖ/N&ɮ# /=v73;)pGtX%\ np0MwCA1Q]| rYmoiDҧ?k>RVUԲ׽2{e]*-bB<34KtRJL)ABF@3ˠ%L ͻb'_[q>ŋHUx[;2EYlϸBP^VEAL |(yS{/##e¾i8+>*@fN[m:3c^};*N`X;閮EQj+$]n0q mtNk'/ۼlF7IBymhs EoOs88ÞRƆ<_n]FX>{&_Mkʲ`4prfɑ;zG@*Vwc4:x ÉLr* HۂoBe9@V3+9H٤f#dE1Kl#/NXYx29aQ( 蚦IOyB@6El lՄŸR'JE^@_$)/[~- Bi hrţu -xG6Egܘ~/)z2JAbrLR^^ Y':dxfʰͧ.ɼvy=%UY9Yvbx> Rk勫sJZ፸9~7ofҡ+6Gj!ot$3?=m+x)ڒչ=Ƌ'oy!O> X"c]UestSVo X`(3Ё$MaiqH,Kfr7tp|{cI/B{nA2j/q^ΓG}[Lwtru y{ܜ1' HvKcI- h!k!a+0E6'^K'H㫲۳ kC+1mEt|flsF c,դ|6&ݡ?Bx.Bt F$>dmFWbn>}Ƥ<_oŸ=~I{i)j7_͓ h,KdN~),ː5ƊuVg"Z;{dms@IX\;UTU0I>) -1ձdJ ]Ɣ4ƒɮ-KSv՘6"/E㞐&dӏD-0βVZ By4KP( ILaDž~L8t Y} 7Bhin!U9]r2$jIZuaek}gh?Ym/ 6β~Rש\ ^j/(s;?C]-ȣ ο.!;,i<QW^YpZornw Ң_u mCOln1.Cl<.F$ {M_XWĉy! FfIxB{М|Z7uem{Nhj9htY ;Sqiԧ-iZ|ix3kC&_D?s}G hz|{bHӳ?d ͐h@ar,v% b.>_q3XV>v,,v ς!fޤ]E9{oWԆc܅]1h)[),QAp[8@ݸΗcm\W]u ]ڝx y.auܺ!.__e58HGѱoJfs(s{؊}Ϸ){Ԙ2<*}9n%eA0p'R%F{)R.OՑbޜ?}Kn6E g]u͝+3q߅y?^Det~s] F]V]W0rK [okc1[t jsVUsKCrX#\?_=9[>8{GĚC,rcۼCwӯ7Y I X^gƱ#~Tl]-e-z~cA<} rc4os醰3A VY "f}W_5s cҸVxhusTFo8oUQ- }zG–r湵ѩWbMhdc-\wIx~meAq~HByǘzӷ7Gi(Ww ڑNhK<P9Aq׭,cwJ( R[=>ºhwfYgl Roݳ*r 2oD^x\>eyπ[WeL|U_to'yϢDV)J=ٯXP~ V>V~E7r[K\5җKi[~Y;{[N/Sp.iwPssmVVE'5pyoe! jjTt[>(ޒ6sjk{fns7ײpٙ {^rO2_u@>rc<>[פFְܲlm'<->w҆6C 3nzroWlQ.1|~P>/U:iu嬌|p𷻹y+BN}tleQMJ=e]M>lʭq^{<`w}yMIqde[4a,v'.. Oޔ^EBL(k/u߭mʯ? YyZc!WSV6p [༵\zr<,,,---//?%f\N\η23:iu'>VAL,flӛ3}'mII{mrk ?_ 7Jx),VZhl ^k2'FZwͪ|7vTItɇL܉OWgi\ߨѫk_ܔהUqu5\rS, [?UADrm֍D{s---%.NoΠz}Lik9ٮA:}Ƭ{~]𷻓e9]0sM\=l};bw0t m&B.)4-4flM>b]~3eU3W*V=[c;l9[jATJYVkv[­9.ʠVi(n}7S𿯮[ Je\p䖎z[:,t5|͊v}4l-Kþ[7\֍gU69&٥dv|^>leOrE&\r\(6m y.Zk<7kCtrChsL&|bTcTs#}S$\..G/ggpЪ(&_>r%2I>lD7.1q- +nis~P\3-SSVAgI?c, ?x/W>+/`]^V \FS+J* So)f:$5ޤE(dDI#<-e(wL\5f-GKCDp=~6UBã:_\>,dU<,n(ʞŖfPũ׸V-ݖ-GqE3͜ŲaI0s^2<pgǷ$ƒ1Vs_mV(yyy?y4>5[[}0~I;szcp&=7Ѻh@?_ywVQWQ¾3iQԳ ":^aU{Y9OjzbUW_~җ?*ە+e?/X;h4."Q.UW~?} 9ʒt|> ߣtHatCvAS^J3oLno)K*Yߐ ͙,Ԧ)Yug^-Dž 壱ݽrF--*is ^1c,hL0}\|6+5}COcvciT.#y.Zk[Ea;L-b͡@V>{^*X'knry55)Yz;G|F -<@C[zl8ʑ.hZWYv5reQ[]UX|7+_)k?m3+jWթn>p[o#{n1Ҥտa{KYU>CdOc.=`Vov/Lm+|ƬP-sEӨShꄧQQv#^V**lc)$ϝCtRvdȒ%>cױ;?Weku*G{qQ%na,E|S |X'?gFq-vvTgiæl,, Լ. y.ZS U[ N&.h)ǎp9]A乏 <|PӢ<qk-<[_8{Gꃱ;Nz6v^ Zic<>[æ7|].-U!0rJ9f];"-u`e|JoeS`\_1߼M;2Yi%̆_ӄCr7e5pXחݠ6嗑f;skfٳo@ذ%\fNӕ`!T/K]كXc"-`&9nBC]to5j(ͼ)sԚm>y#G~;yn!ڭ7-DsG:$$aAڡ Kh.r;q4Q?iq+XuYIr;"([KE P=,䟂,LBL.;t>r̂g.VU++pkIXXcbNcj_jƯeKbsXeMvLJ jMZkԭ0>|fnݞ0gwa;vСNcs-zU.6`\ޛLֶۧ':g3\ޛ*䑭gԊp,ni.{\$MWr T.WRUl2g MzK3 Fo{U&\0PQY_cl15~sfG;{=; {8~bU)sZj봦Nݚn ܡC;=?@S.s-UІJ4ѷCLB-pv7d^fl f8.nX먬qxu*tzsf ?'j^4=Kkx'ew)?dհUs7>oeXS]&\UP+/zkxUWEk"w@= PkMUU MI;1?S݇/ 2 R>.Ix?Rp~W544F6MFF 71SSEgׁ++89orKUj{B|Mǣ7RFmЦIFC-o8)}+ה%z8g'? SKÐ[ X+NS*7Q<@Is׌wz3)ScyuՂ ~ǩ͜εk=J|Zq憂^/I98n%5˱xFJ;v;ӹdWVܲM sikhh];/`d]mKiOEE-e.| |z['͛PT/>c3Ο;q=?3?NkoѧcG>}y޽ x'=ݑBVgz VWG={Vvޕܯ NϊT]Ɗ-H: ~bDs7X0Vo6&++C&uo՘(IǬ%WX-_sZyne;ohu>ynlAcѢ).ޤpϿ663g/8ħ{VU(57\V.N?/j77I}H;yjc/O_tSSu.\5"ČLkskj"tO<=F-ea j_̸XWS+zC sihh7oŋh;2ri20 @ӧ''OrQcHbQno*J)kUe, CтUngDoܙieX'/sڋQאFE]dsr<555g^taƌ/K⋽]se/|wȐ#[r-dCý͝_C;ߥKg/qBFmsM31>tڛ=& :^Mǎnee!&&];g }5{Dӧsz33VT⋽]aoWklllii)#.NޠsXw<-躍w )/7j<ן(L㳀pa {ydzP|?6Y{Ac+l! glp\˾F\. 1;90qvɚ8ύ u$un~ϧ%-fl\P:x3ݪGW7.y.Zk4ϥzԨW444:txmmg>n'|ȥPevkaSnr\tkXA3.ho&`ddtA466611155533H$3%//}Wדzzۇ@׸wj {6y=nΞZfkwܹӾ}\q:uk36-4ԔF[j Ci{ 5tKYXɎM.ҥ3ͼŋQsqqt~giggXk>v y.Zk*e i?ܼx6Ii7{[$HOK/JH$fff)6#SwW_8qiqS99ԇ:!ݻwĿ5v젾}_~ly&L 'wԑڵsNMNNI{v}?unh(e˦ ԹsNaߥ4XC C ֚s۰qjD"033355566ntq?>X+*W(kjB` wcZ-7כSп[wOT|فt-) g y.# y.ZSupppqqqww< -!!!---BF8Cڤ!x!PkjZXXn oUF[BWTTTQQA'NtG"N-R۽{Ŗ-[40jM\333v6ފ~40jM\SSSZXXd/m!А<55s/\`ff&|}}[𶅐>@C C 988XXXMP(ې>@C C >DGGrLfaaa6h24%M/++K8 y40CZZZXX\.wppT4M'ܔcGozKZv!xT!hҢj&CS􊊊X y.# y.@PTTP(MDUTT'Uݹ{)5zw6پ%هZ\hW>X=&@.m my.iW>X=&@.m my.@<}@ > hsm&##½"111½Mkr{W\6cmm++RRRraަp]#G{B fZoakuuuX h@ ڂ?nccs؊ KKKcll\PP8q^CF{Rimm- Ce~~~6x3g\tIOOOuފ M/X(ɂh#%%חNmee~I@qq=$T'>Vʕ+AGNMMҒht>tlnA\zU0Iߟurr*Uj:{{{Ot"՗lp~CpPִ')) y.@*//?vXNNNVV֙3gbcc EMML]mm{<2,??ԩSW^-++ս{npp[gtww/..Vfܸq͛tҨ#GұpWW;wб,lēlj&&vXAK.h#--\Yy.C Ъuttsssuuuccce2YMM=eXRWWwԩ$qW^^~Qa3o*Ͻ{6pV9r$**a`-o]\\Mz8zhnnmP's,ܫJKK{BMMӧ"""oA oJ:C > hs\y.@<}@ >ޏQIENDB`kea-2.0.2/doc/sphinx/uml/request4-lease.svg0000644000175000017500000013465214206773363015461 00000000000000Allocate a lease for DHCPREQUEST (Kea 1.8.0)Check requested addressGet lease for requested addressCheck client leaseAllocate a new leaseAllocate or reuse leaseAllocate unreserved leaseCheck lease for reserved addressCheck out-of-pool addressCheck requested leaseCheck renewCreate a new leaseDelete old leaseGet candidate leaseReuse expired leaseReclaim expired leaseupdate lease informationCallout lease4_selecthookUpdate leaseIterate pools and subnetsPick addressCheck reserved addressedCheck already in use by another threadCheck leaseFind client leaseentry pointCheck requested reservationGet reservationUpdate requested addressReturn no leaseexit pointReturn renewed leaseexit pointReturn leaseexit pointhas requested address (hint)no requested address (hint)no conflicting reservationreservation owned by another clienthas a reservationrequest reserved addressno reservationhas requested leasenot expired lease owned by another clientno reservationhas requested reservationhas reservation for another addressno active reserved leaseowned reservation for the requested addressrequested address is in allowed an pooladdress not reserved and not in allowed poolhas a client leaserequested address was already assigned to the clienthas a requested addressno requested addressexpired candidate leaseconflicting candidate leaseno candidate leaseSKIPaddress reserved to another clientaddress already in use by another threadactive leaseexpired leaseno leasekea-2.0.2/doc/sphinx/uml/appendRequestedOptions.uml0000644000175000017500000000125314206773363017307 00000000000000@startuml Title Append requested options algorithm (Kea 1.8.0) :get configured option list; :get parameter request list (PRL) from query; while (for each item from configured option list) :get configured options in dhcp4 space; while (for each persistent option) :push back option code to PRL; endwhile endwhile while (for each code in PRL) if (option is not set in response) then (yes) while (for each item from configured option list) :get configured options in dhcp4 space; if (found) then (first) :add option to response; else (not found or already found) endif endwhile else (no) endif endwhile ->done; stop @enduml kea-2.0.2/doc/sphinx/uml/currentHost4.uml0000644000175000017500000000566314206773363015217 00000000000000@startuml title currentHost DHCPv4 (Kea 1.8.0) agent "Subnet Selection" as entry agent "Set subnet to the selected subnet" as setSelected agent "Has client a lease for its client id?" as clientid_lookup agent "Iterate on allowed subnets for client id" as clientid_iterate agent "Set subnet to the by client id lease" as found_clientid agent "Has client a matching lease?" as hwaddr_lookup agent "Iterate on allowed subnets for hardware address" as hwaddr_iterate agent "Set subnet to the matching lease" as found_hwaddr agent "Has an address reservation?" as hasAddressReservation agent "Set subnet to address reservation subnet" as setAddressReservation agent "Is the address in an allowed pool?" as pool agent "Iterate on allowed subnets with pool" as pool_iterate agent "Set subnet to address pool subnet" as inAllowedPool agent "Allocate a new lease" as allocate agent "Iterate on allowed subnets" as allocate_iterate agent "Set subnet to allocated lease subnet" as allocated agent "Get subnet host reservation mode" as getHRmode agent "Is subnet host reservation mode global?" as checkHRmode agent "Get global host reservation" as global agent "Get subnet host reservation" as bySubnet agent "Return current host reservation" as return entry --> setSelected setSelected --> clientid_lookup clientid_lookup ---> hwaddr_lookup : no client id option clientid_lookup --> clientid_iterate clientid_iterate -> clientid_iterate : match-client-id is false or no lease clientid_iterate --> found_clientid : found a lease found_clientid ----> hasAddressReservation clientid_iterate --> hwaddr_lookup : not found by client id, try by hardware address hwaddr_lookup ---> hasAddressReservation : no hardware address hwaddr_lookup --> hwaddr_iterate hwaddr_iterate -> hwaddr_iterate : no lease or client id mismatch hwaddr_iterate --> found_hwaddr : found a lease found_hwaddr ----> hasAddressReservation hwaddr_iterate --> hasAddressReservation : not found hasAddressReservation --> setAddressReservation : yes hasAddressReservation --> pool : no or not check in the taken branch setAddressReservation --> pool pool --> pool_iterate pool ---> allocate : pool check is not in all branches pool_iterate -> pool_iterate : address not in an allowed pool pool_iterate --> inAllowedPool : address in an allowed pool pool_iterate ---> allocate : no allowed pool inAllowedPool --> allocate allocate --> allocate_iterate : start from preferred (last used) subnet allocate ---> getHRmode : allocation is not in all branches allocate_iterate -> allocate_iterate : no free address allocate_iterate --> allocated : found a free address allocated --> getHRmode getHRmode --> checkHRmode checkHRmode --> global : yes checkHRmode --> bySubnet : no global --> return : return global host reservation bySubnet --> return : return subnet host reservation footer Only the initial lookup is always performed: other occasions to change the subnet so the current host are only in some branches @endumlkea-2.0.2/doc/sphinx/uml/main-loop.png0000644000175000017500000012672614206773363014501 00000000000000PNG  IHDR6$)tEXtcopyleftGenerated by http://plantuml.com09iTXtplantumlxTMo@[?8HRQUTM (&顪=fTwNzyf9hTPFte=gv6߹8>?XurTGۜJFeYLP,ejLnGe\v \# O4G F\E8G#H:C*ccP ׉&{UiC!u G̲x+tu})m-9>U&K$?uJ_UҺk]KYCRyJr*oaÎ+؍VVa!ԺL^cYDg)Ttr7f\VRR0 l sT\+s"/XL he\ ZtEw iл^~^]h.AbF]UL++l[.LpC1=WRlivxD1tiqTSD$c.Cx\~ mȜ.uF#IH0DIDATx^ ;}_VQY5IM_HR~_B)IYce_ haPc!kx>ܩݍ~ҤI*nsG5z$''[wޙ;wnBB?~x:(L;&OUPPP2ek>=3^7sLҥC "by浔%Nu6bѢEiYLZ*z 1)^J²gϾu֛Qj׮] .,^xas Ur=LΜ9SNc\r[iٲe3f̘[o ΉVy͚53z#G,_<֛w&iꡕޣs2:M2eʣ>J=7J5nܘgܸqb)YnذaC֭g̘a5?hW-…5kRoF5zK.ݵkbŊ9xzh޽{:tjN&5M~a,_P!ez fHJs=Gg{{Ħ;R{Kkɓ'Vm5k֤%%eݯ#{gfϞmݐڿZI "E8*t>ԌWsح{#*E.~qGؕ+W-09xBe7)K?⭏q蹺|2Zʑ6 EǭgeкukzU@q~F?oydԩz/br/tPڵkU5 ιx7vsʷ*QF*AmRtJDb9sG}DԪUEӦ>9 tyb&M?_ھ};vWNF'F@뎴/mtؑzZ[<g!N3zΝ=HW,f!3Pov X{qvtOgFe Јb ,ψysOLL]6})S ݄^-oJ1BY~ױUV%d|N7>vϋ%_~f&J,XY,$mqIz3Hv_Hpi%ۄ'Rg>}={ijvI9)^~f͚#w<Oob DCBB,Th:y'&)P"R9rx 2D_*U g$林O7(C u~(=ځRYn]};r##>2%\rI|_nNjEݺ-\)) (H@xƍifǑ(:}tl *D/"ͮfgcϘ1ƥ}ƍWF Ge<=|Gsh["~~Cz#H.]qq7ߤwUVut3ysˌ_֭ѳ~zޯؿ= 4oޜnas8(U'M65 ι y9~/2WY2^| M3/9s(G|8_M)rU>)4_|ƧmڴqdZƧ8#7s(譏8w>S'РA7=FƧznf林;8l]t0dJ74zQ=g9~QxpixGEE#72?ub3YQj3… ;v8LrՋRdZN:Q;l/8>+6y,컹_~őiծ]ؚj+hь?ywƏH;rQ0<<ܘ-Z_ŧoG*ǜdy䡞Cϛ{N3>>>4;O?ԑ;@oy鍅ƧPOpp}g|+Ef/H@2Km /ɐ.jZˌ'FX_J_}֬YTW^y%5ߪU͛O8Zjm/6_=ԩSrn:t P<͔/YwsD|RݢE7xC4t3otPmI 5~#m)LrwqƉ>L϶Y2>5hP꤉=gԞ={L#k.?_eCBB~ӦMiO>D|\ѬY3*K=m۶?=RJnG7QFv@F!9 ԫWgϞ}7W E8?-[ohkٲ%(%~%F>ѣy==~x7i_޽_P sNL jf_1QիWtAE)=g-W\I=O?ca M< #VΝ;M;_ s}^7]fqU gd„ aݺu͌ޜoI-I'_ ٱcI@TM(-ޯ8Ҿ$x!c+~ DU̙3t~jZ7b{Om5/@gEzB 'd<zBIƟ={vtIoJ?l޼ۮ'n sJ2>CU S1D:5jÆ qqqD:><::b~޽Nce3d<@]?ŋE篿j׉zgݶmu'b#GX7}5nܘbU_x_Xb\*W΃.w&Q-_|Dl :yͺ2 0t/ߺuR[N~3… Ν;@[2lٲ֭[iΜ9#9m۶mjLL?dTkРs=ZP!:w\sqPD:&%%ߵk:N-믿0zϙ3斌'D ;=vӦMܹs_|ҽ 7jhٲ%]޽6( "BBOiH^޳>*E#/@fSRRKgN=sqɨf ~.]L@d<@`02~"?j(G'tv}G|sUzjq |\\c7jzPpa:l0sqP?wӧޚK,˅ zN?$h?гg+WԨQ+T0z+ƍSl_z&ZѢEnjCt944СCQ@d<@`^#- IIIt^r8|>2p@:M+W6nn||}Ƌ=-]vVZp;v<~M"6m;wnGW.\xs0P SN?r||q93\x_i+W0o;@B 0m۶7nnL2@Ox=!2@Ox=!2@Ox=!2@Ox=!wStyx%DZ@ӛXdD0ԦHkǏE2^")-RRC vrc$2xCtl:8LLL13x!_0|Ltt4{MHH13x!;x82>2^nx`C lx!! ?d|@7d<!膌6d*cc۷߱oE7d<!1\ٝ7o_|Qb]#N2Heհ[ھ} zɯϝ;Ҿ! ?Oiڕ+-.;mHH0MѢNoe{ C\ak߇ԩCoogvfhd<!MƿVo:NLDׯH#N^]`>:6v{{c{ -n񶺿jwl8t 2$ MƯ[)ҥȑŠ$]6<{ -^o`{)i͝={ʕzƍb QJ6kVzwZ;xҚ5,m29scktx3*blxqG>ޗ ʕԹ*U}啧>&IZoqvUG?;ӝS\[C lxyo ytm>۞BN*t,YTֻwǗ^zbO<ђfȑD?ev·~^fR}و+'صk>ҭ>ӫWGGv3gEc\`\9J(BiLs5_<uܹ9ܾ=={v0amʴiD=h >0&vUJȑ̙-bzPjjFC2^dF.:![ЩS9Mq=! ?/3~Рg((ViϚ6]t>={vz"R<ϝ;o.[7g䢟tvNJP.{A{3ozu7?8ذ7 J,JK-oʕ6nuJx!<;]}R~3gfgW憌6d'$>L"E<]-=5p.:7-Z N6L2WqlX\l){A{s[]Ohtn矗ɓvx'\a7F~9(((>~Uժwvuo mnx`CˌiOo'׮r^bgJZr_\57w8C2hnNAdf<2ؐCtC2^nx`C lx!! ?d|@7d<!膌6dݐ2>2ؐCtC2^"Ki)): o/t H! ܰgҨMnf|&c;tsk"4e4q4}ypMD]g K=֝C]Y#u7+(H'BOh !'|ZBxt -_@!d<@:2 |BWPN>+(H'BOh !'|ZBxt -_@!d<@:2 |BWPN>+(H'BOh !'|ZBxt -_@!d<@:2 ޙk㦂{^W#!ҙ:%%Ŵ%~=z08exKWN4i">N/T˩366 5kNժU/.pI'ⲽ T2X0`9ro#_|EꌋsM8Ѹ{nFm R6lXV-o'%%?~ܴ;vիN ⾾ud<ܦu֦M_vth"{Bg|pZz#yo۶-UEBWPN>+(H'BOh !'|ZBxt -_@!d<@:2 |BWPN>+(H'BOh !'| t |RǐpӛWW|ɧDmiļQO[o sxt'Q]r45ʺ+o!2呎K pءSNtŋ',,qtQѣCC/^vHJJ2jr_bŊr\#.]Jn6{ѵk… /_~޼y7舑l@M~9s&u,XΝ˝;7e']~Ghݪm۶t\r׮]3Pf˖Mzڵ%K5k|g.\pi\e|֭=9rݺuIT˗/_޼yŦ'Oެ F1Bƃ% tMtMtΆY+lBBBBį)SRQlٲ֭[E3gЦVZ92YmDFWcbb&Z ѣG *DWΝk.F1Bƃ%lx^ŋ~:--ZF(Qʕ+SLЦi\qn'D芳g^O6:s}Դn6Ľ"-[ݻw7 #wc!AO׮]KtӅAQIi}1ٸqQG$+E#LƋY`zҥٳSϜ9sl2/>ҥ6n z ܌v(H"_ޑfSd|6mkf}Xz("WwfQM|@o .LW f.F1Bƃ%|nxŦCS'QP׫Wz-ڻw#Fo?78>.,YrܸqNf={rJ5B GX"]nܸ1իW]m2=3f u:~ #wc!AOիWyl <(zMfĉ.]PjMJ/:m?ZhD=;w4JGjJUǎ?.nfMΝ.(P`…7#wc!AO]6Xx2|l'd<#wc!AOxF1Bƃec=! zB0r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!AO -_]6XxГ|BW1r12$@{ec='|#wc!=|ܩM{2]VG>+(Le,]6Xxh-3{O: -_A@f,d9F1B{ V؃ƞ?6WH鄖D}0YlT#3{Oh J+  Aரlz411ˑOh J+  Aர`hzݻ7!!ˑOh H:pij 7+  Aர?w111֧^|BW{nIwX+  Aர܌WjF'|Q/,,N͛Yrec pWXn>XZBP/ߔNiEz+/+  Aர|'|.rUNiY0\@2@c.c,dh>XZm(}@2@c.c,dh>X#3Nݱ({KXUߝDz- ,d9F1B{ό?2~ah}J;~]óW\Kȯ@2@c.c,duɒqI?>n[xWWoU޼>X+O!-XӼW9Ylx6lXFa_;3Z zF\n֬Nj_|^<ir̾~t߲eFjoCrك'9 @㋴{TOm޽q]6XxYaiZ^sϐ!ѣMW.]x:Uv옻{wݶn-6 k7o76鳴p ?r(/wnt]6Xx<)ek{oklu>T^c71QWC&. Źx<h=Glx\aSWppiV=k:O?&$$-Z?h՝t\#ʵ u.RUN߳1r1m"Kx KXJ}6B\U244䫯eZiȑrb& ]n/ʞ==_GEj4eNgyцTyCʼJ^6Bs<~ ӟo0v+Y(;5Z˕+mWFvm#D-((h-u֯^gҤKNx\5ôM"Kߔ.Z'o7?ed<荑lxwx: E i\u͚ityΜwjNu>t7E7o>XO?-S7Ύs*p)I/v^[ʵ[R"|i Cyt!o^FY~=8ecM/[6.|;)7>5~jja=w[ZRlա!8!a-i.WSޣ~?#e0r12+, WVCk.= wwoM'Ҿ}:u7o^n( }խ[g̜VΜ7?V5ߊ{@ilBi6j 6YR|v{ ح^ofn_n;rܯ߹s+0@g@ec M,㔴/FǯrgF,ÇW^˾Ғwk kߔ6C+[͠Ծ}z##G7q՟7~țWm;Ylxz>W׮'޿E,[ԥKq6C^Z2@c.c,dVXn|≖իW[J>Q|zj9YlZaH_[vw#f]6Xx|+  Aர|+  Aர|+  Aர|+  AரܜNƛn?@l}xl ϛ WVB t:և Aரܜ rEm,@l}xl`j|WZ WY><]6Xx  @"AoecOo#ڲQS Ck'3"""bbbbccyjY(_Y><]6X '4::/@z&gu޽ dWe 1r12>93(PIz>Yibb"~wΣ@|Zf#wc!3M:*3I'=tzd}O W`2@c.c,d|YS LI*-֧[t@|f,d9F1Bƃ0ec=a zt0r12`.c,d< F1BƃP;Aj8]6Xxj'Hm5 B#wc!_ b.c,d< SecvVpl/sp zt0r12`.c,d< >]6Xx|l'L7#wc!AOnF1Bƃ@a.c,d< SecvVpl/NjN1r12 R[ )F1BƃP;Aj8]6Xxj'Hm5 ;l'L7#wc!AOnF1Bƃ0ec=a zt0r12`.c,d< F1BƃP;Aj8]6Xxj'Hm5 B#wc!_ b.c,d< SecvVpl/sp zt0r12`.c,d< >]6Xx|l'L7#wc!AOnF1Bƃ@a.c,d]6Xx.=wjnL\ћ->o >4W2(]6Xx$~T%n3$5WpjA9F1B{ VƞvpX#7Wpٛʠ#wc!= RRCe;1RHM7}h&2(]6XxdUGQSuG/Dƛn+8MrePl`ɪ&DGGKy޽ q7Wpٛʠ#wc!=*UMsox ~GI 1r122YՌ#WoC7ɕA9F1B{U&̛nON4{\c.c,dXeIɼ;y"-65wl:6}hΚʠ#wc!=*UMHM7*{y,+W?qxkɗR>V3ds&Ό&X4/ʠ#wc!=2c5{,׮fGtZiWLsD FsZ߾i}:NIɼߣ_זG_>vygEhnN73}e}؜GoN lVj/RW^yJ\pa{&xԷ7oٳU|w}t.\@3zǞzɯqG\FOt5*q [x~wzA3 j駖҈txҚ5̛!}w鐐`z?}#F;o+R\<(cw$dtߏ{TOmܣϜ 7ѿ  +B7ɖ-i;}y[`epU\c.c,d^2y`h}+Aev~y͚J*g#VL=={vW WܻwǗ^zbO<ђ(b?ժu֮C^W!=z9-:,vHOY|"/\Μwi8S\<(cw$dt/);wڻ2qq_^Fƻ9(\G\##G׫W69x/o}ʐg+recUa2oHR\ݺUC=R׮qRǏtժ2$s JFglo H>{GmH{稨1ĉwSf*-t$ӽMq?+TRG8;q|o0>qkp˻Umx7/os+recUbŲG~-#QxxM̯ %K;5:ʕ+-6BsիƇo/;wd߾]ZnD|&vڞٳG:\Ym-OqiFK/Z#6>Kt|s$G}l6/wsxo qDgty?w67 ~2(]6Xx\e?u矗By'Wut0}#N80Nyڴa^ɢRzM͌Í>XlR)7o>o9;J*VDZD#7WxGx/ qf4ΝR'Ŧ_M{ƻyyʠ#wc!=rO7کSОV!z&=U.$$֦~ڶmqa?];wΣ/iɭZwERҎ|{{F>㏇FZnT`ɓ_ߵkҥĦjMWsYDG7߰a: ָxqGey쮚̛n+^}2˃vӦ?z˳fMǵw\ + 1r12oV ;n5fG[t<͛Qj|5+ў>tϞ}1d*Sk޼.7eG}[Sj}vi߾ m:u".m)Ra{mGڗnƍ?VKhS9{z/zPIɼWZysE߹栰Gt䆅̜VΜ7?7ܰ2ʠ#wc!=facǾXjr_{z護x |:` ϡC+[tT:~MAYIɼv%|$ kᕗ/o4W/oɆ$W Af<&y$K8MrePl`ɪ&y$K8MrePl`ɪ&y$K8MrePl`ɪ&y$K8MrePl`ɪ&y$K8MrePl`ɪ&y$K8MrePl`ɪ&y$K8MrePl`ɪ&y$K8MrePl`ɪ&y$K8MrePl`ɪ&y$K8MrePlUBe\HM+jpٛʠ#wc!= ZV5ɚnWV/Cs+recOo#ڲQS C-Lj5/>oxj%}h{ePl@Ohtt4Asdgszݛ`Lm5`fA9F1Bg@\\=1 2͑mz陧rbbuJ\n3ՀGW l{ 9h,=lsN>4Gz9gd딸&?fj>0c 1r129RX*Uu5#wc!1ťvVm0r12vVm0r12vVm0r12vVm0r12vVm0r12vVm0r12vVm0r12~w #wc!(#RUWm0r129RX*Uu5#wc!(#RUWm0r129RX*Uu5#wc!(#RUWm0r129RX*Uu5#wc!w 0r12vVm0r12vVm0r12vVm0r12vVm0r12vVm0r12vVm0r12~w #wc!(#RUWm0r129RX*Uu5#wc!(#RUWm0r129RX*Uu5#wc!(#RUWm0r129RX*Uu5#wc!w 0r12vVm0r12vVm0r12vVm0r12vVm0r12vVm0r12vVm0r12~w #wc!(#RUWm0r129RX*Uu5#wc!(#RUWm0r129RX*Uu5#wc!(#RUWm0r129RX*Uu5#wc!w 0r12vVm0r12vVm0r12vVm0r12vVm0r12vVm0r12vVm0r12~w #wc!(#RUWm0r129RX*Uu5#wc!(#RUWm0r129RX*Uu5#wc!(#RUWm0r129RX*Uu5#wc!w 0r12ރgϝڴ-S[wivEm5a8 S1r12:hNoc}ޥEմ2XL]6XxjupS"єfҺƛnWVF˼c2#wc!=))ߡ)o'7FfҺƛnWVF˼c2#wc!=jMkFMk\\^s›nWVF˼c2#wc!=jMk ޽{s›nWVF˼c2#wc!=jMksߊ o]Q[M{8jr,ƪ: S1r12VڌuMdǗxjQhc#wc!=j 7?9uC5PecU6d|:yϒ7<,p02>@1r12Vڐb~QG !#wc!=j (}8*g#zx)ג/a4d|b.c,dX2!5@1]҂D5օlx76d|b.c,dfٳ۷mMlumEa3Ex{f՞zaap]҂ԣ Lgqb>*>~Օ+[iy,x|?`.c,d^f|Æ5̙bŲc 'mj;{~Jni4s,j}TZպ- {tse!yQ[Ǽa=kJZ /А Tv<&d*F1B{eׯ_;K̚vϞ)@a͚iݲO\9/d8Zy… ?ξZkvEm5P/ LuծgtԔ)6odXX:p^y)ckժ29?7={"gϖ/_ٳG95y:Uz<oc2#wc!=~29NBw^s7Wo{=]u-ggٲ to zƾ[nZkvEm5m$,!=9[ZMxqG>`|v3~oN3h8n_ʽ;v|ΨM+XcVi4l*bˠe޺ƛnWV{i-?j,).Ro.NFfkx4mu,@b.c,dޯVƚҳgzvͧ˴ ʕD"ts^:n̷&T1WܐOY|"-W,R2a {Ѫ4m0cDZ/hMNm>˖~PZ7'N~: W]c<_cVZ22o]M+ji@߹ի#[﹧L\ܗFFdx%KFeǣnc2#wc!=~*UС=<T1;w[ JA+jZQ?iYMƗD)-:Miy'ּN-X!ݷsGZGG_^c%t~.9ˈF˼u7ݮ=KS뮒>Ulu8CGx%{Feǣnc2#wc!=~ʖ-ʅ7?~PrNO~m]kn}]㡴-Q5)Z:K 5;yuJÛnWVӞG%!^9tuyo <^S4WǣX?]6Xx_)c}z6zkߢE=kW`yBZlӦhtSCBŗtFw&_<_~YN{Gd'u6ȑfJt+&ob4Wu{w^E}PQ@T]FEdPtm8]@Dž}(! KBd$̀W#b@"İ%'eݧTvSUէt}C+g|rG+]cLcwv#2\s9׳݊ E.ec/BW+5zwҮF#GuSﻯYN:0 f*/?1ԡT٫WWZwhuGU&Mߛ>hnuZJ\@+#-^iYplz6jTS}t/%Ik$5R[vcr]21!?+O;PgTp6_Ѩ-ԯ'?̨QϷmی3{e|~(h=:vW_ͤ*~4 _ _bVm4[.c ;JJC<-w߿xURw;i1ʲeˈ~ƍ/G6ir4`[i-]ziRQ'DO ֣zZfNcƼ vٞIkB,,`-b]Kl-QInY|^]hzſkěnu/bѢqƽ66 `{,/=᪫{lk{?d|0r12\JZrsWF"?_ |뮼5[~tС~Xhjn[i-;v,lR ^zݻglX =TѲ[of䐓2j()ے,3s_|r/ lEД*~:kڷo~Z^FQ7EkSVI>er|Ȕy+QBB/w2>p _V%-%՛e˖ٷ~ǹ~iԺ׻#cwQ[^( ~]6X"}yk?i'2\3G.w4j{ =5b\ _VZKu-Ⱥ>g{ x;Fv/1r12Xu .p0]6X"`ZkBw @+F1B֢o]]hzkE߽Z1r12X}rG](ЊlEj[x;Fw/Vec/V+EߺƻѨ-p0{b.c,d|jy;t(/45FmoQ #wc! V+EǺĺѨ-pװ{b.c,d|?gQ6- YRd<􌌌H$b,BP^ _4tN.\vhJibizrHmo1E5CǽZ1r12֮]KsJSF4WRX^z/bkp;{b.c,d| l҇댌%C#M)M,M/=/bkp;{b.c,d| j"ȖU`&<󼳜6R[Lw nq/VecAR;j{MC%F1Bk |Ŧ/M0W#wc!5rDN 4 #v]6Xx$7 F1Bk |@h0r12^#GI Bl?"Hj'_moecAR;j{MsJecAR;j{MC%F1Bk |&!#wc!5rDNbBl?"Hj'_mo s]6Xx$؄9Pb.c,dFW[lB(1r12^#GI-6A(1r12^#GI Bl?"Hj'_moecAR;j{`.c,dFW#wc!5rDN 4 #v]6Xx$؄;]6Xx$؄9Pb.c,dFW[lB(1r12^#GI-6a! #v0JecAR;j{MC%F1Bk |&!#wc!5rDNb#wc!5rDN 4 #v]6Xx$7 F1Bk |@h0r12^#GI Bl?"Hj'_moecAR;j{MsJecAR;j{MC%F1Bk |&!#wc!5rDNbBl?"Hj'_mo s]6Xx$؄9Pb.c,dFW[lB(1r12^#GI-6A(1r12^#GI Bl?"Hj'_moecAR;j{ #wc!5rDNb!S,`-D\ @ecAR;j{y{ͪOLTCTDecAR;j{k\1ijp@1r12^#GI-v~IkߊAN||+z]6Xx$)ڥnEsH6՘,F1Bk |ŔIu+M(M5l?"Hj'_moms9l62F1Bk |Śm s/mn c.c,dFW[9w$[Zҟm decAR;j{svmYYWK?MG4~@a.c,dFWۛ3J?#wc!5rDNޜ<˒TbrwecAR;j{s&2R0?&!! #v,2>m(Z֮]KKUNNy.|l?"Hj'_mob* x+55l\#wc!5rDNޜTOy􌌌H$b c.c,dFWۛXV>]6Xx$7gxa.c,dFWۛ3d<@0r12^#GI2 @ #v  ecAR;j{sF1Bk |9C#wc!5rDNޜ'VoyFS5UAC80r12^#GIYq2>9͏>z#E}ė换h7d<#wc!5rDNޜ'e4_t|]rmcm}㣕nnˬƂp`.c,dFWۛv]ӦԬY\?ZJnnÆDqÙ}}9kԨ9vl[쳽.vre…c(ݻԯߟvN2?1 8uNS: kAC80r12^#GIg7_ߺuM[9/HNMRܯZ@wR };EQ˔)ݫW 6kve\\\fI+޵k15_` տ~wUmd<@+wc!5bn9Tۛ33>;{Q\\m7um\:ʔ)}-ėM6ҧBno p(xF1Bk$s7gg<^.⋔U&QKv5jT9׭z}YCĮ>}WZ9%*_1_qr޼Q|&j]6Xx#vD?ytP*+;vl٦MSM)t0rKNeWRiȐ( ֣z.={8mk޼' y ! {DvCΡޜ(Ֆ5[ӟܕǏGrסCzQec׈="!HjPmo@#^~饗zQrm۶Y'۷oرcŊ7n|w#@;Q{DvCaktvݻwS5\㏋htϿ9t<%"pĉMR__~\pU?~B1--R6ZhAGҮ />4O1l{mW^O-}8GmQO'ߧOZ>?+W^<&..gϞs=t萵s::lѢE5i2މE8#ve|䦌E=_|h{ĉt>|xմoZX޸qcff&m7n}nݺ(_`9ɶ;:z. _~%?k,QoΝ;m)i{ XWg#v/Gi֬ئSd|RRm4hf̘!*7l t/O^P_oh=x ;v_z%Sq۶8 2މuu֍="!Hj9oA 3^<O0A6'+V)S/ˌŒEox9H~cǎ͛7\r޽e j;n8Sq۶p̙ѣG#@;κGd7I:d< 2މ.{DvCad1c͛g@;qen9?C;qen9DN_v#v9d] Cd<xe="!HjC;qen9DN_v#v9d] Cd<xe="!HjC;qen9DN_v#v9d] Cd<xe="!HjC;qen9DN_v#v9d] Cd<xe="!HjC;qen9DN_v#v9d] Cd<xD_]^%#B4jSCƟa{o_]KU#B4jSCƟ!oϾ:.P=PE#B4jSCƛo)]ڦ7ȊOϨdB̞=ӲNfT`nXڇ^7 Š6e9dYZmgT%mПMTo>T}CϬvo#VTe#^pzvIqE7[jSCXnH=#-'mS 6>뜎ugո)LO>l> vɆ \3ţ6e9d]WQʊĥ?ij)}ڢ^~Rטut 5_OqlVjSCKY&6`f0nzDҭ5ǼMm"sx{[F$n]iۼ[{yjz61ݼ; -ξ˶9PljSC;wdF'mwk@L/זFYx)GRT`6e9d|Tx]ӼCOw.O&Ƿ_w@IMYd<x.&2v/PCџ]ʎYKy#nhz E9ybڔ5e|oB:Zʂ;\pEV/ˋqI:3oBYpǁkb(7{g$c󊓗!Ssm*C 8q!cD[qv-\YS?G]q cXW^v QL|2EqE.gE8S{?>IЗjvYX d<6)*IDAT!c'mI- f%) e^qcþ[|3rh/{ Emo?M=fb-#F<+)3Q82\?Uڴiڼy#S%+U믙ˬ&M(U(>>]f۶͵vEeW+W(P'(:A"V{N!N7җ)`ycLeJm6?/R|gi-7|}MJL!O-iڴg{QMR*V,_9鞢}ҤQ׬YmŦVǎS_9e'ʕ cxe{]~ ɏ~iיvu-eZ`!)K.G.kW^|Ak2Tk|g=G-W5Z` '&aĉ TrҡXwX+f}§?.4Oe$㯽rk}G7nnLZ,C`g|nm띛 g,+r8SŚ?^_Ϣ{wԊj**7lH}mrp&՟sN5==v]FK.V珹t>0Q楗lРNu?CJ'Ⱥ>>~η3?y_VwЂ2E}t~S~,0:p`*U*+Wjx{,Zbz:JHԹsիԪu/?"{LfjkKe˖z뭭S۷ 'NضI3h/:% 2Tf{ _"zz?L{^^doѢ1wz?,4}'Æ=3`aE=-"+siuXlsLj}>{:ɵmᴲ,X0ơhySu7oND=%rOo~(`[+E}4irwtڵ=y7otEO8hܑ}ЪO?~ߌw_'>?ܝGIJzUkiHVznMQ|ҴXGZ2M݆wݑy8&D]h |(?U]7gqZ[Nk=FVM2 z]lܹs1- @/i1M DJ>|rӦjK2h{%X5 u?Zc䓌/eᇧrGhj2%vm!ی?{Ѻ58Y48eѣurʊd꫙tx*{'L]eʔѴMxxshp&}Vpǁː)&^JZ0Euj5E19 fG?~|}ӟ LL ¶w 2~郼Ni$2^࣏cjumKg,k|,^]ꄞƏ/|J.op@^{B&!c8Fgd >9%xNj7id>}2E};uk*mV􉙞w;= ݅ I=u̘h履Nsӟntцsh|&M=;q2db^}M7@{.5[̦&koBe_\6Ql1bǎ.Z4t?,L30WJۉ2>GWhᇻ[b>E}PeJwc;Y>FTLh҆@|JƦ X y]&O?-m޼?gS;pMDM=;q2dbfͮ'i+V$mOddLg1]Xbyz2uEK }DJڳgx<իWWX⋔U&*mݺ!m<|ou?Zc$ c~F?L؞: Dޑ 3~>d,w//I7k&.;OQ5%-zħ5f?N_Λ7J|*Zb}ڶZCG;w䭷.eVč7FtK~ҍʽB5pњ; 4PpǁːJVʌ߹s1}Z{tg}pe:wyDIN~S.:o߻ߪٻYv-[[Z[iOB :l>x+ig}:R zG`1R1zNԚ7'R2?ipo߷(`ʕ t`RR.;dݺ ֣:{pڠHdxh𯖢uk,mV<Ѕ*qGR?[K]\Z& POA" Ek Js(.e.Xq>OsoYgpYGw +Yfs;\,3>d<,e.Xq( 8p2> V#d<;\!c`1Bƣ.e1񛇾C7М`ő\x 1Z;w,2%Z#$x$q࢘YA x1S&{/111===###/f$x =)juLk9}O뮘*51F SSSZU٠96OYКF~x!JL**LL0%51k׮ISCS44'43tsrrS3a[? ݗ|d)сݱw&2^}vXZU٠9G\ k+aoB} )J׿|@,Ot_E"-ޡe\) ZnS3a[? ]g1?v f51~`]4]MI~>n|_Z[i6fTl?ڍIa2XIuml3#T˙oڳ? ?SʵMkix@d뢩YvK?zh/8==B6 J!=Eߟ4]MEޞ}u_` zۡ21뢩۠4PoK>p\n]rժU&M\LǏשS{Imۃ .}-݄AƻkׯUnJ6W_]PӦM7m[ӌmI$СWfٳgSX|TSZ_:oԨHK]vٳ>+Sߟ\t8FnB@ =`͒={ݻT,77awM1LxM{E#Gl=^>}Jsm1؎MMhxx%999=l0uP ϳdӦMܷoyGlğp] d@ 2uA{YO.2x K Bd?@ =,'\d%!ğp] d@ 2uA{YO.2x K Bd?@ =,'\d%!ğp] d@ 2uA{YO.2x K Bd?@ =,'\d%!ğp] d@ 2uA{YO.2x K Bd?@ =,'\d%!ğp] d@ 2uA{YO.2x K Bd?@ =,'\d%!ğp] d@ 2uA{YO.2x K Bd?@ =,'\d%!ğp] dnO)KL{+xd6g1K/CxdK=y%{zۡd< 2%n~lF"E^nzvkl><AƻҒJY&y rmZ~1 A@ ݒ?^qWZ[)KWl7͟Iad< 2=YO ?O*/kw2xHr"gոkl><Aƻjψ{]w2xW世)eۤ|"y7x !w\՗z߼Eebl4rR[w| !㋌,KbpQoeI(>)4e)?.MB2žO<k׮ENNʅ 2Bb_DOVjj*-W.\2x"2~'&&gddD" d< 2žȌ/;#2x:d<}A2ž b_AG/x d:iSJ$2رrĈ~փKZ6nLW=fb]AJjѢq\\\Beʔnذu׷nڤ8Lj9TPOmIKy^wA]2~ÆrVZ9!a=@S޿֓_ث*_رuԢq33_Y2mmZEAƻ!?(˖M?3_O`=*mM HL|ús-ݺx՗*esی/~綥8͑A߳gi\\u("_SS6kv9T^rÙ}M5jTnyyk^z .O KsmؐH/:4VpݻԯߟvNβj >% اJJʕB5Cj֬V/Hٲed S\RF 5#ԩEƉ/￿d<@&WBC]PVZRt05zRmذg x8>>~E=׿6{#E?"'Mo*-Z4]] yM\q]ig}мy.:As玤N$%٪յtxUL^N0>df>@\ye}qn?y?m9z*ƽ|ntiMCLu(a<)z^{w\LS(k;th!_"ERZ?򖱭9G3WO=zt_=NCd<뮛Ů{QS[~w}믙+W]ܦMC4lX+&@&.KC u(?߾^dD5խ{[8mA]`2>.{nuz9_}5YVuȰ^.⋔U&ɧBw_OL|#5u%KSܶmLi {YYI{,3a~EC>ݫVVѣ1_qr޼Q7F/cz)uҎvMӦ]fxs,UHO)^s-x RWaFyU aرe6MŶȰA/);{zl2w{K||xd<@p"?gQ6-?KLLLOOȈD"+>拌73HMM\ >@.]l]Ư]NSB堋Bo]Ș|.](kZcxzX8D"[B堋BgZcxPNxpB2 ᄌ'd<@8!Ie6l岲9ی߿<`ƍD~&7Z7())ͱ}"2<P"S.S:oٲ%''g*"? JdeJgz5xALLLg3PxpB2 ᄌ'd<ٶm~'sűc6nh01cƦM̵z뭷̵0)z͵ә;`V0F)??8ߺ>q`AzÇϜ93!!˜&%%j&Oo>qw_ ^8l3geرcꫩS~TIO8f0, 2_uСvΦ0>vX$9~xZZQ;lذGR&f<,%%w}w#G}:e.333~%2$J/zTJ)SP޽{D!J>'zi">t!mdddP,1 ƞxbJq-YD|?'xڠg_#ml.?/󙈗fz)F5kѣ^%D;^uuVڦEb27ƿ.(ngyf ' =ҳԭdѣG_+a:✉kleU̗@'&>ڵKX>0 _~OE^\ ņ'd<@8! NxpB2 ᄌ'd<@8?haGIENDB`kea-2.0.2/doc/sphinx/uml/request4.png0000644000175000017500000025760714206773363014365 00000000000000PNG  IHDRYf)tEXtcopyleftGenerated by http://plantuml.com09 iTXtplantumlx}UMo1P IrBhCP΂Ŧ$;eI{c{<hÔIWI)(=tϺGX+\,| yr0 ֒ sL8bzv@Hbi K)}LeՆ.Sfr2VrDя!H&Lk%j ߠܠ%T/ wC9ncVۆ<$4+ A=9h`cKT&t g-ς= #/ o)Q}c0;ElII:R렻MyB\)bHLm!_b("Wً%' V?U3[DS44nn1YR/7SYeT1 "f&Zo]Nf3Su>I064t㇯3A*BX9EJ P)b0T_\`#Q;gS3Kۺ}9 cO89we$5ݟ]}_Lk2p]A%}ۊ+bZ1h|Uqi8e3qخ39L8!CG_;3|C 3{?vyV*OS}4~Ѽܼ},e}S{JgT&h7p%ŊF1?=.d4qxz:41eDlfu0G %77= 24E 4}9IDATx^tU$&l"EzQ&"Ei6DD61PjPB 58- L={lr]|n+mT׮]ӛ7?v- ߱c6ߗ.]o>=&&F٣l03 \7rH;>]t966-z㥽YfF!ٳgV+zy'ⶽ 4i~ٳg62ed͜9,hD|g矛?/*9ߖ"Eh]G9rh횙3gj'OɭC~~~jW?)F$cyd̘qڵ-6oܰaDBmrYd͛'-KDRASi~y֮];5-ZT&c$#-2C|FWܸ_|QC֭[W 7N0[nŋ2!mذA߼fƩd}7f̘kǎ?:uwTmtۢEM6?'N|W k "]ʰg}ִi2j!!!7nޑʱUwr…RJI<[&c,\[o?~% p2cǎ{[M4ǙKꡝ8qb8Sv 2l߾΅! ٍ 2 % aTBCCn%9?jUSOIK6ml{/]tKK͋+;!%dΜYKzRS5*YS``E|< O3gΨsRgTӦM~xb/OҒzvIPw9"w˗/YFˌ3E^Nw'a kTMƛ7oy]V\dT?#gإe!kv{史|Wꮣ=QjԨ!q:a$IEJ֬Y7<<\Sғŋ˕+7lذe˖խ[7{Ŋ/=AAAUVꪅXytdɒEy2e~7u-[.\X6WZ5 凛3g+2dӧy *$$?'Wl7n˟?wz}n'y0?`"?wp]KDD++ܱcٳ+V0Q|MeӏܪW}&IDTTj5j0^&Yʕ+Ҳh"b1K.K_ZZliz^T^+7m.jYcs;2wyGN8nse7իO`0@ ܹmذ9b'IEfw;"Tf͚3gN=pq|^*NۄIO*bIT?1#0r1E~q:$q<p 2I7nD%2QWꕴOƕ׬_~l Ȓ%˃&7L;`KT~TnKԩzLqVn:ue,PjU 42*ީӧO7 ( ]{\n͚5U? w˔)#uM*rTg$D+uWeuWZiRɞ=s#ݸi#^z>4$=Ĕ-[V<|./Oo0#uԑFuVL> gcW! 9I*M4.aG;wNMܖZU\lB^㚿C.]zРAŊ=z0-pOn$|C_w^{}ғmmڴxSxkۤ}y82#ֶ$*IKtk2JLgΜQ;f{L.v5ѣ9&N&>/XvR9}cnjΓx3edիŋ/n\l$`?F$w$wR=%K̒^}n_Kn,bn,_ypz<p Xf>e!gv2LNEb5k֝V[s͛eN0$$D`'zj|ŋ+[lj}Ls5<2 ηev٪U؛u%YlHIx{H;={va+b#D\u-bRQWlh:}b%6!CuE-szC7H&W]>,?D*Hx+G۽1c8!**Uk֬1Zr#; ss`%dΜ9G,I_QԲs57z%"NZ\zUL0^D\gw2UGɰ6IeΝ2A$.Mh.Ʒc9͚5`͛Uzw_xmz$>/}$ hg)u37޸Rn]?Sl;߸&) _pRAU_Wtiۯ~Q]ovJFpq{ƍ>tmRy뭷|>zj9_'1l1\l|ۖ-[$qpvr˖--I%**jB#IȲE駟6Rׯ{v7_IO/ZhΝ;u$/%{1)\jmvbI}/"V~7lƶw֭|;lj*5~رj|Vƍ'w\({5/WgСCgϞ=h uJɶ$uK~Ç_ȋryK.5o'u$m5{ZRQ%v YvL,ՍwUŊ߿G}TdI}''OToؽKGۢE_]-XN5k1B2eI&uбcGiӦ"}OծZRy`9&({Z?XQZ飮Q`\E}CnR؄iIS?jժɰY0Q&ܸ٨W_}5((H~A*T`,(?Myޚ/\g̤ș3g ڵk>"$HoinrKyZo^-|D m +$jd >$S!wWN>#*)r#'LaCM꒟P}>F2e4J0-tW+%J0EUV5V n$|PA*$|&K꒠i|>K~mڴ1fP']N… uFˡCw^zu(-Rogm`KYl~R3 BBO+VP-NDLVdw=IiIll]G^z>~d/ϳdp9mF$UMS^رm={vÆ K,믿7Xa>#aȸ!)N>-E̋!?#em1GK9^0p@D~ ~H*DRn%yGGFFITf>((HJDDGRn%EEEGW P%)SDFFGW P$"P$"P$"P$"P$"P$"Q+Ӆ lCծ]JP$"Q+xӧ2ڵ-5iҗ{k۞կ_=ضk#I厒sOض滍U{mَLJ9ZQm%}"IrTڷoԲea%K˓'租:gϚ1cgyUȑ#?{衂zSo2ֳfDclەׯhчeҼyص+.nlKV%(]ehKo'eIH*w$ v:{vjQ\rѵkMkl0U׭TS 0a…#,Y2^){l#wV&Ӧ T$1,^WO(QhÆΈK[ސN^'1n\?yBK%}IH*w=$ƍ)RHU'Oʔ)cROR v2eΗ/طo,; TsӧOWVۥ{vp[$;JG-rX w &w-.M*R͚Ւ1>XU;"dȐ^ֳw:K7ԯ_%W#G~yoY[V2wPc$mIH*wTJ)0{iYt1* HPw%F-X ۷֮Jx __9nJ(]ٳg:c)󆒾{vp[$䩃?fۮjؾ6[ho:#rвӧ]+#ouRwp[$׷>t۲QjIH*)^_}%K~Rկ_[e]R$"P$"P$"P$"P$"P$"P$"P$"P$򮤲k)䰐Tɻ H*7-IʙsWmU5oٵԔEEEGWb$79,rp"""+xcR 8888Х$M.%DȘ ޘTd& q)I*zK"':::66V?j7&"]JRr@5\7;T\@H*.CR Q$!(ːTHIeH*$2$ERq " IDT\@H*.CR Q$!(ːTHIeH*$2$ERq " IDT\@H*.CR Q$!(ːTHIeH*$2$ERq " IDT\@H*.CR Q$!(ːTHIeH*$2$ERq " IDT\@H*.CR Q$!(ːTHIeH*$2$ERq " IDT\@H*.CR Q$3|WK*Z/ARIMz|V9HwBRI=qOm#"PBkv +*HLJxP@RIe{I*S3V-w>TR[|//|u$\WHTR[$~+5+GWl$v+2'gś#m$X۠J* hoFRq -Ҽu]ֻm$XZtv|L<-\9}XVU=ʶʙsQ xZRU]B%WRg&]Ç ,֮aCH*̤"l|6b-p- TR7xTHHHxxxdddLL~HI$a2AAAV"""#{u% `B9,Tc{1W3IrXFR2eJ```HHHdd~ēlϼLWΑT(ERIђopg 8GRI%EK=Ag_kxTTTLLL\\~\T(ERI2VVvN}f$aTȐW}CX IrX$-'=ޛTΝ[}_ŶW۶f>CRٵ]mﶒk=X4LulY|dΜqʱcUFKضf>CRy]mJϙض=eV@I- (WƍSlkNf{N &}ٻPv+);BI#12|GknEņU;s'WHR{(onlۯ:H*W +p'oL*Km׷J[lSsqQj>y,evoݱxkI%!l90{}v.+ ~jkC(Gt>#9j%ZV ~mՄʇ4ޘTnҹsSۮ7J{<9sG-^u 9%8Ow{pt~ ;_t̞=kƌyVkھ}f&5e"뮪q\ݎ Daܸ~E>,K5o^'6vj߾Q˖,YLm[;fTI%!l=0w琑Ky∓ѩS?.W`% ,[\GEuiܹ3K5n\[נ8j7]˕+4`z'Ԧsrez&L|2/oGnEm??|LrXhɓ_ݢy  /IEL%K-3kLÇׯߘ1U98#qAٺ+9KZnRRO*„ AV(Aa޼+%6hPU>rdp^>d͚Y2矷^tʑ#d]5mZS6l3VRy/+?l]Hqj'% #}zڵ6*[ Kc\\ϒ% >T9sϞXn.}=HR9y#ժ=rBm߾WpƌN8u:ݻ;/^7lx;:}-:)9ӧO'&ӧԫWYfٳ~^!sy] UQhjG 9yR̍Γ;p`[S*9# ZC%_m +<.A4tԮ֓1c:;~|=ۭnҥO|?u~^T&Olcǎ^{1cI護Wմi0# 8my 9%'{CzOZuOlT9NE^GGG!c]o݃;& Ԕf>+K6Yn-_b&Ж&Mu/ԗk׶rԮөS&MBBZnHVDD;Yj;_gRqJ3i$Pxxs^ۓTb>`AW^cDGcZ&?޸qu4ݒ`ѲetN (NJ2O˗˶˜Tm|v,> I%""",,,dAAA2_|?ko}kZ? ͫ[fx9zK4Zn-_9YΟQHʕwnm[|-ڍ\УNrȨ޾SOݼ#G>hݺl~JlO䎉ͷ$L:uΜ9Wҟ1̛7ED?O?}.﫮6m^ЫW?\~_vsp4F7kZ7AAäqРnҸhѨgי]4 qϞuT,P Ϲs륽c9rd o2eԒJ e{.0s(uc沔TM6dRكi2Z:* x|7C]x]˨ÆȚ5-g$(Qm휷뉍.#o,AۖR ϟ'gLNy?׮m#O7nw3gJGxcR9; j ߹s 6.۵{]BtI,UjY|J;sfjI{֬*7E%X#yO>4>KPS/46o^m"QFuv\ɛTdUaň,v:|G?HzRE._o{qq7?c{[ԕ+]"I%>!(ݻ`v'-.nmWcΝ[/!@\}G4j66JR?du!m:1UT-[&a%88Xʜ9sM$L>]BԩSmC=$-;8./ (^T'a%&&FJTTTɎ;$KYz]'Qj7mwIT@!PQRqJqyRq"BR=$' 1$sT@!P+y*ĐTERB9TK* I\$PH*"H*T(I2InT('IEMȆdsSgew#IrX.I*QQQaaal9rLx' \T#""d[AAA2IOnr8ѐc ;yfR5l̲nUGMmL:$/1JLLl%<<\6q!D~ xfRR98O9r4ȑxOK*WΜ;jyGL. ?m{2ִν`i?RS~J8*[/$G@ 9&rd^ӒY\_6n{QuzLd.: Q5/37 (?]K%.I%=uX:9)lGsU_رȊz]R yrRqkB;|o֔ghKPnp<9ϼy2/5Lv]TR}w#>^HEYUe9c{#ѳ6Jou<2xrkzּz\8xɕڜs["q~Yå2+شr \8xtu9p$Ǐ;Eou\?J Z|['WX-TRʥ'殑&俏xJJu?[խ}'}zCRI)jsVw\~;+7ARI) }zۻ-/ז+w@RIޚv-hwS9p-J0y5M9Hhn+ʷ;J TR͋T7WoM<ɕqr$H؝QzktJ+*K:J}4nA2=:'W, {3m_bÿU{oeg92H*oS۴M*I9z͓+9H$FyCv^Yù}$+J2y#GΔGψNRI%;5v[=Թ=QJn}$J2ȿN[=W_'rz7֐TYK [=ݹ!js{}X@RINWz~}0'WF Tө;'guM3jE5]8]#$#6V"NyrelS$d[q[_k\$I%lh?jB&7O|39(8HJY[[a̎˾I%y\<+K{^{p> pTGZ[4)(}>H*%;"igYWzTGh.Gm[d7O|=ɕ >#$|/=.:R+[H*bt̼uVNH*X֐:魰ɕҭ9 $mj k;ǂTfފ`|,\DRIvpVފdrο#ׯ hq:/I?#V@GR,>~vW]ۑ߂l+뗯Yv@ZGR"kup}Q7h{@aH*V_5Ŏz+RR;%(=k4ok_?jHH*Ve_HyG.zQ_]Xᬬ/xU6|D5]u"G:|iɖ3U\V5 Xf3HEg9re/_rk } m"XRU[VqOm9zf!1E@RjQ7گ"ܴcSۯfe~H' Xeywx<IŪ99_9sNoPWN=}{mp?`fsrלwyjTyk!Pw[H*\7+ˋzyś\b} 4[=J*9utC&ٶSI]ÆTIŒaTR9,>~u #Xе ?[=IūJ%yGGFF Ha$Kz$&ʕK… ^v*UJڎw^2em5UX¶ lERH*/ճVϕĤ" N>Ȩk׶H{~zlc;yBR4޽ߵmwvlERH*S8s%=<NT!U{m9YJn]FN;_^U$bɬ,/_z.I}F-[7 VdIٲeE|EٳgyU[gϑ#?VZ.^عsSYNcnH*IŒzGTgAvdڵYӦ5}||6l:th,BQRqӵi_Ȭq.>a€GH˺uJzPҲpHۭdɒJ /-ڽ.=7fLoGkv2X[mϞm2exjۨQ2e;6ٳfmy/;9bvT,zGKzR1:ujגLۇ-a<*1孷aWlΘ1bw GLФI 5ĉPeWw%R̫rfl%k۶Pw[%JZmf'{qc!rFeGjι2@q`?vGGQT%$%棏ZdTZR1n_EfMƺua=%:BC[LO>W]p#F|󍷙eފTHXYIʞ=k"`m 9F:̙3<*>#y]4_9* w@RbYsٲ1r{䯌6,]^xINݙ3+Y1$=HR Wմiٮ`ۤwFE-Cresժeս{1cz'%8쎎"p$KH*vJR9{v]ƌߥӧoB; T׹se jU0/+[.߉rqH\+h;RyN&y~~~Ad/YZ.'M25e.ks1GERH*T얕"UZ\r|݇G 8ׯ"k9͛UעΝ{[n_ڥKYPN\.u1d+ڴyMH^m#p5lwQvWom4Mvd‘Nݭۛj9z0 b InU|Emk,/]լ9`@guwډ 敖6~3YQn%___i̞=СvO?]OeEJ=%2;ي* ڽ>}:!CzuњZȰ/hjU_ԠAUUWj;l^Ǯ ӎ"p$KH*)T2۷ʕͶ]Iص2:>Сe\}&Imڽ;X57:ZIS'N$ 1cvH*I ERH*T(. w@RBypT%$ʃT,!P\$b I"p$KH*I; XBRGy ِT,!@WI 4g3+ׯ\;b IV5u '$KH*uxe[{BR;?z;T,!=c־Co=%$ubɂb I~[wb I\<37O+AR'6z+nT,!S[w? 5d$KH*p.j3W$#XBRsV H2%$8ظC$ I ψ}V@ҐT,! QW^օG@T,! )"s H%$$Aj_=Q$b IIi?[!XBRAŬ۾ɦ7S$KH*HF/\"XBRA_Z pb IIZCcpb Iwolj pb Iwsֺtpb Iwkk?No8@Ruwe`I >|`I ѐK!XBRY|#7$KH*7&-X]C`b IzٱW܉b I쯁DR{vęyj-pI w~1VoT,!sbb I}Ǟ[T,!㫷-*ƍZDۑT,!ƸuT,!+WVn_>;+S;T,!^y7>OψT`FRIȑ3/Uw#GZ3ɦ<b ١y橹1k"]4@_'T,aR [KEAjaAF|'T,aRE]Ti`J\ )/O*I&Xw]欮b O*0#X¤dzxAsFR I&$C~*O*0#X1ʕgn\^ m;ʙs/ H*xLR?EEY㫶`, XYIe!B(5lIHv$K<,ȟmE[@RI EQF2o𨐐Ș ]"XBR((Tf>((HJDDDtt.T,!PeJ*S{  %%$2ʔ)SCBB"##_6wb I(H*@J XBR(( H*T(2%$")b I(H*@J XBRqT|֜9Cm۝wmc~ض݅ug;޶rI IH $K0._ROv+S=zmnծ]JR5jXm=W%es ,رՕ-[}˗wu7""D> ]Y zuKY{ c%ɓQ͙MXbY-m;N?UIH $K0|EG5{mQN∓.կ_=ۨۓ&}ٻFWO?6c"6…#.y2=.IZ_,hdɒ@8T[$ %T,"t),ӌ~z+=h 8KfF=]cw)k2&3f殄`jqꭕ˲-[֏OҲg*sof~dSoÓC{j탚ϜYc˖}psWOONdĉP??hk{'/X2rܛW U tI<9Ȳec.0Ѥd]._ƎgR8q@ݺr^@yB n>Ii]4:* H*x[Riu έҥL CA{*TxNz8K[LÇׯUcVnݤR4atȑMH׮͚6)kްajk˒%Y\!ЬY-DYum۠g653lTn|d=:z1bwo%;4(^ro% mrHemڼ6~|}}7n?DH+Gn#$ɰ;Jǎu{s!oYۼ !x޼e4:* H*xUR9>Lb̎r[E5P"oUWݽreWqI^!yBMWR%JZmclInrСeK<ѵe͚y۶JU[ ldPH̙3Tm̔3f|s sHv˖}Vnϟ{ܸ~52e(kժ9߄yG|tB_=q"TuLJRqiO>m;$^zlC*Ę?$x;&f2|X.^(IVU|Ҫ… ΦFGERRIJ*Ɣ)_Kײec_8\bު;vl_UT=$$Ͷ*&\ȑ&c52SFE-ڪzeRWs̟?BƼyrWce|&̫U3q^xG ?)- vHGmm8,WlZL|wo5fLoSR9%$ LY|qOU=k/'|$Wrhч[w [(skC { T+RM];&ΡCdU̍2opYjҤ/`m\äJH-[֗3F")b$ߗ Q'|+.}/>ftMd&ի_?8 cz &/Z4JeN*I\mq|© YCٲϪk3_g5k&ʔ/dXؔ'Wɲ<@,gs=!uNrs;لׯ>yPٙԩ7kذܐ6.uiHv9,VMPаxaٴivd‘j+ݺti i/P D@ERRII*ŋ?a~RUg?CtkN,X0ܽl2d[KYf6Qpi9YZ1T-6VCJ6ڠAU6zt/YOQK(QTN%ޓTR2 ۷ʕwեU\ܦݻ_.\b$em[7nzjۮdصӶd-[=… )WypΝ[?:-'YD_&+u\vIH $KH*EERRI EQFT@RBQQ$ %T,!PeIH $KH*EERRI EQFT@RBQQ$ %T,!PeIH $KH*EERRI EQFT@RÒʮaCO-EQVDRIK*EY/ H*xJRrU[U>0c뗯qH*T*zQ<I \e mN!XBRykV8$KH*pw?OoCRW!XBRDz+x%$[T,!Uy b Irl!XBR\9snNz+x%$ЬU]g!XBR -xGV,$KH*pڞܴCoBRZ'kV,$KH*p_<I \hwn})H*TBPu;jb Ii }/ kGnonWb7<I R}AjopWѐ͡ջ\piQ7n@RT@KgI6/-#fC#T,! _xYY_ Wkugg:;ˋ3OERwbßsrU޲R|ˍ% XBRKl\5'5ӷܞ1)H*TWΜ此"o`$KH*p3WR{@> $KH*pߕgqAzx%$4Bz^_6+ˋыvyX]9sN$KH*iLu*U[*AR$kcTڪ]ÆT/GR&"^|6*mH*#XBRIH*iTR7xTHHHxxxdddLLH*TJ-Tf>((HJDDDttH*TJ-Tx4%$4vH*SL <IJ@RIER@R&TnTT,! $[$$KH*iI%IIJ@RIER@R&UR9>]hǞ?d)]4kذڤI_ڭ <ʸ|ǎ/nj۞rrK$$KH*iBJDD@2O$H>ݓO>j(Wb Ej׮XJIu{Ŗ-ԩmۖLsߧʷ~#Ε+ǯheʔGmSU\ٶK+ %$4!)IeV3ȑm/+W\&oRׯCϞmѣ{Iسg0urO<Եk3uĉP/E?ujR+JLLviER@R&$%˒BCvK*ׯoFU_U{.5vl̙3:̶Nի'v1)I'ػy挋d;\$$KH*iBI|}}%v2'ԭ[)g p`پ}f&5e˒'ONN۷oԲe}1t'xS&ԩY3g۱c"/ -F[Vn$uWm%(hXɒd~z,ԥK7w +!CvZøq}XBubcת.aŲ?n\$$KH*iBIeÆ2kͶK9tظ[7g曵e)qU `1o ̟?I͛mԨ|׳g{w&)A"y7ΟիQյk[V-s}ٌ?d+9rd˕+G׮͚6)k:th,ސVtM*Y3.\U^VA~}kLt6"gNc?#ľtAJUmTjҪ/RK6lbX-Q%,mg7nsy?3ϹΜ9{~TARsʖ-vq !Cuv:v_nJe5{„1W~#΅ یi޽ 9Ξ鉗.y+WN4mJ_`GR}.:q̧ψ ҳg0J6%ʕ]S{oRayVj;6eL!"H*\xnRٵkm6i-(Ul4spg'&ҷCnժ:ŦBukԨh811{LFpF:xv7NoÆMG6fÇ >& 9=ʃ 9"P5iRqwe ITARsJDm}v2ʸgh޼ArF-/ĸISͩ_5߱شTd+^}x"#}iBi2G޼yZRbiT :9"PUVkWvL!"H*\xnRnw=k!mGI?RT^N ִTOwߤnǏC)SLj,kI%1c#n I* Ԧtbȡ' (…&?h/[ ӯ^paʕ4jܳLС}}o߈nÐÝEDPB2IIG,6Mf0K*TmKM?$X_~پ(Lh8 R`d-PlAR~͚)ժUc L:̈́ _rE sCB4\BR$ET 'PY2wo'ڳhܹ9txڏk׮/_>[^MOۼ\mL6,ĉLhk/Tq]關MٳQBiC{кRDWO ZK*c(zA($?E'kABR$ET 3Ht&[WlÆm8*ffiVH 2|H*oY!Eم+C_z#i)yo[_ӼTIE$.d+nI߁h۶i͘-~>,z6i!"H*\(ԩΝ_& 6d\)$@RQI %SH* T-$@RQI H* ($~ ITAR H* pIBR$ETo!"H*\@R᷐TIE$.HI҂y*Z5$CRQI RRA[H*z+ bJ5kɖIsi\m=ݼ/Lm\;888**]`"H*ܡ{zzҶQ [Jشx9]XXXtt4 4$ETB] ڹ/^vBEFk.* IE$VG.Z ֋V.:::!!]T"H*ܡn.ö]zѪQLIMMe h fB@RQI7]! (Z2藫kv]! (ZK \.TAR-87e.TAR-4]! (ZpugYl@H* \wc"H*1;tIE$Ђg4IE$Ђ uz]! (Z:]! (ZУx $ET@  lH* Fl/D ?$ET@#|uIaCRQI4"^N]!(lhSlH* FhlH* F=f ?$ET@#BGt+"H*g',0s"H*l:=v1"H*+2IE$Ј[vv hDgCRQI4"ș=fCRQI4"UlH* F(֜IE$ ͡!#pIE$Ў[?v8 hO.Q1lsH* vp 9$ET@;=v8 hN}]!(qI `CRQI+]!(qzs7]!(qasV]!(qwSgCRQI#r.TAR5w7@ H* v8wƻ7o [H* v:`vk"H*LvxAF…eڤ%$Pn!((92ڳ\gd>NIWn-#=vׇ=OH* @8g}zm3^>9wuO_ '$ET w3QR}E?a (sJ/]q{TAR-81t-髑ݡUBRQI ->i[VyT?Ə~@$H* F\+@c l:"H*;͡!>  $ mZi!!+->]i"H*\]MzJʮ4IEW$HIyw\QԥTIE$.HIv8Jg+ ~ ($JJ*^ű@RQI H*TLIa%,,,::]u"H*\@Ri/...QQQ(TAR`eL*7ntww gWD T+$]ARQI H* ($ I@WTAR`+H* pIEBR$EThѸ~/d! "H*\T>~, 9֪UmQhZQzv5iRa I@WTAR̤B;+TZf]ʖ-'O]2M*& 712ki?y_;+H* pA~R_t{TM9&33]C1oZiRtCKxz.)(MGOiR1mŋR,[?oCVc32y&s5! V6=3vRvVJʱMfԄ˗O0?i7rs#۷K/U4| I@WTARbi1I% VWd~jժЍ={VĉLO={vXrT74ҶM*`iz5! V9^ h޼ArFlM*6BR$ETI%2җ;KwжmCCD;o;65T M0XI*6Z֭oiX;k!^~῟ !IXH* r6P995)U?8KC/.5_2s52Ξu?rdÆ Y;|UPB&|,>Ťbc/#讟ߒC[;t{i#zb-?O+H* pAfRiw}ӼߦG4dڹ}{OKnbE8ٹm|hHBүӖ?p`Y@~0Jj׮.Y^M)pXl2e!Lm#eSIwu mջ= %ΝҥSΜq\._7X{ I@WTAR̤JH8D^ Y?2JM=~劷R%'ߺ9b)a,>qc?VoPޢ&3ܘpF: -$ET@kN ғp IE$Кo^c+"H*5y+"H*5a?.4O -$ET@kΌ_7]n!(Zsv 3ֲ]n!(Zs~sSV]n!(Zsg',gBRQIM-aBRQIѿ]n!(Z-t< -$ET@k"o; -$ET@k <p IE$К>OgBRQIڟ;v hu=&]n!(Zss۾bBRQI֎aBRQI&Ql[H* }|l[H*  -$ET@kd0"H*5w -$ET@k^ՠ/"H*5g#}v hMQ.TARIrnl[H* $\2Nv{v;H* \WPvJt܎*N!Ө.wTAR-HYV.^Syh (qZEJ~SjDP (p!='GS<5NQ\ARQI#[ 5^C#2m?n (q;਻ccr[nQ=7H* D_RyUpIE$ДЗ[we8 hJfm[>}kmt"H*5g~,C IE$=H{x'(kCZ$uȂ>j|*Nek $E\Tt@J*λ` ؀HIT*}H*`"H*zbג׬%!!!qqq!(H*v-)l4ӓJXXXtt4cH* ]KJ*Ů"H*zb2&7k: صT6$ETIŮ!(H*v-$ IE$=@Rk!mH* ] IlCRQIEO* ؆d+\w4Y5jҤy~պ3SH*`"H*z2yֽ{k 144cƷ6uIQ[tKO2~Z7Hyz.&n1Q[7C%KP ]óI%%ذaK*>jWNu,NE, ؆O*v)VHRґÝ)ڵ8DQN^^ ׮c6O[֢t1c+^(Eou64t&P mĈzhG\o~#cQXp̤I˷l϶cq .bT6$ET@fRINЯߧtرMS0VTYS,6!:z7mݻ' &[v͜4,ŋCGC8zto_^ؾ\ĴT6$ET@fRټy&m˗OrrENJ:"N>FVc?4憽{WEQiR1~#Υ#-p'%ئM3wvrj"==c |)^P+Vzc>XH*`"H*z 3m0gpsS~-jFӘo̘f&͛7(Wѽ-ڳgLV(ů^X+$ IE$=Tnؙ/_q]/թS[Q|1="E^NԿ{7ZӘ_|=Imkgߧܤ嵐O3"o1yS޼y;vlNE ؆I*f¼_vkj֞:|UP&)Ŧҿg_ϫV$7S!=cK)zgݏٰatױ6VH*`"H*z 'ԭzv<M;z)zjzz.0dmM6m>j6mF}հ۴<>}aRR:uzvʟ߁"E鰥K3ǘ^*1]x:@ Y)XBRېTAR9IEN%'ߺKNb/1](:QTOcKM=~7Xŋ ؆TRy4ipϞ/i/˕+UZǏC㺐T6$ET 3{h;o|Qa>m~ !(TPH*`"H*zbBRېTAR$ ؆ صT6$ETIŮ!(H*v-$ IE$=@Rk!mH* ] IlCRQIErip^v,KJ/, :?i+ i'Vo+nvx鹽خk;ɎzI酥bJjj*!(9vQxQ;2B$UƐyeޯt|$ET@xUh9v@]zhl/"H*|>iv@;Q1wKO|Ȏ4$ET@'/RL(7wd9'3a"\;TARp1ʫ'فp1;nNNJZQ k_|m?;C}6kv<+cv ' (/ѝ~5/qgrNp ]w]HOJ r®w$]Ŏ( dWZBr@积arԱSULҭ^ګ$ET [2҃ڌv.;N ғٽ >v>=v!=xQH* @;gc @N 9/|۵8 l:$%r"H* 4:#E?=vŹٮ: Q@!(2EW{jv>N\qaZ;Ns01@RQI;sa: +lW])1q͆dlH* sş;<=fU!=OK}^@$H* mq|ktW;`gBGc$ e\y/IE$!qZ`g',g*ϐnIՠ/!$ETg:.WRXߩl7W5l@RQI%~v@]wv5_Gme@RQI,:p賱lW.DLQCIE$0r+bJ~Ygrd 026 쀺;7lWc"o} $ET:rƻhM9Cj}BQCIE$0uQՠ/դ?rÛw1$T(Bgs;de0\iGeۻC{H* $o\~5]m=pN禬2dfccH* $too{ҙjޣ"H*@}TL oO1ztWl̳{i"H*Sܑ3@شTkˏ#.ARQIE 6#O[s[ƣ[ 7e\yx:N7~1YƍG<Ȍ/9ґG1vg͚գG-Zƌ҉eMi0>~ƥcRRR~goog+TAR9G`! ͽԐ閯!MfZzyw{v{kVZ5ggMVT)O<Db0`x:`ɒ%݋/ҹ%K9ryʕ+K'֨QO>)U2dE(PwΓ]RtW_}+ƚ (gyoh{ƴӳ]pU5;Y$--mĈ#fϞm>,o߾D.\0ѣ'Y'SfM qqqY .㉶/%%*Tɓc6$ETtː+le4ãXK9Bκ=o"~J*Y5M*t62&ҵk ? +XܥKJ)BxGeG]]]U L<믔W^y:udL*k׮~C'VXQzK yyt{ʔ)ӟ{%J˗/g&$ET`l6"W1>պ<~Ku;|w'+VO鶇qؼym۶{SzW_jՊn6쉌K;vРA^^^0X/q>wZ_$`BHO:e̞G0Yݻ%Jpvv8jT .ܺϞ1} 2?~aɒ%>|K7.##cǎtCGH* ͣyox;=;*퀣Or@^TIDAT###O>RfߺuK:FR!SN}zў={ƌ#p<,ZcחN߰aÓ]Jݟ>TRΝ3^l@RQIEorflWv~%+CQ+)[o 6GbR1Nff… 7ѡCSNI'2I^z7/rͥ6.e<;w*U؀<_˯m>=!;BRQIE'N uzbmts>+i'g#1"H*zxW's-?]xIKя!(GO7M[u…H!ge^+$ET7>7g<8|Eu=zg^fGIE$pG8=fѥy]J{툠VQQ>$ETv{u{>1N?ĐynJ?;| CRQIE`AmFp3+^`.-pa@TARKޯtLKgqaڳW]xۻ?菏"H* 1 sqΦ3?-e%qډa">@TARR[ۧ'\%쇅lL\U-h "H*B:9|?7 _:7 zc?5 ç9t ("w㷗i#F~bl fRv5{bد}ʽ l29 r "H*⹼h)lC|vVÃ`>Jfc-/_@H* g=c]] VP49e?η٣  ("{什; 3(6LwCΤ6TAR̉!-ܶp.f0m{}R"oswlQEZ|{IE$'<}/G 2F?[7=TH"2r+QlLK?q}I+4-G֢= jARQIE$͆F{dܺv#.3hTn ƻǫ$ET{{֙nx"lL}|[{N~[F̸% $ET[ߔ|ե{gmt+5G{7 gCIE$a[v]>`Ky+ǡXl^{[H* !=9e[V2c4vuIJ*~m .DRQnnͧN~lWg;yTARñS,`{vy׻}خ.3H=Κ5+000$$$<<<..=YH* Y]ʭXvs"vݓ vH=N4ӓJXXXtt4{гTAR~o|vwmgKJ*Yn߾͎hذaѢE]\\xɓv ݹsh;888**=YH*  b.o^ÇgLԫWٸq#;R텢"-\LL ;&cRW=000<<=YH* XߩWW{]%][+%]%nݺI^ɒ%SRRիϟև$TAR.D]=~lWTܹS@ +Shʛ6mbTꫯ(QE/^K6mʕ+WX1lDDԿvȑ#ԩSHZjھ˗׭[w̙>}]/ZHMM}wM{'!Ќn߽{wzzݻw:;;)S8gΜ{F7JGzxx 6bŊM6/<,k/{AWVرcTARQIwl';U;]]OR;w.t߾}Yҍ͛GI*Zԩݨ^zff&kٰ֬aqQ~.][lIw?~}k֬}S3[oE(Z݁~O@@޲e˓?4#[3/[pazUV͓Eґ;hFE|tE;::Ջ6$tlW¼*v`RV5j 'NH۞=t>pvpp7K/]jJ?~Ozi?`b/Ynϟeŋ\wߵhѢf͚,Tt:E i[};6С [n˗뭷nDDD՛4i"6ӭM_ 6 HobY"vZ}mi_yLrr6$ps xrخ.$OLUT)=== xѢEt;o޼L۷[G?~Oy(dH2tP1}\ɂ Hgggzz}QgϞ$kC3^tk}jt`iiOŤ"i^իWSH"R*BRIE$y(+ivuII%!!Mͪ mu޽;@%f͢fƍ˗dmҦzC =4!k1wyHm{̙'hӡ' N7Pwܡ'%0~vRDDDH'OuIwT!(5CFBM2?x$n/ݚʊ+^T#7?SӦt[7lؐ+WnȐ!SNܹmFҥK;_Qڤy睑#G=zȓ^QaZRJ,Aw_z%i4OQ۠Aj%K,Qt siRk/ZhV/u#CRQIkIWoT7ēd+KzH*Ɯj͓?6<&|mgggfI1촴O>$_|tQF5nܘԩC[/c=tٳ6G5!iӦ]i/\m*[?1{Ν;רQ#:Yf$%=3G&'_;vHqB bÅ6$u,+ćJd#hHLL4m޹sGz'2Ȉ|뼀xhS=Z- ?UʕكHj6$]ca]Q'xovu IrJ.]k7ߔ~X@IEmH*\ a]Q'xmvu Irw}׬Y~RkA>$!p-DŽ뮻ٮ(2RRivu I@;TԆµNbsKƣ[ vECR$!p-خ(2S>uI@;TԆµ]EfZlWTIEmH*\=1!=-#KH*ځ6$dtshvu I@;TԆ5-ғMT1 nTBR$!pF.GCSH*jN5O WRHyoIEm & ?A8J*9uv@H*jN5O QR!Wyp!=ڰpM~2TȁߟIEm & ?AK*)1q^; IEm & ?AK*^7ZڰpM~2TSBGcCRQv |OP&NJڃDW:}ڰpM~2qT?{C_Dv[H*jN5O oR!s"ڰpM~2qT2=|}nvOH*jN5O uR!O^!V,;!$a''e= 3o7R? :6\~L$CF枆×ARQv |OP& Irӳ|K ;ׄ_>'(ID,߶5$a''e&NMYv6\~L"%'g86\~L"%rc_/2>bx6\~L%rԓ] ;ׄ_>'(xI%->η6\~L%r'(N%چ6\~LB&6#?]mCRQv |OP&QJfu{^s';aH*jN5O $jR!.{Uh?V! ;ׄ_>'(IƠV H*jN5O $vR1dfm2v@TԆk/IB#=˷?$a''e>ȵ޻8$a''eCR!>{fl@cTԆk/I'I%5*NqNZ6\~L:I*$o@RQv |OP&$rbȬg]@RQv |OP&]%׻dGH*jN5O ʤB=ӣ6\~LzK*ܤ;f6\~L:L*iғmH*jN5O ʤäB/],>1;TԆk/II/ݺ v ;ׄ_>'(n@O[rڰpM~27vԅ6\~LH*̴]f;"$a''eBR1J;ʫBZTԆk/ IT=6dd@RQv |OP&$gH\;\ IEm & ?AT)1q;*; `ڌ?azNTԆk/ IAמ2d^TԆk/ IŢ:[أH= ! ;ׄ_>'(E}_sn @_-+=كrڰpM~2!Xw̎N܏XZuӐTԆk/ IT'/f>`!~O. TԆk/ IŔ!=MJY𢡄./rYU;ս@#h6\~LH*撮ӃB(l/5G[4uab P6\~LH*\]嵵H3<їTTԆk/ Iņ]ǹh,%V#rڰpM~2!<_-_C 16\~LK*iQ=qTMn:n>JOb TԆk/~Iv/&(=TP+IEm x& ?AT.-w'%p]Z0I% ;ׄ_>'( d)>W$6\~LH*(%%YKh[ 5..jyTԆk/ Ie\OOOY¢ٯIEm & ?ATP KJ*~5'(_Izn6߼o{H'hѸ~[TԆk/I#Io|i,8fL_!W|6jTϼ[TԆk/I#Ie!y9n>j,;!W*UQ_pOuޚ ͘Դv0sO>xpgG:5nitO>5 _רQ!_ժVhx6[7C%KP ]xŹXlZ{XH*9IEm & ?ATRX#ӓٵkqhns6|F;!Ӣ}p႕*sm\wzVMws[te>y7x(UHwbiZtJʱMfDz\,6=C[+$6\~LTڶmƍ3hh׮et{Æƃ;!Hg :g+WNqmNllT>o޼QQ~oe>y1}Ch޼ArFlyLl.֞ŃJBRQv |OPM*7n̗/߸q]ԩ-(hbaG#G~)|f@Ҏnc)&RDG<ѱcS#8٫=wllT9\vmI K擿uk;o0}C{y-Z~t06D*ӹXlZ{6 I%! ;ׄ_>'(S&YFn|TqR^ݲJ35kTV%;Om ySzǏp#mdf("§}F*~e`4^y%N)%P_2sC?F뻘31\,6=Ck[,$6\~LT}]FLc=yFCUX(QtɅ $6LM7~Ǵxx.;й 䗾C{s)!Q^R&v~gtNtq* ;7Ö.oq.֞-JBRQv |OPM*r*#ddoZډl =RS_MkLN#`etO>)HTaȊt,b`Z;)$6\~LO*(JBRQv |OP&$BRAH*jN5O ʄRXH*9IEm & ?ATP I%! ;ׄ_>'( Ja! $a''eBRA),$6\~LH*(TԆk/ ITrڰpM~2!JBRQv |OP&$BRAH*jN5O dri'(JZ|RPf-2i.mf:-śmvy_ڸ|wpppTT ;ׄ_>'()?\OOO6KO7MvFKKL ~'(:Iߖ;1 ::::!! yTԆk/IBȦ ,***\4x:-.-1-4ŔT+ARQv |OP&u\d '(l&$a''eBR>Mc6! ;ׄ_>'(xPo.MH*jN5O ʄb' j}vlBRQv |OP&$;IOJ(ڜ؄6\~LH*T.uH*jN5O ʄb?;?vCRQv |OP&$9#l:$a''eBRA\]vCRQv |OP&$9?u+.uH*jN5O ʄb?kvCRQv |OP&$#.uH*jN5O ʄb? "k;]TԆk/ I~=`! ;ׄ_>'(]m+2->X6\~LH*v_G9|ȅ6\~LH*vȘl $a''eBRg\]v@RQv |OP&$:7eIEm & ?ATjg]+TԆk/ IŮbvo7X6\~LH*v.b[_]+TԆk/ IŮ$n+يX6\~LH*QyZB2IEm & ?ATM l$a''eBR6#n>v,ARQv |OP&${;zZo ` ڰpM~2!۹I+O[v,ARQv |OP&${XĐYl$a''eBRhF]KTԆk/ IIEm & ?ATeڰ]KTԆk/ IE[ 7xARQv |OP&$-106\~LH**b؝}'.$a''eBRQ^m7A`ڰpM~2!CzC,rzܒh? ]#$a''eBR0abIcl/],BRQv |OP&$;I^nnw8tҽ@CdȂ6\~LH*s~*2 rbS??ԣhmZ0Y0BRQv |OP&$1g.K^kޏ<~$a''eBRg7rw-χEzo H*jN5O ʄbo;/6|_ H*jN5O ʄboiI[ 5G؀6\~LH**[ޏjmH*jN5O ʄ]?Ə~6$a''eGRI{x'(eZWxmw|%UZ|?H*jN5O dB J~eIEm x& ?AT.-w'zn]Z0IE6\~LK* P?\T$H*jN5O ʄ׬%1~UڰpM~2!re\OOOڛ¢ٯ*}@RQv |OP&$TT6;888**$a''eBRAzYTH*jN5O ʄBR1BRQv |OP&$TڰpM~2!rTTԆk/ I녤b6\~LH*\/$#$I;+Wbbbر˩LoOP&-$3gܣOΑ 8|g\~(w=mo޷Xv}m_TT&u1b;}9u_d7n>x9s\ptӧOҥӄ LsiӦdffQoOPM*aa Λ7o,;ƫP6jTv5iR_}=b^NiLU.U1}+g_[兤b6-'Yn^{ZjM6T} <8558ZF:(QISx7'Nc*Tbz'|RT):kȐ!R_˄ȅLTBC] /^tݺ׮Sj;wԇ֕Fsv74iqK)GD+e^TĀ6M%`zQF3Q0`t;--nfϞm4hݠBERSN]"##/{'=̚5k,YR:L˄ȅLTtiA|`i7 2?R~S3}sz>/Pz&f L!!͘TbŊ޽{ћ7ovڵtݻʃ1|GӧO|}}6mXj˗/Kk׮k׮PBժUرc ,Xx7xc\٤$+[JR fխ[7Ydȑ$nn,YB2S 5s[-\LI6uˉn̟cioY}F_|xթSycx?rP'%ذaK*>jWƷΜ9s9g-[ܹe8[OWvSS1TL:*""bҥ{qˈrjRPN%!awʔ]pVSr 8Kv bnjH*umғS;TkԡA$ΗKgː*S+Vr%o4& g:wDߩ\/Ctn'{ 88xq*_~%5c…V>ݤ$Oyk<#N{*sQSyǕTNcpg5_?)i6ũ|l. _3{;~{ʳfc̻S{R9{ Np*6p*T222TҸq}ݻAv[F.n:.]j۶-#!g.\@é*U߿?%ԩSZZ)߳gϪUP*:w|AEʖ-{Q{d_BCCw&L0~_xp=Q\/ Ԥ ȾۂnIM/LWhGh&T킂OLT~m|ݺ)rT畭PCJ'}&ʴg7_=rdUgڵX&T޹Eө|>[OWvp*6p*;o/Kڵ+y{deJ.mvř3g*WLf"jرcV>999""VRJXIII7|sppeo +!sIQso+V$/E&vON*(2dcƌv= \|SX]WYu駕-Ww]t۶ ~UڴiHۃuu]8]my5Sҥe$=PIaO}u65d?r گ_cc9BzN9%P׮7lx?*ꭸIgɗ{ =Mvp*6p*Q'N8ӧ~ߖw8DCil>ֈ/Pt*o9TzSK+%j֬ҿ#P˖5mm.88įC+/j(SS1 5'@MTp*6p*F| NN4Pֈo5SAxJ_&p*88@ X#} NQbb(kķO| jrIHا\XJ_&Ω )T.é'J_&©dI?aM_6BEe[~P׼Ƃ㓒է?S1 5'@MSq~ő-&aϏ W~ =$gϞITL%`/PkToN?mr,31=<4P1УB isjj$p*F|ɵv*s^"'%%}ڷf,c!G4ITL%`/PkT1 Orr۸mij֏z`ȦdddOb(kķO|\ktOYJ_&p*d~ i>jR~n YJ_&p*Vn}b~ i>jR:=,cTL%`/P8$aMs2J_&p*˚]|}@N4Pֈo5S)X85 8@ X#} N`n/OS_S1 5'@MT _Voz i>jR?r|y,WTL%`/P875 8@ X#} N6p*F| JIS\N4Pֈo5S)p[,KTL%`/P8Ԯj=f_b(kķO|89SR:8@ X#} NX{G{"HS1 5'@MT|-E&/Xfb(kķO|ߏ_)jp*F| /plݖĖ,?TL%`/P8_/qeۨYJ_&p*>rm.YgJ_&p*>BbAMRπS1 5'@MT|=/M ,3TL%`/P8!yᚤ#,3TL%`/P8M?N4Pֈo5Sr.e.k}: 8@ X#} NwXWS;WS1 5'@MT|vjp*F| Gf?b(kķO|>yjp*F| pe[YOJ_&p*>Ŋ9fb(kķO|jS:q,TL%`/P8GkPoS1 5'@MT|3_XS~i>jSdf]2J_&p*ƺ:=i>jkl}bjp*F| J_&p*Ư}~#;yN4Pֈo5S5\eλ@0p*F| :?J_&p*O:fnh=5i㷭,+"3J_&p*)q[AΥL;dw^}b-7>:N4Pֈo5S5UvemR[c7"6a 8@ X#} NŧjkY|:b(kķO| Z s 8@ X#} ĤS<}x.U稄aM:Џ3#J_&& @ذK}}8DC 8#} ļS?yhITPֈo51THHĵ_?SZ@ X#} N!/,|tR۷اOb(kķO| TEGbgϞqITL%`/P8aoFEE'%%%''Ob(kķO| TGS1 5'@MTN%`/P8S@ X#} N!/T.Pֈo5SA 8 5'@MTN%`/PaNeԡ ygtD|FR҂57uQca| /-tF|9K6sD 7cmhqoÆ\P.:d|{czh7m+ u0qMߺ[_+é]>jkND릛*L4}&ta˗:9"#.X;Xi:_b{}ٻ[sˮr%N%`/Pt*uަk絮$((Nʧ&֬Y%*-kO=zeŭ4TTK!!n;#cۈRRrf/ˇuW|tqq::f'N|lRt3W.ÞiR:rUK6aÞ̅ [[d,+k5SXu͞YMEukF+V,Ӆy纮R¹e+ӹs :,ݕx 8 5'@M|ЩԮ]' |'|ZU;t[O;ì]gs۷ FGoN:ĭx:㏷_b yu޵ 9sFlݺ>Edy#cl2cvgAgT.''ԻËv-ZBdSPV 妛ٗόíz+,yW_ݼ=9o4n7PvѫVMf:d/ QJxjm#Y|2ZJ< J_&>T A6 %.RHݽ{[+^/'Vf$OIzAoi5ѣ t%c^yӟ 3IIy4rw/v}Ş^؟uWur<ֶ]Utm(ȟ5mz׹sIΓ+p*yJ_&>TH+p,]sh˹0̟?^٭x_M9srݺw)9{vrE=9[T^9jwo;kH+9կbGiS_8\=ST(bb`leT\SPֈo5esڬԮ]/s{M!7UY*G&ɃWO>MOLǶj@9E=9Vl][[E]{$9r?Em߾BX(իQLIkS֛7LSѣ]PPɓ+p*@(kķO|S]cj֬B¼cǔׯSᱱo٧pPOrݬ=%K?^; !!6V\$Jkט6:; фe~^ؓS 9IB~s8KWHدݼy>f ~A?r9F/Pf^=$/F98K|ViӰ\9\S]>jkNQڅr)Q"_ʧ߬Vfʓ̯X16}Uketƍ/_ZjΜQE@̙] aaE_=vl}۶'QX_;`ukw?9$?Xwtc 4KwF42z\YR4DukcauV|߾v*/ zQ}TjԨl5V}t\S@ X#} ל8|/8G@<*3s$HonZ57[l_*-#cۏ?ۿe~n8z4"tov.5JS@ X#} ԄS}wo;m+w+S-pvi4 8 5'@MTćڵu׿GRR>qSA^ӟep*@(kķO|  J_&p*ytF| B^]>jp*@(kķO|  J_&p*ytF| B^]>jp*@(kķO|w*'M !A ]3F|y@ 8pu>jbҩdI?aM_6i B'G^bQQQIIIJ_&&3#8..d`QV ߞfWg={Ob(kķO|Sپ};׻@Ǽ9UE=i$#J_&T/MJJ h׾5CE=i)))iii#J_&T5.Grr@O:u<5 BO=cMPAN4Pֈo5)(ߩjN4Pֈo5Sa|l5 b(kķO|i1OP@p*F| }vKQjN4Pֈo5Sa/k6jN4Pֈo5Saɭ߮S1 5'@MTXpëvV@p*F| .j…UvJ?pDYJ_&p*\'۾OYJ_&p*\z𱄭jN4Pֈo5SBR?/DYJ_&p*\9]f,TL%`/P8.|=}oU@p*F| Od5 db(kķO|p!y^W@p*F| R,i>j…ԍ{?mWYJ_&p*\HwpMͮjN4Pֈo5Sc'Wx@YJ_&p*\ȹ[S1 5'@MT$<"+S1 5'@MTCf TL%`/P8Fޫ` i>jˆϛ 8N5 b(kķO|0bSW,\AJ =$C_~<2111nFFܼy9{qvz?oe_uXwy>8q"`rٳڻws\z}…Ç;O p*͟,W@p*/u>[o[nҥK&M*T@?#KĚCO=}ș3ghӭӠ e8F4;n{qjN4N6֩3~Ro;z-۩\?cm,p8Ȧ<3Vrԩ>h`` /8ի#;;G:uWҥKҥl' 8FYίY 8ӈ:وo'Bց~RX7tS?xm2w\A X|y/;~$rĉe֬YҥKN/_~לK(A3gq|N?ߙ|S1xyr*}-V BK,G#"".]g$9Xx1;j(gҳgOJvk޼9m>_ӧOPPp*8@p*/u>Nɓ%Jҥũ-ZE3֧?I˩\p˩:uH"Q{;uСC۷oO6nlSaAAFF|lҹsݻ7**jժe˖=z5NjS!FMOa5 Db(kķO|c}NnVJ_&p*YYJ_&p*cԡ֪Y 8@ X#} NOaZ"S1 5'@MTk=GJ_&p*NtΤ} b(kķO|Oh햞ٻ'6?c{N4Pֈo5SaOs;hmfj…mOv|ꪝOl[i%S[TL%`/P8.\jŽ]P[78@ X#} GJw!tE]:g'ǀS1 5'@M|ĩDEQʼn 'ǀS1M43'@M|ʩ>>)))99Y} }8@ X#} Na;}P J_&p*8(kķO| !T{>jSF| NJ_&p*8(kķO|=}ҥ]BLip*=Pֈo5T~1>((nC k^@\Znظq8(kķO|pt*F+W_źk*>|\#KS!2С\]SF| ;rJE_~٣4פ!J{?Бs}:nȪUo"+ӭ[Wpakj%KDz~_:$.nR:QgWtV-UD_{鐐`rn0m+7X~ذKY` u|nvjjb͚U޲v=woL-LzgB+B˱edlBuњwU=!a=V^5SsApS@ X#} ԄSav+w- +RRW_ݼ=4JZ{!Ztn!}gnRx8 3ty䑖tȖ- };ht !rN+-ZBdS`y9kժڡC}zza֮6En`s^b e6mWnu2vYjc\sд^3g[9n5LT=5µRO%`/P^Nܹ$-3&%+(IZŊe~yK/Ikz_iDDԳ!\>z45vWݻef q+f޽t. zr*V oԴ]t]+?yt+.ܹ{d" Zt.N mCnNJ_&ʇ9s/ E /u~Nѿ#AAOefт5Eba+Wr]-;{{=i{5O_PN\T Nj99?m4,Wz1]z:Ozh׮1M6;>ڵ5M[l5V+|/*up*=Pֈo5aT֭*%K&O#WH\ᄏ&euksk!R޽&A!X|˖5mm[IknA)SD9qs) :B]4tWBHkԨLjUdMNN^]nuJҭtx*رm6;I3 8%{˭VxYS#jȩ\5G&X_"#cۏ?]I23w]5ܞyAo>ҸΝKgRqMꗖ̇U֫jSJ@ X#} DSq /C>NQ歆SF|$2ѹssGYoj8(kķO|Hr*SF| NJ_&p*8(kķO| !T{>jSF| NJ_&p*8(kķO| !T{>jSNe (D=Zp* Pֈo5)@\ӀS*PΈo5y&Ć]V,7}Q$*hKbQQQIIISJ_&>Tqqq( W, =ThgϞTL%`/Pt*۷o7x%|, =ThF򟚚>y>i>jN~4KޤπWTQjx*zKIIIKKS<N4Pֈo5AB/vIH^YYǞ5jx*z#>y>i>jNhrЯnW3p*F| _6>j—-ExS1 5'@MT︟fǩY8@ X#} N/)jpN4Pֈo5S#f{sS1 5'@MTB63, i>j—ND5 8b(kķO|9˷ySJ_&p*|9*!H5 8b(kķO|/7vxIJ_&p*|-Y8@ X#} N/'^_jpN4Pֈo5S~\W15 8b(kķO|%#nfgTL%`/P8\%uEvjpN4Pֈo5SKfڹ5W3p*F| _Y1 , i>jšs2.Y8@ X#} N5ˮoyY5 b(kķO|&?r\J_&p*YGg5 b(kķO|&vW-p*F| k>m7u^5 b(kķO|fCY8@ X#} N5:=N4Pֈo5Sa͖}VJ_&p*oO,` i>jšOEjN4Pֈo5Sa#flS1 5>á|*;p*漯P-p*jߑ#G:vX*Ν;X=ѣ_z׮]O?:tuE[~(1bD*_išCclS1 5Ծh7k.+Cv/^|9: 4O<թXVR?Xd?j.iš˿/Y8@ XC/… |@III5ʜ9syϧ k}mC| 8@ XCȨRJƍ۷w vmTԩSZ:+oz/NEtO 8֤nY8@ Xc^(ڵkWw9x`k{ʔ)aaa6t`@@@RVXq.iš{NO5 b(kwĉ'O: ~5땼N<p*9Q-p*F| k.=R{5 b(kķO|&eZY8@ X#} N59YY8@ X#} N;!YjN4Pֈo5SҒ-2ϤY8@ X#} N;+nhw!%UJ_&p*Y]sG, i>ju{{1\S1 5'@MTi>k΀;p*F| w6mjN4Pֈo5SƇ]oY8@ X#} N;?G%$ufOTL%`/P8]/Y8@ X#} N;~=3Ιw#TL%`/P8vd=ENnf} ,Kw/TL%`/P8yijھoM6e-3o*`i>j‘sSʶ*/ڔ%aM?:N4Pֈo5Sa3S[^ ٔe[dž4aZ:N4Pֈo5Sa#+{cǕipc)WvCJ_&p*|9囥5'&r$ 8@ X#} N5;[\eVi<:N4Pֈo5SaMtmwTL%`/P8Z&6at-cp*F| TЇk 8@ X#} $Jw! $Z4ef!<J_&T'm ?Ć]꿊i>jNe &pJ_&T觶ñh8@ X#} NcXNed,oN"55Ui>j@1,(8{IIIQb(kķO| y ˩,fTTT|||RRRrr/D8@ X#} NcNE^xN4Pֈo5SA p*F| ǀSa 5'@MT< c>j@1T%`/P8"(kķO|Ps('2С\]Sa 5'@M;]+W /J}eZB\^(6kCG_Wt.ɫj;6[]x?V4%fmYSi]p*F|ar\ imZЩ=JQb^k_?C4{ZyCH֬yMs{qjj"kmi;襶[+.޽t. zr*V o.W֮Οnx:WtgG!tIt;Ij;NL$@% :Xn˧?SnKsxۀSa 5'@M ;?K9s/ /u~Lѿ#AAOefт)+Eba+Wr]-;{oڞ4ioW(+Px:=9OQ3*U*7ecJXWt0mLR"Ӛu'&O]|j7._: Tt. nN1Pֈo51TZjPȅ ްF0Q>4~{A/gi!՜E(!amϟ?^͓+'t. zr*V"&f|:&Xg%t+q HN^}睷You-6V}w[nuA%ҁO'T6T%`/PNᵁC:thw/d_ իWLއRR>!m߾r.g:{vShh-#Gs夞}ڵG)St=zsd#~%AC RwO>eMߺiO>JOLC@'8_թXI. 8@ X#} ĤS7n0#$I#%MpFHHo駕G'E1eWHH {c7oo"B͚Sd{u!w_;UV~./ שxطGSpTի?t+Qћn`iͿw'۵kLCӦc]e&.(t[. 8@ X#} ĤSZn$,@0a‹\ ݺ9~5)Shɘreն-"mv7n|Ҕ)Q"|ΜQEyRt̅ԱZi+X16}U/F֨Q%kժ7ɚnꔤ[X+TiF3{wرm6fhba'd:<q>= nN1Pֈo51T ܣGI Y鿮CndU;\n张CU\j%Y3)A86cRÚ^6߶^1Ps]{xp*F|o:2T0J_&>T"#z^|0:wn1wh<"ᛏ c>jSNp*F| ǀSa 5'@MT< c>j@1T%`/P8"(kķO| y 8@ X#} Nc0J_&p*DN1Pֈo5_@Uc(kķO|SA 6TX %/Pr*gOleqEM?blt@}$h,9;***>>>)))99Yi>j_NG?3̛@MO=={IIIQUb(kķO|\ }v K/.cESQ,cO?yT_p*F|ɵp*^V&%%}̰.!:f{zSRRzJ_&© Ja0oifY聧Ǟ~) =TL%`/PkTa_\fCTL%`/P8d"8@ X#} NEp*bS1 5'@MT"8@ X#} NEYP!p*F| Tb(kķO|NE p*F| Tb(kķO|7jTDb(kķO|#8@ X#} NE/.kfCTL%`/P8d_ȀSi>j"+Nh5 b(kķO| EY8@ X#} NE9p*BS1 5'@MTs)30TL%`/P8dfņ4R!p*F| Y1 ,`i>j"GvNLP5 b(kķO|"8@ X#} NEGt@}5 b(kķO|ϳ TL9>j"+8jpN4Pֈo5SAL`GN܀S1 5'@MTd 8@ X#} NE1 YjpN4Pֈo5S9,5 b(kķO| 6QΥL5 b(kķO| 6KjpN4Pֈo5SAlTb(kķO|`q2,i>j"EN4Pֈo5Sr܀S1 5'@MTd8)TL%`/P8, :wAnJ_&p*2XR,"+N=p*F| Tdb(kķO|NEp*F| Tdb(kķO|ߨi>j"8J_&p*2Si>j"8J_&p*LI>yߨu( p*F| SOĆ]K6iS1 5'@MTqT\ֶY-8R\Byl8@ X#} N/jšEܻb{z)im$8@ X#} N;Kknٔ: Xb(kķO|pWgXNeEv?XVJ_&p*ɸ҈2i[S1 5'@MT+1:b(kķO|p*OqdɧÚ:'y&]}&xb(kķO|p*Uw5c&J_&'F#*O>Pֈo5TH_?S(kķO| XTN}vTi>j`SYv\\={RRRp*F| uXNe7㓒p*F| uNE_}8@ X#} N:TT5'@MTNN%O@ X#} N:TT5'@MTNN%O@ X#} N:TT5'@MT <}ҥ]S^p#NN%O@ X#} ʏ?֭[u@p!CpͷhqoÆ\5Znظq gSSPֈo5C2jTB|UN%2С\>|]gNN%O@ X#} ߜJNήʕ+_~٣4פ!J{?q*VwM9'(;6[]y9DgmR 88<%`/Ps*sypCP>{h7{vdժ7֭MZƫ]Gh%?cYY/h7NҨ+xq+*U|үtHH|0mF]W\i{c:wnAoXFW*+U*7kH4.ˇuW0jTb$i[IDATh~͚Uzr"t{6TT5'@Mͩ0jzPI ,nM!Zt!}gnRx8ٗg#--[ZC}v"w2`4<9O'ׯӠA],еkk:ʓ!|sGX1^~o4:m+ֽ-88WE" `/l4nd褫VMS}AOm!nNN%O@ X#} įʹsI$Mzii=JYb^kuWuއ~e=_ikժQ>z͚wh:رWHMM$ҽ{[kZfONI$n 6Tֱ|uժݬ,bDZcW&t=y2Eǎu<]}ƫ.9<6TT5'@Mʩ|X*v$E7[PHPP_zY CXʕ+ڞ4iT .\8v.5*3v+U*7e[:վBsYa}46yl\'-rx(m (kķO|SiժA!,xUxGiԩ/ _mtv֬Vr]Yfc4}ʔ)=g^ɩPČSU)XW;rdsYnݻ4aH;s5oe+'T qp*p*yJ_&T^8thCX{@/EիAnPJ'7QΥ,gn <5tȺ btɴڼy;rt=Qi'O&:Cjk׮ȎWjO>MOLצs:ujnxpUf!;UXoQ=[d?v\HOLxjZF W :n:8TT5'@MTp0B7o JF| }=}}/CyTT5'@MTNN%O@ X#} N:TT5'@MTNN%O@ X#} N:TT5'@MTNN%O@ X#} N:TT5'@MTNN%O@ X#} N:TT5'@M$9&n!*p*%`/PIN&PΈo5T2Ϥذˊ/|oblt@}ł㓒p*F| p#Z'̛@Mnj:~Ϟ=)))38@ X#} DSپ};|ױ~¢hr*jϠvSө멩38@ X#} DS:))3?a]BtH#5gP4i>j"ϩKj-Xnj:lJFFLN4Pֈo5TGY8@ X#} N;Y.N4Pֈo5S$TL%`/P8HN4Pֈo5SNV%", i>j8IJ_&p*܁Si>j8IJ_&p*ܹZ8)J_&p*HN4Pֈo5SNÚY8@ X#} N;2Tb(kķO|pS)DJ_&p*ɾxiqjN4Pֈo5SNNTL%`/P8\ʌ- "8@ X#} N;9Y!, i>jGVvLpC5 xb(kķO|pǑ@J_&p*q©N4Pֈo5SaP_MJ_&p*,8kķO|{* S1 5'@MTfCTL%`/P85pdéHN4Pֈo5S@LpCGV S1 5'@MT@N%'3KJ_&p* is)SJ_&p* /'㒚 S1 5'@MT[qE8 J_&p*X\q 5 b(kķO|`q&p*2S1 5'@MTpũfCTL%`/P8,k "8@ X#} NEK#]P!p*F| JSi>j"81J_&p*Si>j"81J_&p*7jb(kķO|NE p*F| Tb(kķO|NE p*F| GҾOv޽7j?(`i>j‘?>a{|My/TL%`/P8d8WmVb 7ιy9צPF4N4Pֈo5Saʆ6-.2+!r2h{qzU>J_&p*L9{?=KO%&w,.8:~wT8@ X#} N/t\R,"tK›_Il9HXb(kķO|u؞feeZ~: N4Pֈo5Sa[6%&>nJ_&p*SYQVÀp*F| kr2.ņ49LkV7TL%`/P8lJL`oǼJ_&p*6O.,tqXӣq\|<2Ϥ={TL%`/P8N;3?yEq}}XXF5 H{oD ƒX5&vcQcG@*XQ5PT:L2z2̝y|f3wv;T,+*(>}fSL]9ח/biP FS4`*TGG\a*XTQ|"SXp5Uⰰ0*Ʊl>T LҠpӧ'h&0B0ݳ{yyQ1aJ`*k>?A3T,l7$$DѰP%0KJ5O⟠TD`* T̯*biP FS4LbS1LҠpӧ'h&0b A%ŧOOL`*"0L0KJ5O⟠TD`* 1`*k>?A3T,0cT, *(>}fS1T^8u{)BBӯg[+%n2vOS,߮59biP FS4m/d?ǘ;VVVu^j۷oֲeCaY|&OJ= :9}ڬY}vYLL0KJ5O⟠TD1s+zϝs0'[)֬N?*Ox8{ȩSe-9D&X A%ŧOOL`*"ʁkr%DFF8nK^֭?_^:ПHl69['?Y1S1LҠpӧ'h&0sL%<܅.רQ}CDŊ0˴׷̙MB֩u/[ChkW!#z z̕+U8q0ѱ̙0cp"P:BEʖ-9wyꛊIMYTLr[>NCԺS1LҠpӧ'h&0sLIu޽@azP^b#׭OҨQ Jo*gի5jo ԩԵ{o=i`Rq:Qٛa/hM~D뛊IYwYh z>>+ub A%ŧOOL`*"fJ\NZE+TbCTI)ptl"lݟPᡮ98+R~ǎtdBbcI (˫o*Z;ӳ+WU/_Ng0`*ƀXTQ|\ղ"bh3o믟 ^ @|RT)HbV( ΚPܺOB:vl.6-Ծ|dq$Bؑuǘe޼1[b+6L0KRl=['&"bq^;Aq6bǎ°,?x 7hluԾu*6uկ_kbTS4/uF;pp\?lc*̒|߅ n,9̍/ߜŘ'JO?wjY#놉Y9260cT,KQ|7{"۪>`*"f BzTS4/uF;c=/gSXTS4/uF ]VSX,`*ƀXŗ:e][VSX,`*ƀXŗ:e}TVSX,`*ƀXŗ:eŞxpU}TD`* 1`*FN(>}T¶LbS1L()ŧOQ/SX,`*ƀXŗ:en9ёslʀT,0cT,JQCҝlʀT,0cT,JQC4[O9mU0T/_JuAbQCS0jHKQ*"" bSbi\UPҧMKwLKxv H pً=wr`9mtqq h4l>T LҨ)H1V5S1U///dΖl#'奋L:"""&&́*X:][ܘl$,,.sv4e9./]dTcccQIS**IKQzjb*~HHH9jیmtRdz9P%0KRTԓ>]"VS1OSDʛvkr]^tISQOS$IߙaDrc[ULkmS4)uD=txU5T&UҞV-0KRHԓ>toqSᚴl+QOS$J_mg[Lk`* biTUꔇxVuSצRȑmS4*uC]j?U]T&LEQT,JP[.3|UT0`*FmNa-}-65.P:0( Q[S*L_萹Wne[Lk`* biTXꔄ ROI)lp LEaT, KPgumU40IKxQsp LҨ)u}5h32S0`*FN16}[ٗmU.0( QmSMߓ+>Ld; Lk biT[ꔁ2p9VS€X5:`ovJ5[ bi\wq3C籭J50%S4*/uƿёsl~2X"pȑ`[JX:AbNKg ymS(u\//e[Ϗ… gϞ=ÌSw6e[_7$$DѰM`*@HyScByyyяmo5o3pAZ#""bbb؅&0Iq 'pi/@XXp/vۜmCkV"Xv LE(u\ &۪D"8pAWfl+Zi=dz M`*@O$=)@AOFF 8៫lsh JH.7Hk>]^D.I\EN¶5SJ } =96 Цg4e[Hk>}.L]}Q)l6#j`*@O 'L 8Z-@TTLE(u\$=)pf;%\&&`*@)O=lʁHk>yϧ|=Alk*x;RT$R5Hi".fTSJ }o_Y񄬀䵩T LE(u\d:n60USJ }fuhlͶ6-m" :A' J]ݕ q<#5m" :Aމwa*| XצʶSJ }J-F4KnAT$R5H_HOL>sJX=_$zHkMϸ0e.`^Jb2 TLE(u\I^;пl͞0USJ }W)] GSWIl+P 0Iq 'Gۍ;Ww}d0SJ }ق6#­vo0@v`l+P 0Iq җ<=s^glf`*@/{IKx:dn_gkSySQ/0Iq җq .⌵.]x$P!>=wz@~.};?OKxŶSJ }9]#>:n0$PiR%:{>ƳHl+P 0Iq g8`Pp1W5lfSQ90Iq g1ݽt%+0SJ }&96.l|ثMKg0SQ90Iq җ+<;hqj+L`**" :Ar6rG>r`*@/jW}ON_f{ʁHk>9vO/={ LET$R5H| _׿Z`g$PڴtV}5n>QmF;ʁHk>yBrȡHYnHO_:T;2&Hk>{‰St<ϤOnl*m" :A !2ܘ{;{ܪk݇{ |,>dz⁩Hk>H-~(v~ю{⛀NbP0Iq #Ξ3movF/z7 Ag^ڋn"#P SҁHk>~I{4csۏ(J7fi'_˰ ء@T$R5Hx||ϞŜN{;ɳhznMfёZZ9_հCҁHk>ŐF Q{K;8eW'WvVM= rkqxvP0Iq ҧ<^E?h|~qښ4Z}[+-U :*" :A+7/\_7kq-x\wfjvuSJ }j+6ٯrI+_#VrՂVz/?$PSFvn3ƧB3_ϻy_"2S6wr.A2P!0Iq ҧNR%hRmcK}v 'zNvnze&$PS9I)1 Ż\'ώǑ/qr"5.Qp"}{^G2hI`*@<u}ٮc&xח~\9@Lo{*" W:A> ˳7j6h-)O; r}GKa*T$R5H0Mj{.W Bwg׵PK! 3`aR? d/f|=o +?oq/q4=;K`*j LE(u\%=)%B׃g=kRo>.p{*=;=[X?:SJ }@nd]vҬ?N[ko,unP]`* LE(u\9hg=Lݮm=[T0$P=S^ǻLl/${Mzz.o+BBӯg[ߛa+SaHk> O]mh­OtE:8SqƺQ]/_ ۳f#1ڷoֲeC ܬ jմнb90$P2%2"b̙ML.^tCRs1Ė-?pvٳGN΄#5kӌʉ0T\0T$R5H鯒z3M%##jՊObhaN5իuݺs@caL//cizuM K0$PaoJ(ZBsbWRR/Rlɹs͛V+WNWݻw۱u|>}Hd o *hg YL(hAm7lXv-JF1Ŋ0˴pU^ӿ^ҥӓ]ӧ4௿~رyѢʔ)AOP<33g66+ٰaYt38s18DT`*@p2lX*/^3Uubȑ֎뢦M?^p+++a~ݻ; akL6kVj͛YI-'OniԨ6)= E.\؞`~}Þ>)@X1y(@|ʕ$M!!;{vW^8ϤgDq=<ߞov$۠Cts&9p`Yt38s18DT`*@p9eـs7 .g+66gWw5V,Z#wM@ھw/;We#ܿaҤC:G& n»?b׮v5kV`b1L_11t0cSaHk>ʎ.R/_޾Sw]i*捡+Yrڬ3q!(huљiP*TjՊG1ؾ}1:wn)\',bb] cÌL" :Awc*5ͣǶmPW`:޺u88 B涨aZ(ؐr7l%5ԳMJ*6iu~5k&f36bb0T$R5HJt~kkSo66֤/ԛp.xݻlmm J5ŷB뚊63 LG~$Xy Bi*G9Hgҥg&x{m߄w[OhHOJwGaKs11XT`*@p[Med*=T$mnIb-~ӦUTco]YFg'JFF1|}СY2%,Gc[M9%3M9s7ҠӿtԩM̙}WqaUGnrqkYt31s11XT`*@p[M^7c=*yԯ_C;FjSSGum[o O ja=lmm%o^[5!B̟}*w(Ȩwwc=~_v™,_|Yt36\ 30$PVS1'HnOM=˴;;wQ}ݖk3?^$Bg>!9̍H*[{~֌Y]1ɹtaSaHk>b*L<|xxܸ+VL._xC"SJ };rT>=گ_ k͝ ,0$Pܑ[T`*@pLE SaHk>05L" :AwT0$PSQCT`*@pLE SaHk>05L" :AwT0$P!K!_.0Iq C0"S+J };Rx|4\w^LL!mŶh4Hk>5mJ*ۤ(_rDDDLL ~5SJ }k¨J*ۤ(_rdddll,~5SJ }ko!!!AJ*ۤ(_rLLL||<~5SJ }kla&RReTeKY&MINNfӯ&`*@.RU90I`p `$~ TULEX?\X*" A,US }pʁHk>KUT$5H\r`*.RU90I`p `$~ TULEX?\X*" A,US }pʁHk>KUT$5H\r`*.RU90I`p `$~ TULEX?\X*" A,US }pʁHk>KUT$5H\r`*.RU90I`p `$~ TULEX?\X*" A,US }pʁHk>KUT$5H\r`*.RU90I`p `$~ TULEX?\X*" A,US }pʁHk>KUT$5H\r`*.RU90I`p `$~ TULEX?\ Oit2K" :A'חz|4\|TzŇ@ T$R5H$3REY꣠N?$P@a(ȊT ݓeȅPt`*@rgAZEېimsW+v(P:0Iq [ɧbikUñNA@T$R5Hr?hknaSJ }mz]sASv_o ;$P@ %JovuSJ }Ȝܬ< ))#9" :A?wmve&$P$5.Qpb"jG!{^G/شT$R5HP$T{; ݿ LE(u<E"K"ח/+0Iq `*T#0w" :A"9S^:(((,,,22266M!x$PN$T잽ˋd%"""&&M!x$PFsջ EDͥe9S>}oHHFaS" Wy:`N/-m:䮬Tl۶=(((22M!x$d^id> AY 6we"瀩d$d^id> !d޾6ǧh4lvs`*yy`*<57Ó T0,SK0S1t& ZXV`*rJHBF郩LrEV`*rJHBF郩Yk*}7s~`*yykB7,i[]bH5!F&OvCB=01YRV79sɓŋC j ͚կT/<~eKҕ8q^S0Iȼ<}j<#5mrdPMVz9&s7o[95h KefSOBצ3M%##jՊN?@h E0m*5nbf1]|QuKWt1#"L]yJHBF3h*͘=k/wZwڻv>gNˉ?-[־~2۲e%;; _:RhlѢ￷/wj=a1_|QtԨQFkW5~ʕzx 6ΟVV%KmְaهK|?mOvtJ[veIflQb{͝vWR8UMOH3酱//ю=?Wg~U;|>~37s{_Q0T7RYY%JPtz9kĈ^uTzIe ]F+Vx„/= kQ%%RoEʖ-9wy4 qh_~ktVM{イӧݩS1cڿm:DG7swLL L% T$!RL#JrTӵkM67.2-mkkݾ}Uw^|RYa0*jU'O&=ͩFRҔ ʔ({ 6T9yw2a\̈'zw rWgd3~UюC\9ڲ巶lyx$1fʰa= *)T_׉]NN,b23f k%xk gimmnݏ. ۓۯo߶ۅ#{뢦M?.BN@zAyO>>+:]ʕ+IA66us|vϞz{jݤɇΜiS!۳KK7`*Y" :`OTNB?Rvm{z꜖YS[8w,P75uZ S}m7h*'OhҤ7NDǏ& evҥZR5*۵kuqRx; \Wؾxq8vϞ^&@&MѩwFDmvs߇NZy 0޿RC%˗!dh;4t;=!CT˗/u~aƵI#LwݿIk8:6w XC\~]$664e°ԳT yCVӑK?[xx;(Q͸ftwg´PT\JG1`*Y" 6~TvN?s† CWמioe˜M%-Sj(AP 0O_M4VZNRB>sl|(^I]VVyn-7TaIISfhf4vUML"f҃1׍:#]=_sϲ5 xqJjMeX=N[Mձcsŀd$o¶NTя-[7bӦĉik*vaowԦB ̈́/# L]w_ !6tmB.c2ɩJÆeiC팩/0;,"bMvm{3jb}c3? >ԭO-݋׻hqWPL;׼7>~"))TXRk4۶"2z/WxXסC7l%ɐtM%0pmo:O<1Sps[԰a-a zhtB,ZF" iSt7}7PG1`*Y" ϢmR_3/'WZEJ. BjŅZT1~ ukUXo }^fh&@O壏J+gO޽q^^}'klРL%љqnmp.aP>gNҥ ޹3vǎnyt޲}ʃխ^xl,{0vgy GW&:zWU51]"9~n #z6^[tvi:ȥ8s\~Yy!<==摬c*Nu};@wXjݤɇJ3slKϘ]tM%!ᤝ]]w1f*¾u|$鄠#ի5M}zg&L%-MAOŀd$vLzmPPYST/y/s0^c6/w*X0/'T)je'_>mܽ;Nh߻O2ɩ A0׿D*"515Ud;Sx~: 62/mm]5݅wmt+W.B#sLFʒ1Kh7J&3Ӟ]5%N ТGG7f;zݺjL@o߾cǎ;wfT.OE65޻ͬy.X0M~Ew0]H#O%S/3֭?)VEo4J y Q>fL?_ߣ:thVL ]*s_N;.c*t·oؿ[hHS5K7`*Y" _ܸ˶NTIw O$&Ny{|e}08F&eNNuktrHJTYƦ3Ov~[a3f.v]"$%TϲTի޾}3c)ڥK'i35?iVtx0DWBazPٌ M۶͛7S _Vƍs7ʇV%t^^͟N͙NѴGаs/e0]nOM=ߥ/_01!q,L% T$qԻ{V 05}}R.`*RbRK^/+Ws.S]s`*8;k`* `*d l/їB No~3g4&L% T$qmΈ+V 0C0ASVѶ8D_J "T90,S=ǻNb['TTv!h ى7q}`*rJH"f_;l+ !ʶܹ̏4؛8Y"瀩d$i^$o*plU)`*R9|l+Wy,h‡Dt? 8怩dTڒl+ `*jJH'zLf[b*!!!...o@٤Rf5 oT0,SJgŜil=JDD8 lLL oT0,S w/B?'o(M)e666ͷdS|)E܂SyW`*k/\϶ËЯ~ #u)e6>>ͷdSA9`*L%xZ_@rb*T7oj&O6~uARx|4\w^Lu!ضWIaT=.`[T2AIJ =\⌵l+70xp<{L%{) PT@TdAFjڙ~24S0?0/_]0?0yq%лL0?0̀ l"G^&<@/"_nn.l&`* `*&}27Y$SS;鯒̷RlLLbO\;RJSSq$LL33^E?d;SSᏌ+?o*[2RRn`60?0^y}5̓{Qw>6`* `*ܓz׭^ڇ] SSQ)OGLZds6%b!`* `*՝g.]q>4Sazy~n?U~cGFr ۭb/hPTzŇdLE<=s|ŧBVNOLfUIg^ڋ" P;Pp.D;ݻlǫm{+ڴtIqa(Ȋ`*2lBv(SQW5*cv;5UYDmh2wVtءdLEE>~mC}v:<(1CIh>Wwd*>#k9i; <ԸkKv-qI6#ChVח6*KSCgALEhңrڿZKw& 폏G4[ݹыoHbHOLvm&hg6u#yzʙ<9 %gS+p[o& _ϭn^m9þUqzx՝nVy{툯9Sopc͞%ۻq^r8to@ЁFl^ '`*5)Oݻ\'AP{Qp⟕=yR^>8vbO\8=x|-=}ެ ݿ!|SQ5/>={Gb M] ˗T\So U8|p6~!_>…j(ukW flQb{͝v'Vֿ2{k$DOxvi6A?x[Y_?{WgN0u߿ٺA`Ӱfil$ }|2wQ(QX&|vPvvy`һwS˯#z yժiWv/4)yg|tw48,9 =94n\;0pt6t(.\p޾uXIjx '` b L;fJrTkM@KIJ]u떶n߾{O>)߬YEf_Fuɩʙ3c6)W^h RǧoB&Nw2wnK+;Ә6^ҝ:U;}0kk#};;pXa[ʘNz&MFFmvp}O#m&|N*CBWm#)С.hvl{xhW=d4oހ,իBװa=X1yH'OniԨv 7oԯ_{wGaY3]xiʕ+IQh)\؞ رmKOޮV&ٹƍs̭BC f^+o||V,b23f k:s} 8m;%SYr)kPzܲe)=c̾'OwS;t@t{ui]T+U@Fev;&ގa/^Nݳ5Wtħ??=n1Ԁ~U[34+bҏ{6'=>_H?ChBw&߿Z&M,<$qtl"l36S 5}^ ?t0:>U>N°'OI| AwjOlU/_0/_Νƍk7mm'` b L;fʮ]gXP!Uez3ڼlVZNRB;2͜ΜY(^I]VVynmp:;,)iՌ<=cWt9k̥ţB~iONnMth52yNؼ \lؑs^S)YQ} *'u7 6ՠjMzV(tumIO?G so+&#Lj֬lZOg޼1Ub+\wcccw ;m8%SY<~8n}6u'N N3^^ذ#nT̜N7͒2ɩJÆeZ_lgLE| ahkۛyzƮ+)=oC}=0UsviC|zM;/`ڃ[]رc!!!aaaG6E??>Pyg Aa,!v/t墩86ϛ~q*U*>ӡps[DA`pѣgώ>cmpӧ0e*/_NZX.]CjՊScz,FRҔ ԨQ" B߼ bt2gNҥ ޹3vǎnyt޲=ΠAuW/;A4vg}!Cѳk2&BR3' h='W+m]݈]C&<. $YɂddYymϝs11tL޽ߠAMm r5[i{B׋lmmڵkjZOGeР66OޤɇJӾYzJLTcPPISTL/s0^c6/w*X0QQZz/O[0ŋ߻Cs{,AA_Zƍk/qN\r矝cm4jT\Yh>w^B9gʘtRrmŠoW6nں6컳&mVnkL@o߾cǎ;wfP\?]:$&܍j__~M;O`8\NH8Iwg/]4Ν[+VxժigE{.3}! >1cסC2eJ|3y履F̜WȘ8cmld,`*1T),ӧ40KZ捷nNN~^IIj7 'LS/:}:t_VwfWT7oШQmo}OUx-Ȱa=HS mfBT!5+ʕȶm<@w[=xpcVy*Tpٲwg CUV^^˅ԪTu6f:2qN,OXTn0fSD )2FSyk$'q×eڣ~sI5ӡ'r^nTN@! ~TS1Wߞ+!})Ri#""\B5,tl0uG0T Ttb(%D NEٳG w`*܄`*#Pm_,1%T0T Tv!h ى/ LEaSLL Ν;ISHPL`* w BA"0s%xǀ5&|D3  )`*ށpLNS)S uqq兲h؜IxǔDDD5///*ۀ<?Cىas&SB?G \PF(/X6gLT J%L5L~ej{HHH 兲L2 02*[;UDF A6~uARx|4\w^LE{l[g9)S*'w)) r 0 RR 9}{0`Hܥr&9`*(KI1MsT _`*/0 LS|@T _`*/3wlease states (Kea 1.8.0)Freenot in the lease databaseAssignedAssigned expiredDeclinedDeclined expiredExpired-Reclaimedassignmentrelease queryafter valid lifetimereuserenew querydecline queryafter probation periodreusereclaimremovereuseremovekea-2.0.2/doc/sphinx/uml/request4-lease.uml0000644000175000017500000000744414206773363015455 00000000000000@startuml title Allocate a lease for DHCPREQUEST (Kea 1.8.0) agent "Find client lease" as findClientLease note right : entry point agent "Check requested reservation" as addressReserved agent "Get reservation" as hasAddressReservation agent "Update requested address" as update_hint rectangle "Check requested address" as check_hint { rectangle "Get lease for requested address" as get_existing { agent "Check requested lease" as existing } agent "Check lease for reserved address" as getReservedLease agent "Check out-of-pool address" as out_of_pool } rectangle "Check client lease" as check_done { agent "Check renew" as client_lease } rectangle "Allocate a new lease" as new_lease { together { rectangle "Allocate or reuse lease" as allocateOrReuseLease4 { agent "Get candidate lease" as candidate agent "Reuse expired lease" as reuseExpiredLease4 agent "Reclaim expired lease" as reclaimExpiredLease agent "update lease information" as updateLease4Information agent "Callout lease4_select" as lease4_select note right : hook agent "Update lease" as updateLease4 } rectangle "Allocate unreserved lease" as allocateUnreservedLease4 { agent "Iterate pools and subnets" as iterate agent "Pick address" as pick agent "Check reserved addressed" as pick_reserved agent "Check already in use by another thread" as mt_in_use agent "Check lease" as pick_lease } } agent "Create a new lease" as create agent "Delete old lease" as old_lease } agent "Return no lease" as no_lease note right : exit point agent "Return renewed lease" as renew note right : exit point agent "Return lease" as return note right : exit point findClientLease --> addressReserved : has requested address (hint) findClientLease --> hasAddressReservation : no requested address (hint) addressReserved --> check_hint : no conflicting reservation addressReserved --> no_lease : reservation owned by another client hasAddressReservation -> update_hint : has a reservation update_hint --> check_hint : request reserved address hasAddressReservation --> check_done : no reservation check_hint --> get_existing get_existing --> existing : has requested lease existing ---> no_lease : not expired lease owned by another client get_existing --> out_of_pool : no reservation get_existing --> out_of_pool : has requested reservation get_existing --> getReservedLease : has reservation for another address getReservedLease ---> no_lease : no active reserved lease getReservedLease --> out_of_pool out_of_pool --> check_done : owned reservation for the requested address out_of_pool --> check_done : requested address is in allowed an pool out_of_pool --> no_lease : address not reserved and not in allowed pool check_done --> client_lease : has a client lease client_lease -> renew : requested address was already assigned to the client client_lease --> new_lease new_lease --> allocateOrReuseLease4 : has a requested address new_lease --> allocateUnreservedLease4 : no requested address allocateOrReuseLease4 --> candidate candidate --> reuseExpiredLease4 : expired candidate lease candidate --> no_lease : conflicting candidate lease candidate --> create : no candidate lease reuseExpiredLease4 --> reclaimExpiredLease reclaimExpiredLease --> updateLease4Information updateLease4Information --> lease4_select lease4_select ---> no_lease : SKIP lease4_select --> updateLease4 updateLease4 --> old_lease allocateUnreservedLease4 --> iterate iterate --> pick pick --> pick_reserved pick_reserved -up-> iterate : address reserved to another client pick_reserved --> mt_in_use mt_in_use -up-> iterate : address already in use by another thread mt_in_use --> pick_lease pick_lease -up-> iterate : active lease pick_lease --> reuseExpiredLease4 : expired lease pick_lease --> create : no lease create --> old_lease old_lease --> return @enduml kea-2.0.2/doc/sphinx/uml/currentHost4.png0000644000175000017500000043632414206773363015210 00000000000000PNG  IHDRr3f!)tEXtcopyleftGenerated by http://plantuml.com09iTXtplantumlxWN1}_)0 @[@ V$ص#ۛ(e%!}h9s}|ӆ)Sy/%!-Ban6p}s8[dpz|q|rdؔp0* 0p)i ZN䡽`fcJ{C47FHܬy'RA \.qZiGcڠ}vc"KUS0θz/bdYSْ)Fc}ꨧ90QՂ٭0۞6;hGb'Xp̥}N~\r3skܻ[8w.{zt .`+c8qkSϬĻŞ;Esd \Yؔt[^- +/Q MDq0|,ȉu>>r"釜F^vRs:(}oSN7tH5(;]dVW1r +0!Fn4E ~7<eZK efYJ xWh _S`p=w!u؛Q% + [9#a+5fGA!kDĴp[Vd薵q0֕nhlvϑã( 'ifKxxcH%k.ԖdDJj<|. pd+zV^$9*i4h;SOjL]mY͓6mF{K?y̙0/w@e#k0gpq\=u8J?sRk%C`T89m?ł+) 4o>pg\> +sCTfPErG4GGrEfF?{j'VIDATx^xz"M@FPEQ.QP(M(JEA) JoR PB J1BIh!@~8Y$ |9gΜ'@>5ҍ7MFF>v)C1N>yCڣ;GDgϞWϞ=zqwrѣ cǎ'N,y"##&G .LgǏ;vL#""iN=ZNlٲ!C :ۙUV}W3gԟ#Fȵ [ Xcƌy뭷 SL-[L2,YK..]2 sg%ɓ0PB z)/4tWeSwZ$Ef˖mnBCC_{ ݷoqIll??K_!!!2?2{liIsQZ5m@2/~'+V̙3gBTzji駟y晟Y -ǏWw.]*wG!5k̖-[ddFզM%JɓVZ7jܹ_/X`g͚zN裏Jg8q"*bbbvgϞL2;v–"I˗Ov!iL jfV+ ͔)to.^4BS`-$JKƍ7o.K=vnxb޽Q;G)P@ɒ%*Xvzovq>ej7ҥnݺ?hؤvQQQNߒ]…_HNGSTmڴQ2kߖӦMSw*1 ɾ&3g$ ӂضm[=|ޓk.]Lmĉ}t'S^u'XKI{F$˗iu}ŏ9`||<37n:B.|PiWIʕ+]<я@ShQ {h 5kԿ ic"E?K"1E֭[WI .^Z|Ұ4xw=<\b|tZ>8qBZ$J*}ܭRʟQEVW{{ u$dKGMU`A&i#G꣄6mRw+W{Qzj]N4Ý`'OL2iϑٻ"WlK>z^~Z<(Yl".G=2K#$4h pz"gX?"X˅ T|q'$}Q6\*g/{IޫW/:|Is-GIė/ |:X˕+'Z.<+ݻw,Y|\~S'$:}Y>~ 8E2gϪi^$LNy QQQ>IBM,W^/˧n&Zj%z!7oaX䣒 bH"G5k ũ$&eYkk׮5ND>!j}-꛰k׮-ɓW4mt+VI`uZo&;' w+u֕M JVwwԪUK?"Xd끺oQ޽`9̛8 ]͜^‹/Jixܹs n֬jq_3̚5'M*Y 3,X.w%kxӂ`gO.$'&0={]mW^]Z~5z;%W\"x9ͫ_I`W_խm,XoKzeCENZX ?˗@C2;JP\.Cɓ7Pw>) j/cż!XoذA>ڀDsz3_ 1bOҋ˗͛7~͜_ezb.%w^ >IE It#Xϟ?'>I0v_{噕ORFZZl۶-wһwom+lFjժҢCל눈hFG Oo$f&[\9 &)9sh-`` d8QQQ;ʔ)ӿݻWXQgbgdɒ-[6*uNA>{zË/8n8If[~W61k> ;w5k=nTK=$0?8sr\aaaj@ ֭;j(bUTmu릺Lԩ/?"X2\2d'|?:u<1i(dnkI<;^ ևIN&rU+@R^>}w6-Z8}IԴr].7WHU 8qs='wn72+r堊/}Cl(?s`` dDGYJr~3СCU{I&^1cܕ,\IѤI2o޻ldҮ];־Z}&5zC}t/'&rj[Q|-[h\֐ վ}{}2Ln"_|ժUС<̝u֩TjdmחRgk׮>R/l*cGr GXBU{׿+F9UVW|*[x9;wג]F@-}͛ջyh:DU$'ޱc$@2/ڵ+&&{n-b$n:;knokWhdZf!tdɒ۷;wm E'Vٻ۷O4|ieҥn PW O*Tpu};X!XزeKB$;2QFS ksߡq_>:: @ Xi` 5@ Xi` 5xzv* KȨրg 7*UKȨրgPzOC7{5@` x%9;PV!Xրg XR:p[GGG/ #!X`JH 4^6@FB<: 3{ 0^6@FB<:g̘nl` xuz+5` xuz+5` xuz+5` xuz+5` xuz+5` xulƝ;>ƍm^sw}lnu[ksa)(5` x7s$Gl6޼<;6˒%sLRtL6o\"X րgp3X?K/=/0O\ôv 9Lݭ\ljOi]C.'% Vu'NtۣG[m5.ކtm.^֠#.7ϜY#ZkÜUk3I6m@޼%5ΞjtoeԩE̾ ]7nl^$%˴Rz"X;Tޭƨ?nc؋U^f9SKi`` 0 X`jϞٲe;w~]zϝ[ggU~ۭZ54Ya*\Ljj]VZ}r~ZO?^Tq0"k3n*STɒE JkoH"XWRP.}֌\)S_~AJfjk/9(mm.n5.fh7AСroDn̒%sz k@N ٻw9QQǏ-pȐn͛X߾fϞf2x.0d>}hY}ƙ3]7n駟V2 [dŋ\T[iӦL׮M@4jT#c|l%,nk3j wMKrdJ޷p(!VuI\O?.2ezVu>[zu[eMId^.˵}#"TTV;=YT[pa}˖|}}=Wa yN\հau9LPB5 ֎O~mٿ?H4ˎ.] 14^T(aZҰTĬ7"X րgp3XS`@<:`@<:`@<:`@<:`@<:`@<:`@<:`@<:`@<:`@< F|/yJ%ׂ` #XAk*!XبU;0tZ*zG͘8/((($$$""x <fx& &%WA\Hd$klݺU\PPg`mlXrZ 6^*@FBi`Nq:Vy@FFߟ^3` 6 X#SZ‡ñNC)/ ޺ukxxxtt'jkdtkKH 4$R``My}`=`??O ֔חg̘nI"X=֔AFFG`52:5E{)/5؃``qι/qcџ}Yv}I}aol^*5؃`Ν`=cܹs$ɑ#oM2CU3=Wܞ>ڹ/ٳ}[-` X#eVDgyr۶uzia>/xܞJba.\a]JRv3Xy,k-}||1wJH.oiڴOZ睇.9oE&Mk^նWn3\=EۿYfq_PCkdt RUlݸǏзwd#?ׯ _H +U*[xxT$ޅ GkҤ}*׸q]arPػwڵJ<+oܒvm٢E]ڴiffرY,۷o޼fm H9sV4X;]]"X= KKɓ'0x*8=zUw%KլYEvŊ⋕/] 7aWȑene%i]}.'%V|4e wd먻gά`-\oHђe>9.6Ԃ^􃆽lmtIխZ5Tׯf˖i6Ys/`52:w#Ӧ ț7DٳI}$$IБC%`q믻V%J5}sĝ:ȜWK^L]kLFT.sB5lHђe>9.6e5Fq^kvÆ>bĿg=5;AFFfVgςlٲJ۫V qU&U5k 5uܙ;whŊeԶֹ{:C>la.yɓiO?xR]oHђ e89.6e5Fa5[mbi駯ôfG"X=n+XK)Sdɢr#2reL^~fj;}EV+Mm4ʜ̙5rBKsêT)WP~s>laCloDݍݘ%Kzʑ%Kr\lj+]έ]fm/^ Ϡ ;\΀Uv /5؃`:0pdDE?ĝ!CK뿻voȰ%KJ>OKAAXԠsE*,o}7{l5kEtxק;Z5?1c> 7Kׂ J:9Ve5F®AֿvT?oF8LkڶVrhJqE5 /5؃`zܡگ],^pN-._ޤ$td#钴RX'M#ZI+W!___*_1$.4>} UPr٧~\3e ׭lm$la殓'W5lX]?1wU ɱP/1.oٲ3W kviE+ܓ{ʔ/spV5 /5؃`ڑ\s\#.nAe/P%ƛ%#e7vfY/?ɳ*jIZY[nbLlFQt[Сׯ&4՚UE{ѹ)ʣ` X##XS^_k"X=֔AFFG`52:5E{)/5؃``My}kdtk` X##XS^_k F|/ტMѩ`MQ^_k_Z]U AR!fLaIWppp@@$Xp[  4>FuVAAA$Xp[ 6>F%#,,,$$$wkc+sݪu6VWϞ]6Ma"Xmր[+VXFJ1NeթsϕWF1~%Νs3eԦM1c>9syΞbٳ}[rCDZj׮}y3(QdҤwiHݾresN-dvZB~L@HoϞɓ+[?p۶O՚_ޭqzVh5|' [l)ٻ#Q9ʕˮX1A|̘ϥW.a*ԙσӵ.^Wn Srnfb֛7ϔse>v}6k`#%nYk}}VG々 G$۵{Yz% ._>ޑIܕ3jT7o{$4WHT<zyEiop֬/wn5]L/}^f9SWG7`m^uѶڰal2bĿǮC%ϝ{E}Ew{_YJB{h;a*ѧb2ҨժU?H{߾ڝ85X&4Y*8k{w=ح&1>X/_>^nOO}K*n8X;Lk].V Z>X޽{g0lCRG L:,MF37"3eTBi}"EFNEk֬WofU-$Cnbc7fɒ^jyHEŋe˪}ޱcص`zƟ^y\lCRl%7c4xNnoIҥePI{"%`'xڵS pR7(ݵ_BZ7xrwɒZnwy$kFjϟw̘CCQ\`yyσuaZʱԩK*m~={wk `5V^B[z AGŶmeɒYJ[&5}ܸ^ʕ{Wʶ*…-[Scrʡngewj^Yh/Ǩ7`@'u7"bIJeNW cٻyj} ,X=/~)ڜ{}㹓Ι>sn/><[/;8^<ã{@Z XH X+g/|zjiJ[&Ώx89ր'9kSgi솇r8NؼE/[tf3W<WOB%e^7ڹ WO;l֒-=*|ܯ ` kؿ˰nlo[Rlz:,f!c7 ]"XQBܵ?/A%_WN=2"7Fkjw96#q =!XK;{ (`]ÏsH0@p=U/˻M>oH@ Zbi%^G û֎C71{#` eWNDcPW~ៗCZ\Թ?X/|jc7!XwͩЍ-z-(Pg[oC?-{M_į` ڹO{8p5(+򠊿c &5`{t8?MEo 6;H?Lv6 n\;8u*o-~aV!Ïn}wuzW47bG[o}ON,ݘpGiM"kסsIc7!XiVb/3vlwج7]Q孃S޸gXʟ|s k 7͗a=n ]zLho 7O<EnO|-/zI9ڹ@w1Ɩ_,,n@w]u`[ آש߶Os6tϚ:]myljc[Hv=1+~= ~ɕ[Wlv3ntPiG9n$G^ppJ``3ꩳn{ր _R v~::1]$$ ;tfµn]=yfA7^;g-MfxֿGXkDn:fƖ_,(P'ӷw Tp'[kQ>52b=gi!qQ9o6+P݃pԹm{;x~:!Fi_tiٖl7#X#cqڡiVV}{QW|;#.q _`_=͏ X"X#p# ''lLt8#Xq ,E>@5#ƱyX_>r8Ev^~icdxkx+ǣ夅tdJ>} y{д N 4@F:|z.n>djw`eշ` q̅}f+*;89ҎFžognp`R2$5]Ζ>Oc7;&fOʧ>``8y6KJ7w^?z*}.:nhU[lwzdXkxkYxϯ^9mv<]^m̞cdkxK"w~66H ><q#8@ppJ _ c"X#]}|^.Nttcҟ/{Ֆq%ދ`t3M 9hGwv-z~:|c7unMiҡHcrŏ4;l b ;{YR>kE0\n/zXpGF~ , k{{=撱 ׮2o/']9eIl- `qPu/}dHr)]}pljG ſ,)bg1>YTչZo׻4]>{+(Z׿>XkVBܵU5\9{׻D\z*@2 7;^?Wkñf!z}OԪ-PζC.\q8zZ^Փg]Hg{~3˞xsSKJX^́c3O_H3-5RniYKNg{ V䯎\cx'l(Gs3W'U]ٞ_pomF7oXOsWmx&d<`-!(JeE7bpr" Nfa9E` )0'Wn1v' ENC'n4v@G;[*XuhO[ ,02wcok~$[EFb-۶7K1v@:F;[*X5/((($$$""S|_7HugK 3fRn9z>g XSԝ-5wK>lT:cl2Hug` ޹75 #XSԝjN ՙSʭW5t9`ʁXK'7?tn~OHц'o2v@A;[Y>#n6-|lhY -*/}_鱽}|vt# dTg6/HÌ>oQGwoP~j-m]|ZӧeMMCsVs<9a=FeO韽F5.-G欼qj'N0ަ4$Ν;[o_BBڵk7lpUc_p%-Tpd볡{w:?iRWPRYsU:<\ym*{l~vejι2ejӦј1Ϝ9< 5?QyL\#G3 >_k ]Z1sc3ݓO>[o~Ç&OKVO4pڭ;uԩ%K/^XbI).8rb/]nӏ^9d;nJa~'+T(nO_cJ SSkC7"sWKhu1nr1;(ꩳ⇛.{՞ogE3J [[Kԥϒŝ&ZoS;5}XM*W NRŏ4zm6+W6wԢ`|޽u|6ɻ6UzWZMаa|)R0'W\ܖ=̲aeWuXL"̛;,V{Q/ XrG?^,-PEIf49y:s$fjwF=yr{@z11Utt1Z IoeK2M[/ٰOw~D̾%J4#QN1k;1v~:zi6%&6nܸ]v{ѣG=/ 8g͞={WZ%-={y-]Y-[,\Kc\\\bRܹ'|Rh *o=;a5Mduϑ#GR 0ѼZ+9Rnݜ9s+WENc~ŋ_xlٲ(Q⯿J>0q^Yw}&&?X:=jcǎ5m*›7oe}iժUܹ:IKJO`ksz);)+}[Mͩ+}Zӯ_ӧ':۵~w.2%zÆ*-^?XxtD#?ׯ _M$̙X{%3Y3EZ{1ooQ_8|xmMeɒ}S|))SL77on X]lѢhӦt1jsjp˔)Ƽd?n#]*}>#9%u9CU{Jud G)Zpj,_&Mj'OmǍʲs!awkW.-89Ni:NUh/M֒iM-їY]e//6qzu.^qzҗẻseCV֤3g,ykٲe}M6-4֫V ?{Z54Y5Cl~=f* ѣ _Xkj|x,xqOqZxC?rXъtZb5jq?Xw;Х?9Vt8 K]7Zow N[qr[H+ҡȰOF߸ w16_|Sg}t׮]+a}>Xk/q/m$mߔ)S ޣ~.hڵK?j7n)$&]bkrsݫW/ݨ郵Yu=Qk~w0sLuwڴirWNlif='Tza&<=؂5轭GZM5qDӮanNu˟z I`ݬYmȕ2ez̛bS`H)U1DFX7'СŲo7Qwcc7fɒ^jꮋ,8nᇭ =䣕* Gtlٲj@dv[._OE^ iD` 75jdΜYWm۶=^XX1ԩSAAA/|ugzKkҥWXs#G_ x9ލ7O|L1%Q7^N䤣GFGG(QB2adb`muV]Ohui|eݻwKoj}Q 4S鹾"N3eJ7yCaH.ŧn=zo@drرC.[o%Vưkmwrz$Xx/Y2Vbm߾^kҏ/bӖ-dox |mMFjϟw̘CCQ>\`yVbF7{I|HݻYQZUeΡC?:R}}0.!a{.-F8HZHA_͛X߾fϞ'yΚ5Ճ I`>h]V'jc}郵ys$`dک~~0lmxkxK;P`toH!?\q|ӆ-(XwMuzzDS{ꩧunKTk uԑssUlĤ|,Y#_ŋK0L>j(VݶrI#""j֬)#R.\hߣaVI&y䑞={bJn z!YIիwIHѹs *Vg:=j=I9O<:?CYM8=Z*eG];cIͩ+}Zʕ+.\X֓/_>oî($8XW#"Wd$}^&-#}z Iڶm$ä$5ir/ ݜP_'Ojذ}!c5|'Z~*KI4`.xRD*Uʩ1uXӊ?QL2G'o3,[C2|Tt$}FJ޷p(1~|o%rO?O_o@Vi'jc} ,X8oG.\X߲e=\r:VwqG[s ,X4+_ٵg p=^2ЃS=>>+=ZꯩEnGoWȍ/MGo?PϞ=MۚD毌6]լ?^;9pzV&QGEE9sؚ";ymN3Ň;?NW$Sqwn>b^fNHH0Sp!\G.5D\ܖOH}EEV/JDd)0&fL#tXUjdC_j/93pCI37J$U%,_NH~uR|bc7]QܼFZiUcnr7K?/ )[: \[n =7#f.ް3.s$$ϑ<5vdxos,XST]FR=;?_e[O7vV)֟۶Jdg)hPWNkj޶jj:jXZGuU[WVm:[DAn{滞{ H.${r C~n`9o> * ڿUTӭ hK><PQ-_pifU{U #[[[̶0kd?^`D`Bl kÍv"8 *ْPrr?nq((T5\ } XP%[;s;V`Bl!XV8=g;M4X?ܱBA lS 82Ğl7hDPDDH00L d;`CQk:ffdLllBAAD%z@İ4(y?]r? =k .(XM%f"lF.t6{+hDPTDDDbb"H ĦQnV6%똘!!!(XM?z.WudA=D(J# J/ιZR@}Fm*E1VzkϏ OM\e[Zt9qIwV^.~ Or3 LvhCd'u!cC@y|"= 2?hYFCvcvcWڀ`-c9Y*8u@ң^Z5a; Z3Xs9g.8~}& Pl2Zηmnf Puo1eLʐ6͆>avӎWņ`-c2 ֜xGV) 9v[Adzņ`-c ֜X7w YJ_nfֹ}4:ki椿cXl7@Irl?Z`-c֜씴GMZq5dЯa\VA29윧&/v~ֹ! P<2Vfs_a `8@Km<`)xekNsX=ۏnb oqk>xekNflߏ45r4/(cvŃ`-ce;XsrCOevcTOM<'f[ZCVywub놆W( ^ױŃ`-c*Xsm?kq7 =;wxekNf\҃-Z~}Zx>(cvvŃ`-c6XsW,nhxkžv^7 P<2V΃JR`ۭtm/]|n5w8 P<2`͗c ?\vr;?~s;Ƕ!X vܪ@yyWG,<-C1k i1wn۠yVvcLN c[Z KYo-ߗ;%ۃsVA1k%= Xۺ뀹OM }YZd}l+@!X?X溹]v-==7Dk޽˶j (((22mՑ̬0Kn) ޙx?!C:=ڝMsb{EE޵"o(},| Pl2 ϟ?w6mڸqc XҎVPATӻ'+459i϶}vkkKlNӢ""o(},GLme[ ZTرcu}_pP9lݿ^d [#w>5Ov,?|ٲekD@)w|j24$p}cvl+@!X˘*Xϙ3_Բ"?穇"LW={Ý>VYIٳԩS|:]P cag6HtC1 wņ`-c\޴iӛoYZ6ml?7GY?ѣ#""{믿˻vׯwyذaׯ_xqF:ut%ӧYfvƌ#ou޽z;vtvvfz^QF- (8}>}h5kѿ/$ nMJeؕxYJ }hYfPHHHؼ$d%<9bX`[^zi؉TѡC###:/^ϣSO*UD/(믿҅Tre;=;;s 4ؾ}mllhl`]}FpۄSýSQX)S(_n|n =dU w}M[!_PP]hy̜"xF';byܨO FuVWڀ`-cswˋ<]=p]͓LW}ʢ˛7oY&]0ȑ#g+Vԭ[ׯ A .g[iڴ)B2қԧQ6u[֣n8h9'N:<ɓwޥ1VVVmFtB7oμ;K,7k.<Sýc ].pzw<GQJǏ 6ܷoC3^ϓY쌨O"< nT`:laO@+eL0Xwr]=w\dRe;wr͛7v~QFZhXXXX@Omڴ_5#.=+ ]|rHQ7a p'uxfggWTiժUtNY۶m322 6n6-ޞi b']pyJ{ǧUW@C?Xl)RS>s̈#@qti8SDl=byݨO Fb}ﳭڀ`-cݝTN<]=z(]OW+W'N(n߾MZZZr{Unnn{i֭{^ڵk/_}*8CZZ=4k֌/͙*5Y O[so2ӂ\M4uI8:y۶k͛tLBrfCg6oM#-q )\k.lDl/^߿Y>fZ&XKo^QTn >dyT.z*UDW]\\4Y쌨M"oT`#K)V-A1U={vNTӾ} ֭[\MÆ +V8pU*wy׮]j.s9M/]TBOx 7xcӦMa۶m^<$$_~4P~}Z -[1իW4h<3:γzϰn4~&vNgͫTz7N}^~߿Mo^w mre ϸE6mڔu+0˖8PMK.5zm|̴8[%fp>޹s.ӯ0:t̂gDElx?%C3sf[Z_i.::˗LcVVVll,(!33Smb^xAϦt!)))11VCc/j!>>^p6AZDLs0i6oh7 Gi#,, @A맞BLzzznnfΜ[hOJp.\;SIO+z|ʐjyUbE@Ej l+!XuYu͟;m`kCO;f j, m2`]V%?yu]/wxG|gR`-ceE('{.vL!}=fq _2e[J!X+/:ljM#Bۧ`O܍6MrR`-cVJpē֞W)7l rJHf{.}fc˶ kC>zʐup>]|svRs.:O@A1k̊rΚzH!u\ lAn~l@A1kaw.ϾϥG9ygC({M^Ƕ$kCMd'E]KY)j/JV|;o(ePe  +7#3ަc{;L1gSSv(lBg.% Z8q7!X5賔pKkv-F\˃-;{~f'9v;!X5HңOM ~zؕvNJeGbI~*F.󙱙kCJ$ ;\?9~m.\PCS̪wt3373 2` e277VP w^?۪&FZ/2>o=9bmV k1"#)pq(sx[kC*%4_f)%'_\mO)g@9THn oE~37'a7(2` DV| WGLps5z_kn?ٻD:ζNe%W ֪鞌xv4@)B1k(r`K?c s'Glb}k+!o;^0 r޼ګ7XSUi䫠} =kCED:x`ys+C4jNݦHHGO`zЙWnjkÝ^PVwlCO2` EY)>,>l{dm:nyu\ǝY.}f椦sRy*z~@?!X5@Iˈӭ\hhhZ?-|pkT7#^&(52` P:rRӽx{YWGxky^jAF~q- Dk\]A1kRx/ءJ=̫Ǫހ+_-|t̵֨/.X\3$$. X5@I 9nov->iSS(~/cI']\\LkC(Qac䤋~5J'` e @Q:)k`-c`I!X-kC-kN @o!X5n!XtRz ZtK߂˶nYLVlPliju.!X&;w!e*UD)J}p1kn={v.ܳwcLw) i&*^ڗ_~>FUz ZtK`MC-˗Ob?Y3cٲc4c~_K4u[}gJ↴Xou?ccT` e @ssԷl#nE˿:rdW/13R2Q[YiKaLU.(؞}FjCQR5B1k*~>zt!|NÆ ֭`6zڵk6hoU6#{ժZjVrf}Ÿ;PT+Uc֭Mnܱc _?I׮ҥmӦt{[3hf \<' ԩ E͛FmUJvVp\8 )9wn_|J]KLavMH R^`-fUi%ߺ&;[`ͽؑy| u=x`!XV1Npدޥ2?NhTr%}#4bŊ:7*EeD  I5jԗWRU$XKLſuMv.7n0u zؑQlӽԻB[2` [ ާ¬Y y)?mΜ? $'{0RP ܎PPʩSQ۽}ۜ0tӦytᯤk[3Sk[/pg~8]ӏ Ѽy "Q(8 Ս&KN!X&WN;淬X1͋&UR}ڵkYR%Jr9cԪU(q)N>nWZr.t_o] qҥ-PdnZp̷ns-U{Ŧbne/vas.G$w4 s.~ir$wD AϏ.t\zH;9%8 E`-c`I!X3`˩k#4x{OE/bGe @Q:kFZD3[.alQs~艐 PX2` [(փ5_nFfw=rW!W!B6&e @`p6 :"O7J.WrO]Åe IPȇ`-ckJ'U ߐߐmڭ9Pr ZLt*+!9͏+]NY5hنOXvJN8dbbbkk;Kҫg!?X59au 8v@`-cEPP9QF\lAgpQtlt;D>2/3.Y'745r1Gb} kC>>>qlmm33)l& tlt;^```LL {ԅ[A?~ 6_»A1kAӥlq6ٲg->)]x=R2bC{YnU/l98P!X5|Xj[1g ,;9-hwGMzZ[O[74 reU̵ 9 e  Eܕ! .}>#i| wg zZ;{4z{ʜ\@>f~ڌ o3@1kМ2;֊}|?$jˬү9kC F9}1l%jU(-r  L/'윋lɈ|[|flN~(yrph7?U~vU] 9i~`T܍l@tg!ekz ZNwq#lpWUZz?ʽO?p}ҺTJLu!^ץG%Zf|~dV'`M١P=Ja`rܞ(On8x{-F(2x?Ĵbw=(OSO=_G d1/v)P.eLX۪+g'ڃ`-?nUܼJO /]vK/=o۵qs,|K &놆fZ~;{+L+L- (gQ[wGG]e Z~ IDATѿtr&-2u<~iϢ>P}XFJ.kY >jgĈ5Kn(O"=G_[4 cfO3kkY BeʇܬlŻm2_&ܘźW>+nΊOz*Yzt*+!}<NN:bIfl"{bn)^*WLRz0/sD`iۃy.N~s WST)WꍨR;!XdyOp@'%$ʐ:Mh@;Ry*')5ix<5hh55 U₵\\\|||cbb1SS>ږs嫅Nݦ%= e!XP..X]EPP`ړxubϾ_Vc(k7zzz -rKvKJ_R`sinGd XP.U>qℹK` O{waFQ}PZxg!fC)}CF ]P R_6h^'QN6Mo>T}AF ]PҞٸ[7r\|K9H{}CWkЅ` %'7#o6#^ze@ה9w#4*B!XC Izz$oWd%$}7­\a(QB5cvT䐂h{v@VJ:9# ,4|N ޒݻmz~V1W[K~ivv/ +;)wk;LHJq{r32>(J#X8[5+QڥKǔP ЭgώY\m17W/ݻիW[dz` Z{Q7ffIRqlK%o4kְ[N޸apwWUƯc~]"v$$٪-şp͚˖MUaIS*n;m02rT^ߡ( ʟ.P]\Q[F߾#7 ?Il%Uu ,UU_ \o(_S/նkH 6 5_+C\|FS<ޣݦ Ozv@9Sτ4/joz.\]v_];NZժUV&1=v횴oGrWPvК֭ߣƏ7LLuȉ;H-]M 0 owQ'xq?Vj5_Sb;kho[{ׂ--xժZjVrfw~q2nqٺa-ʍ_}9:ujo##),Z4J_e 4nV߾xu&o?}]䓶T]}ϟK)V7zh whѣpW_t`MRiI{,U!;6֭FjCn6mO%#~ڞK5sL+ pYYժUQՃVU&0` EcПwNeUԅ .l^=zt}ݺ)b=CAr e-q%B؆ shfڵT}f\c1+WtTY\] ;w.QRzleRiIn>nժݻQ(ߐ'JL,+TV];FV{%u9s1B5A9z}Ä%͏/=oPR}jժR*S0a:uj#7L"Qٹܶ\zo7. OE[{C֨z?-Jo,Ғ%WCoιWQbBeň_r~ b|- `T M"QP(9i37;(ӺaM<˝R T~wх+G}̨Q_JdHmDʕ+|JW;uj{ău׮(lwS` پ~8w59٣JʃJY%Kbgcc(vҖ洳lEĄ[RmOJJFw5<7`-ktǎM)ۦw!I$ 4pc^e'}Pv[\ib 2ă5E+FG_^7ݼy>5u0J6˗O}M0(i3gǏ {6l؀22֭mne)>2j*4'vSP[UX ,Իz׫WwϞ_|}p~D+V?PuvիwKF0(!Cz-V3*’%Tvv+߃c[V s_[%W6j {ڇQC ,f$#j$=|x n݅&+k(PV|ҵѿ\drңlAٿ?*#&2ă2??yb`&AA/|W|ljz#E%Rڄ3>ͫ SJLJ 9)%q%l=4Y0Wҋ,Mn%Q~~bw!I m|s3>(gn-g&~Q:/_EiX F{o1_E]e\ F.~eu@k_P/k:`/ӞE}Pe]2'(sQB5qg;lAF ]h{c}00m(T T>zqIlmu܌L kЅ` ӎ à}cʶBY`B!;9 ۍˊOk12*t!Xs>燍9l@A^z޶nhCF ]٣ݦ çNlo>8Od;@QBu2}bnR#>P:o+dN;m5PrRtG.oltOnV6Px + "c3ku9ɽMS^l@1Zcr䬸:+!9͏+]tv(]ՉLLLlmm===CBB /\r4/=%P<91 |... (&(EtW;<#"2虍u!8?0ilȓ6 [[[s(&(EtW;<cb&2%7#o6#^zewk;Ķ"""11}`[A_!X˘;p5ERaD:RoGɮ؆` =feL5=*7Q(_/"Xh(߹t!XP(97|I ZQ( glQ(#"""ۃ3VK2`BX\>|gHHCs8l+%kCFPr,U>qℹK`` C>l3w!XP(95@ܘgsl+%kCFPr,kBVK2`BXQ.gigʬh`St9n;a(/ײe Bɱ}zޚ2[Vuk>7) /vr7hiGң1(_ z ZQ(Kd%|ny37lq<{_4sܪ[NE:zf&2[I/w!X˨v^uK~ivV)/[<=O̙3o;zU{#\r%B+M씴B\åڌk1ɶ7]9..ݻl DRe ^:uVՀz~zjKLQo/l 0XĉF{rFVI셶pɕ %J%~wk qxu?7o۪k޾}%6+T,{x,zuZf[A/!XXݻܩSc\"',N!X3Uسwrzz~NPݽJ[GJb]%WZ |wo:`|KYnѫȥ!?e˖ޯ|lk>9,>QѫVK2VƂȑ۷GWbd&So԰)0X8Xߖ ,%R_Qߖ~KOϩ5DP_*ӫ~Y VnEbJt1T )=.8%D)]=r-8ssMOsԩl뒣ɎHRh `v:ujUVZMdD-ii^c4x^ ~}GrxF˗OG74l`ݺY0'WFҥSifO^_ݮBsiRd1J`-v{wVm~f ' J=JO֯FFo$UVLNfG]&@k5scb\vML~r>thMV&&^$"ώbT}bܶm ZRÿ ; Ŏ&gM[=:"B #WhhkԨѢE B5oݻw^zǎF{{>}TVYf=؜Ol=UVM0vM={6vXZ@6m22^}v!ʹ-,tRnݺ4S*9>}:p5kkn̘1Ư[nܹoG}{w}e˖gΜQQ)x4D\={=D'r2ö^B];֥KۦM gj>}==ܹd͚*Uڿ% 57n=3G͛7b۸q'xhBMȯ3FURyaUbE/ۭ[6ŋsǎ3n%ns`-vp?7fĈ/hH33}(E8J)G4'ٳ55G`5PIfرcqi۽{sWiٵjh֬ӿ3R%9#H#U9?MK;N;?nb{$*q Ŏ&gM[MUT144֭[^1''K.)e҅]o˅TE!ƍoܸ17w^޽?3YfPDKHH\Eb=O2gZZjΝ;7h`߷I`]:xjNдfDgl`M*WL8::>֖槈)x4D\0;"q(e,k tgE&qW)וŻ7Ϫ'BM*ʑ4f՗/]y&R;~/]~"ƍR1 ߫UvFԅ h ĸRl0aמ[ZU`MoORRu~I[e.#H[A/ ii^J㦔pKO(q 35_QQC4NZ۷/Vuo d.g[ se[LIh۵k;nf =zhFo?>|xmoԮ]J*Ѱ[+;vʩm4͛ 0~IO?BsO88bkׂqSKb;$pgM[;tMb]A| 6z~an3̫}IJ8?mw6\>LX׋79xAtt˗/Vq_s7AkW]SߜQXUD P}nLO*,W s#:bOZaaV0G ddxҿ#}{Y)񿢢&&^Ir$J$#VŹE=| *StrEpy .R[D N#,XߊRЩJl6Jt ϗ`z1q>KfW)S#(1cK%ێ5ԃ6蕝0}~>kY5 * ֧~n|i^tRu_fnA^\cӊmR'h;s3_}@`-c0CFPbkVI*tػ,A@DD*P?(KD-[`ٔR[KKK.mRn+i14i)i='yMy&p)y.3IRq5:B><k 100xS dXZ! }|y r!Xc``(+צ2#n5֛#IŲٵv!X3Lm`lË7o:b]iYm)5@ &6CP6n'M]1 &Ä|y r!Xc``(ʂ$N^".!X3Lm`lT"#>Cf C@xYB><k 100 k%#>Cfی A1tv:+>R+1;ʀ` R|y rqC@9A5Äf `nX;qcArЩquNP |\]7Znܵˮnإ Z3gmll|||EJYZZ#e%צ6ɐ=xc]wWF2!444%%^8@+G|@f+m@666Kna ]eΟn\kj$;773yj de CLLLff&pV`0Vڌ5CCC}||@w8:l6֞3~Q3nu`fmzxydM +YRRRrkf:#䚵mt}7o;2{{]Q*_jZ#kY@RuYYpV`0V>IWEQ\jȺ?,Z-'!= n8ah3,Q+Sv=<|ݦd3Yw^+9KoP+8ah3;#W:U]ze~8ah3o.,'tO]^s܆/ ^@f (=eȷtUWI%˷m?94@ k@o7|6I(愩U & C _o$j6 ipg5f 4ťfo܀;+6ARRVNO@uR='op37? CsB%8[w0> Y`0JcQ{.Ux~_`3|e! G|V X3 m, //*)@' k@8ʳ[E\XuGdKzt@f #ut^M}n TsKpg5f =8t+-a Y`0C99A*I56pg5f iYoJ$OA='PC C@$;ݛ]u+"641_[pg5f KIWnۼ;)ez@f ®'tLiZ밥Bt@f 8!ͪx uL*^}iIhY`0Ӌ>sW$ǽ{JKK꒓IWkǩteqo۾7%;#>+6![YKgϞ;vl߾=rQzR)=Q޽{^2.+̺' "8ah3w'&^x7ސW!XS8/oL C %t"_Yr%ɯScNH_tg ; z؇#>+6Ş1f޽{[lٸq=z:tTM3fp[6ĉGq'Os 6k׮_~\=!!a̘1M6ٳ̙3 ֻw4hP&MB>o7n믿[ZV*`gg7|pr:u#痤{f~(-q8ah3Irqo&&&N>{| VVV$} %ҡC;vpںukΝ$6jH__|C}a2FRozfffQQQ$9}.m $5kִoߞ@q.ifqqq$婸8IGs2Y`0L*jO}ݛ! i???gΜ!g ÇD}6mJNJ.xyno)**_ZhAOk___MMM鰰0f{ ұcG|}]v]vUF633J@bt=&MPqqɡלLz؄#>+6~=:j|?U֞$^r;{r`yf>|:""\ܜzhf͸$dtmƍѣGow^r>H~y\\ٞ܆VZS>M&MƎ j$z%s˳*?s  C̥JddddeѯDlByyIOO'(((ϯ{dGΫCnnn{SEl>4kQ~= Cv%t)hA%' "Y`0CdtIZ!oѯ0G|V X3 m|o,ϡ?UQ\2h8G|V X3 m|ɼ4k VcgzG|V X3 m|W؏*^nXe}_z G|V X3 m|3rE7]$t`*-8ah3EEiY/0"?WMx,Y`0"Nːo*CZ!q@f xqQtRfn|$=#>+6^[5k,M[z#>+6ͫ(.5keEI= _;hotG|V X3 m6|]6 mޝLO k@"6Ưi[/+K Y`0hːoU`NUY`0h ֥4-q  Y`0hX/ *U Y`0hX'#=OW}r33#>+6 s8n]luױ|90 k@f- ZoM]*@f S|GIsxT"'o8ah3SVwuSxs4؉pg5fP2¢\u-~Os5] k͠NU5:;9-.k!%gAH* k͠9\l#'Ҝ(>-xgn "ԍtx#>+6a0)b˙ۍG-l>ť#ϝ @f Z [M6:x3[OcMLKo Z$x]* k͠ŧY=s[6xvW] k@MV\v;OZ_ .Opg5f]8<,[Ӡ ۼ; Y`0h@W$z3sU@f( =C#.)Naq G<~<]>] ӵ6#C): -xqUم`0]k3.X?<{ ֌.`]QZnӵ#>kmkD 3X_,MDW@t.kZ!Xchվnnn111'?O0م`0]k3k \%֡)))'֞wǯqvg5t1rpʦ666>>>qqq'Eqf |ӵ#>km`C/_ljjC?"R_kt.kZ!Xcht~o|"*hم`0]k3k z ]ҵ#>km`CdU,]; afZ9c\f] ӵ6C!`b9y]ҵ#>kmV`]Xv3>޾"HqƱc?:b]CVcPcWlX+U(巚Yvg5tͪ d ГdɓG$';ח,#G~$AOo&{.~G;믝vvǩ]E2km}+)z4r]'OnΒ|֬$˒Q>'SFZhNb֪UfC|})N-]:QM>wn;Ɏ{*.7$s)sܸ! z߾5o\z-zl3gGƐKf.H[(UlFvH:;nԏ?~Mjo{wDAl'ʆsԤ kG|v!X3Lڬ&:7s„/H! WLMu!fYp'[XT#_T;tx'!; G6EIr$~r={*_ݬ "aƌ٬,w/%M$@U2y\|ԑ"Q wEׁ IY]c)^EUn,/#5n`]NIMvB k[jm kZ$XK_…-Z4'ƍfDI$A2t6V_wZI.թSۣG7ʾOŭ OM]0)wsdG(%S!V7I~_<,&͛qgzһN]gY6gl7~*o!5l3W;lM4VUwewDAb'5T m kZ0Xs#:ڢqHf"]\$YgFF[qn3 wYR`bٜԔ| >y6g}U_JZ$?jE@= N,Go w[~*j_Z}WqG e;Q1ZhCV X3L쥂5\KIq_ĉ>}IV?ߠaYYl~=d'`=`@O_nӧvdo|3;[XݨQñcV7I~$X[Y!& ٻo "HaU\E-59ݦMٳqS7nW{wwe;Q1ZhCV X3Lڬ`M/, 5ȸs/$۷Zh2=6-0t.wj' J" ^rͱX[}!m۾M2ŇZhnjzp4iL^kDvΝ$nlW8e`0쭷Z?s`uu(^7I~$X_%g'ճmCR)9KnycPWQ`MyA~{u ɝI;Q1ZhCV X3Lڬ`Mg;vlch8ؗ"nɒH%S$^Ӎ'o zAdoG =zG$Y[%'H>#H~sˮ}̘ACNs$0ũ4㇒k#oRICdKGEIX/X`@C Syucfk7k:u*nH]n}M@kJnJ~A}({(ۉ` Bafk@mX*c_@}7H>E_E,| S"iY**vH e7eGF!P ucH]ݸvm/&&zTyߕe*wR@j Y`0]kk ضmܹy͚9w,/P܌` Baf/5^3{~z pfJ6BP-!+km`6d5t1r X@І@f5Vkڐ ӵ6Cʁ` BafZ9ZhCV X3L C+5T m kZ!Xch@j Y`0]k3k P-!+kmN#bփ<@5!+kmk ڐ ӵ6fxsj߉,aI3yHvhhhJJ ./56d5tH1>ܜd pm k@ [&,5Cah3,Ż8KWMXjx6d5f XEq+ʣ'AXjx6d5f dA+mDW5Xjx6d5f peŧ,5Cah3ی즫,5Cah3>Q^U[G K І@f Ϝ-t؁whCV X3 mLXw0 LO#N-mTrrr"##*e:`0<:jՏtwji޽{^<##zIRz'nzBfZ @$e6]dkP ,5SK"Xk5f䬥X5,yXjx6r~)=2*j aji3͐+O'@Nֆ'O޹s ڵkׯ_?WWW4mڴV/̘1#%% +V(^v݃ jҤI߾}]\\"-[̝;yO>MHH3fLӦM{9sL.:ĉdK===հajqƽ%*wRQQɕ >qƝ:uz葊  uR w6$QF$і+**>%I%10`D".\ؿYTTI#Gm\~$bRqvvvss# v]Ǐ+?Nej碢"I ZfMm)\uGYy ifqqq)G^^j "HzI'@ذN>X>\$kڴ)9G͙3g.*/+C/ҢE ǏNJvxy- 6$ҥKdm۞:uYrs{%"Ԕ ]PNe5RYǎn/bQ5khI24u6;Xjx'e>rMLLH: v$RnTyYӝ;wn׮]׮]lc.}pgeDj_W_رc…SL!7M653wlqS2;Qv{՟JIѣ4iRFFqEG*ulQw'I~W\.\ g}||2U^ã?:tA177ΞuTp'O|{ܹsYEUDQV^+y7mڤ 5p:8#Uxީeee]v6lXTTTXXwNs%IVVV$=4lذژҥ tzz/^%3334h@κpb=zprr"7ٕ؉ۣ^핖HEN ɖUUCfv(aKU*,5a>}֬YÝ>zhf͸$ի ,IG&z7K}1allL8٬[n7n6l6۶m˽{#կ_UVֲw(W{yyy|9ۤIcǦͪ,`0QwCMհ 322j KN/W-~dRsUDQVWx7ʢ2 aǸ5FU|_)ޡ Y`0+; ʦdIY9=ƒwhCV X3 mLQ ƒwhCV X3 mLˋ` .*'@`m k̀u>sFDWA`m k̀u1mE_J whCV X3 mZ =[ $Xjx6d5f,Z-ȡ'@0m k@;=t1 whCV X3 mڡ4-ˢؒ zK І@f F'VFWAm k@kgY3i =whCV X3 m$r9EU,5Cah3&" ޡ Y`0hz/twhCV X3 mZԺG K І@f O[Uޡ Y`0hI)Y>K І@f ӿlG,5Cah3JRq3,5Cah3V 7]/,5Cah3ZRcy)^twhCV X3 mZ,ӱ 8,5Cah3n.'tqXjx6d5fݞ4KZ!'@m k@E[ whCV X3 mZ/^S%"1=whCV X3 mǧ*hޡ Y`0肜6N()'@Sm k@Gܛ߯U,5Cah3QOڍXjx6d5f;uF`ڐ C(|lF<;whCV X3 m:%pM*=,5Cah3)Iǖ>˦'aڐ C HtX-5޽{:DFFU6Q!X3 m4=ǢgԥXj={vر}$[`ddT^=TJO0.5f"6XB]R]}ŋoD"'I4 ah3A9m 'Pgj 2dѢEt+Wݛ5hZ!h5f\𝿝B-5'O޹s ڵkׯ_?WWW4mڴV/̘1#%%7nؤI-Zի{mٲeƍԡC6l؉'GN+% cƌiڴiϞ=gΜ` ZG|V X3 mI\Xb~B^zlݻwF8tPROH$'  H>}ڧOt&&&N>{d*99YȖ:tرcw[vܙ;]-!L9;;@f t?MBݐÇD}6mJNjddmsr600ʷ˝VZ ?/t Mpg5f*Jm:O'իWs9Y_: [8X؃`xKLMMCBB:5hY`0?-+6wۈtMg͚5G6k֌;Mo^Я_p{5jHg+V-egںm۶3f jYVvKI&;֭ƍAk k@I%g9V5\j222"ɵTJUA$eggSEe5j؆;ko9 ]R;!+6x.:}0҃`ڐ Czϕ= Xjx6d5fס_w&Xjx6d5ftΐk 9xYXjx6d5f2cV?9gEWAm kd"mLʰm kyM ]WwhCV X3 m /7uRz^ ޡ Y`0gΖ*,5Cah3JxBz^ޡ Y`0"Żo7 m kŧY[KO@maڐ CT)xU-,5Cah3*fZ[IO@`ڐ C(xʃtjK І@f @pg / K І@f @Ra1 ,5Cah3j9y]whCV X3 mP-I)Y 1,5Cah3xr}* 5whCV X3 mPRqg 'fm kj(uwtjK І@f C)v ,5Cah3Kpds`ڐ C灋M]*TK І@f ।9%ޡ Y`0r3dGWA%,5Cah3fuD$'@9,5Cah3Z;~ms aڐ CBNw'Uޡ Y`0@ܛ*(whCV X3 mP;yOڍP,5Cah3Z]#j_twhCV X3 mPkIms PwhCV X3 m*|,5Cah3WQ̢z K І@f = whCV X3 mJs,Z-NJ'@ޡ Y`0 T}t`ڐ C6IK І@f @-v`]`ڐ CĪwhCV X3 m._7g /`ڐ CKEIͻr@І@f @6;a],56d5fj$m?FO<,5Cah3dgHWuޡ Y`0zI+$Js'tޡ Y`0%:\LWuޡ Y`0IN,LKuޡ Y`0@]Hr;O* K І@f 6؉*,5Cah3:-vRq=m kUO: K І@f d޷}o\DO,5Cah3:9i} S{m kTn#ťR;!+6k޳6? ]1Xjx6d5fu-AU[}Q~=Km k4];U]whCV X3fq8Vjθ!Rq]I|ot4B\Plθ|!Xk-AW_wj@UЈ}o lڠܮg DkN7=]?@+Ij7$%aCi$[MO@H24R)=-Js,Z&O! kRas`=|mG4G*%k-9xMHWYVL{k1k!+hV=u#ǡm}M{l*ԱV'?'ZQuhz밥W*$"V2uCUw0Zۤ:tR;hBG]Rq=4G_]A!Oh #w'Zё&YK8E4ܭt`kNlz-?ҦI|'ĢhH!p :k f+04UR$໽cVZŜ0'U^Du9(^NhT\3{ xZ; Xkk֝&>s'@|n2 6# V8ɡH+uN(MrK@_HZi\$9Jht>ֺBR. 4<`유CQ|Ee;"6H%-gO LuK=Γ#=sԅՇB7(.%O r&EOS|i9i(`s2sMo sjJR3I6ek.cN޺;~-]JcNZw4оߓy:*mǻ+lI+$vݧg '&X1nMqB=Zwe/euZ'=UgθTzB؞r3j$"C׬O5 :ZeD8}u1gEWL\Tb ;Wly'sOO!kDEVmC s5ʻ>zg|U XD[Xty> J\ Rl_jQ|ͻ^}.Ӡ?JsB?LuԞ zjԺAnX =!p@'>#ڲ~Ė34kFyV^sV'ْdx OYYMW#y@O@usfY;nH 2P#%e1ou:8ԄT\ql:O/ԉ.<;?kxRiה V'ti y"R*A+.GW@t1n 9/ XCm'r-=$"1 HzM-p0NEyP4G_,蓧yc XCIE 7=Ʈjyӧ 󍶤s?dv9I鑿2q*J- X'zߥ?^sv$%@IΟ-NykF /J֪~G[(` 7wƹ^eYf.NJsxT"'4.7,Ʋ~Ir=$dkO9[,Z7$;>5YRl< ˓O[Wf܆/bOW5k"Ow3<^zzV_DoPcPHN`[=Ʈ~rΪ4-@7d 6O<;;]=rQOw{ڍw%x#5hBEiy;%Q{.ՠ<ƭ5biTL\Txw6F0gF4JZ!Iw]Ԯt 3@~_И(Γ5M*@eY._EiZxNiߧl #}\~gH6+M7.^_訉|%3T_{?T1nMsV\-0lq;/ޕh*.,P+k_yNA3Y;M6=ho)6X@+EĒc߇Nה ]sb$kEOS7'>j_$k'^u͑n×n\Q?hܭ/qKӲn>j /6#تXizN ~0պ;;g=kg.O ;#WD:7d %X~N1'L 8Ys=ޛ4h=kdcE:_` g{R݂DPc܂t` ő{wZHVHmtC9O(&!4?:.=$=u~p_,sfg(G@Iڇn89w'j6$H~{Ie@ nb`hldxӏHPؤdk]ձ[` a'tɋ|/y` P=Q^a_d󐵇ǬjoHB-ܰ2bO~xtw*GܥSEWZ-$墼$;.]+];Mk.tѧdF []C,'~:5udzM_jޔ׆|8i^~O&AG* xvJR33b,+^7y3+߯,)Fu` &Ri[p3j7ެc޳6np9'(VCepj 77Lѩ cǧHѡ[FX3eb;\Npϋ|x)uHW1bi_!.Ͽ%|ө'g-W%ZZZlB?:u\G?:jUpuw4 noKOR4+CдҴLk/6bM&u>fu}8`/zH+$tI5/ W6566񉋋Z(.5nHUs-$C$}wUal_mP'A"rPqBZN̖<%^ω?8 8kR vOssV)^u5n|, >/5eˠ$#U[}>s=ƮfW?~-3[%iı\FYpgGKW|oh-GI$O4u^r4<=k/ o8qGOޝX#dklHV@eŤ9<|GH&A*?ejSIbNފHE~o1ykҖm=5,X_[ͤ嗷铳fR[jgN[nC7#>j% \/^ OJ0vqϲ@ {.ΓsV$L<3w+=gU~E׆4DN_ɓ]TWQ֛֗7ϫPBLصRwy盫O?E;e [ݻv 5"k|m=ͧ劂*Ud>7"Dnw+UVQ[dʕ+qY)UT;SX"ݤ}eew>{pmڙ}aw[o:xpM~Cy\Q֜2eOT̽_+x^Le}ɂ˪򾈐Y3]="v .]Mj11Puk׮iiPDnOb` eJ6:ߊȽI>}ƟZÝH+#_A̷__l%vDo̽]` eJPk` eJ5L 5 kT5 k(A֨2)k(PQeRP&!XʤL X@ BFI!X@@`*B2` %U&` eJ5L 5 .Xhדbҹ/z!X@CkL J5KA\7yzOIDATu кf-්NsΗ'g =`eeP(g'@qCR)(7$'V[}O,}i!'dl'^HHHTT(nPJ(888 mvv iqika`FO9zⅅűH` MHHa8m~Rp]I!qCO6z/***11}F7k(%qqqo E=w\ax/6 =)GOA(ԈM'6έGe' (kH|ሰ6lCu(5Ң[Պm4c"5Vc^8}lCo /76I bP(Kn̝M؆ڣUvCGFlkqfJuoǎ@!XHɈ{uw796ʋg/卆 6DeL<ϒmkv^έGe 5d']55b 0Wǯ  X+;%Bi3/Wb{ e{/n 5?rR= 0S5_n]W(൜ gOYoȩ5( 7'=@ kUJSDVPjs)32<5Hj2b); )3.ؕT&aù(+l!XRfe{- gca9lD XRe\6؞^ؕbG@5"Ug2oʬlA͜9S}7om<~ZիWoѢL&NNN}Zj&M߿ޫHQDJc  j߱+/23iccci;""RJJSN vڜ9s5jDݜ ̘1CP\p!!!Y|tWQ` ET^l/7722A˗/o޼9Wo>>>R7nܭ[vr$+1Ehȫ XT7q9߶m6##A6~ښt˖-ۿqYݺukj.ܛ҃=gX` C9ilKe3fTR%&&&7/@٩yhkk[Ve˖Bhg` A ɳ|?.''I&G͍dggwܹunnniii6DըQc۶ml~3; o X@ypg?d%$ qׯ__fM||<0x[›bܺt[Il W3'\Qȃ` Ӎ{w >]j{MkQP\|2_?3^m@ɸ|w(aCSO (1t_0=Rǰ (a>ӏ4(C=̱YJxۀ{9ĥ|5+teȵyw(; `@/)994,صYQC` IM6tF(AB=af4m@YrF7; `@ykvAGEȎ k-Y{ .ĵξ үdũz?; `0AпO٢PRKAS 3 Q` B~GuJxCӡ(@k] FP_.Z:XsugH`G 5kl Mzzzű/Tq}g> dG 5kl ֧W.([DEE/Tqs-mbG 5kl 'mrppU( U! t 5 UǏ c_.]0 ֠[Q"{^bG 5kl:#6޵9; `A XPe[E *RnlŽk-(TV57@Îk-(TVуK*W_֠[>6\wKvXeqsϝKP_|+zvЂ~|}Z5T^…9%Twٳ#\ZMK  ٻwgxT?ߊ=zLǎ:XXX r՚6wQhžx/(t6:;Z&ݻw;qy{OӬG]Gx!xāI^Ye \ի-Y2?tԞo*z{ګQ㟇Wp _|Œ%KQqYhs^\L/(t6FFFVV[\)AT#ÇkCL]t$lZ+NSӳDmK .fؕgjfJkϾ3n=bGuOϞ='MĎg*^p jzq1 R#k- + 3gh^֭3wٯ?~fڵkVZ[N`j3rdZj^ 98/Q㹠sV^ƙ$&zsSo*mܹ xos Ziӆ4صk[7=u{vmcz2C=G;o5l.WRY%Zܹwٲ*Q\FA*be b1;uuMWWkſj\WQx5KDYE^_N6vڅ 6lذSN.\&DFFNOF%К+W477UVxx.ŋWVN:[>y:ӧcƌiР͡ yl֬Y?~}9>e0 ۶-ZzZŊYAWҥm ^䴓YR |us;23mx}ڵIAfM/_>?:QNZe~įQu5mڈʕ+M45*T5} g1v :Ǐ]qڝY7 YEgTFFޥxuP%r"*(8YE\_l2%U7>=T"׊53x$}<2iVу^SD楙ʕ+;w{z.]kN.Snݺ)J$Кt'N䔖W'|bjjsu;w\^=KK;w"bR pڵ9s4jHz _yVQ,w}deeMjԨA~~~t&ݻnj, 5>>th;44TbiӦQ4ر#Sa5w~LMbNAR&LhժիW4{cǎ583\n͍^!33sp E +p4בdtc ]J*%%%G 9ڵ 8q">onhAӜR4{:46GiIkmuԲ}ժժ>5N*iG׮Ij-SLM{׭[gΥ_dv?I3ftrICcV~7;t-iDPOD2 2\& . ҩ5kֈĵbjģ.х盺ӟp۟|ɜ9s۷׬Yۦվ}{<:u~:7Il暌ޝo+W̽}WEIƍ)劯P(K3wy>(QF|w߿-y/b9 ]R:OOz뭍7"m۶?~ŎD~5hЀv|ϝ;Ǎkw}+L(v֠[ 66vq3gJMʵ(.L2(MT(tiK>4. :}PbEjuJ&ʍSljݺ9 6ovڠϵh]~ܿ"ƙ}{msAPߊ<ݣQޯlZZ.ds5a) 83qHÆǩkW-3JD/ SEY_lಪ/"ikLW]+k&(H<;]xUƳBbcc_x6s$Jz |qqq/_dGE$&&hvV(4$xj:ϟ?PKIIIbjIzqA J2/ΕCݢMVGCCm.FF'PAwhwȴMو{Ubc/rGH&/E%X'"x-e]+k6D.}0֠[Qk6_j;A݂`Bm:X'?zjiJ6 5kl:lMQC` A ]OQY+\4hS%v Xn5 **hΌO:il XnJHĕ|ӟWN a:_5G~ Uu|+++___BPy>k00֠<==e2~ ɉN?Ǐc{PZuG>z DEE/NO'ŸtW@@` _֟)^w`XXX\\J``Awѿ!!!`=Cfm=㌜mAɣ5Ⱦ8+m7WgG 5许8G]PYNJGAJS2+[ppl XN8.76ys/~50HC卆v`.@qܙ0Hž8yclct`@?dĽ{︕ʌLe$hחfG 5 )^Sf{Pҟ۽;0K`@Tחvn52) ۂEDO Oay!qޡlJKVB1l!X^z/7686Txoү(aC}pcs3JxlJXNjĻ `@Ǽp61 %+#[vJBY)lJ]p6 5=U2xk.]ˁװ` ֯Ivm@I{+??BoSlߤ_oǎ@k(W^ޱol,>}c4yIqm?.xJd{P}g>:dώPe%${ 6[WGN k(T9Sֻ:)=ۃRfd:}4مTvmrU9ʠ9v0Oa{oү7knmkwنa8vLNj: XoIeQ%UЉc5kxxǾ7wX+;95+qkq, +|ƬV5d?y[ʗ{[?A6s'stRz W^^7b߅P\pıŰ[؆KoM?BAGG ʌ _L5@C卆qdzoZsU*` 'NbO9w~;餜 E` WkJC#ˡYZTA(L߱+/"阬v(77>I|"@C( έF&?d[If$-:ΡY之8 kxt^hHw(M`ʂB͛9pg1A(F/768ΌŸJL9S*Gyט n>tlnv{QAJ#IkS7=d*;Gc 3k"Is61 J6hg6o`}kAO'ew 3kJAkEh l`落Ӈ#ҟ;rs-];FP㞥;<>p:hgLuK kbs}o(Rdۥ{Yt -Ŷd X2#3ݰ]gmrl? uϟySKρ˅{TmPq%;) n֗[m+'R)V#J.-\s% (Rl6[xv_ i=KzTnSׅ^?"OkG֫^A(m*=zM=SK?*L>M i  JJHf_ A|PZ*{)}%{xe*hE;口tm k *U0 UEkk(k5ʠ Mzzzű !kAO]&Q b_B (*.XXWP !kA:X?~3,,}UA| X  (*k(4k5ʠ  `2BBC| X  %wXe⭹sϝKqi^yUj\^bz?)k(4kȇ6WN]kSwٳ#xZMK ٻwgxe/@]vEk?rOV55C`:ujG]GIuaNUz%K&K *X2}/_^ڼy9s'  Q`Tq5|x?TLu{Ѡ6sԃ0'b&p;(]"v.7X绦XK=m]pZ 畝}z|Ӈ  ֡6#GUFzo_3s͚k׬Z0T&d[;wnC,^ii~3gu̟"f5#GktS[ KUTV9osҦM.[6YOSpP5O\N ZN]ۺ{ԩMnΛܹ*k%q5;VjՌq&\K쨤[`wȈ{d  Q`MQR\ۙWҥm S~ԩEQ짟ƌ5rի'S mۢիUXqϞ>}سg;v 7>m pfܣ'R9L5zAcT`U'c,6K>-smڴ=zءC֛7ny<\YFӎKDYwyzX!X@!X@> UyQoxJ*xL1kbT{ A.FUzTZEj+Wą9=J0r&c,N4̸d8nΝk;o5kh߾UtCtiKUJksukG?$J,{Xzz}BBC|(X+Agqp𡣉IOczu7ZPrv"J2 N4eoܰT'W'i#vѶXկguټyޡChH$ZL;dX0Ϊx'.,5MM{ٹsi`),-b2:ݜ?;ڦx;kEQJezzQUPhkA55eP` ` @FT!X@!X@>QUPhkA55eP` ` @FT!X@!X@>`}o8 ϟbfS kʠ  򑕐{)+?O2X~fS?9w x ~ӵ<%9PrppU(@5L&q\bPN԰jf!عQINOuz‡DEE!P5l˱6YֺZNeM)ֵ:%T'|XXX\\J` @!#$$׳***11}%AP(ʗ;As8?гltmo 5aMݸ($:=)Ugdd!`R£,ؽ?poIaO6,*l]` *>ؕ&7VImHl]}6@)B5¡{[6!efN];vPfd3J5 UvN\?1fg{v!7? pF+ PIMڱŰ_͌vaP$QLdNyiQ<(%Pe\_gԲw6k^.aWo@KQDm` Sk?W?/mm0/n'?oүIm` ;>\?#6mAJHoڔpr(P~D9]LIMg`Ro=(?c^mAVwv`~+7Uv;@CחΌOb` z,;))4"ݟmK{8}O*36@!X^Jyq}.YA^gI {=lӇ#"@!XIq(k8m3@O/*4h FܕPa ^8e"(U~ѐ'p@TOextPN']6k(%"|n_fz%CvcIMg{`Ľ }eR3b6~NIakq =0HP=tl1,l ؃rIqBre`x)3:kwU9g 9}8]*% SVb-76d2?(2_&zo"vC` #-*.tNYAWǯyƶ;UvNvcž=0 PT'7 xԈ `H^_k 5^.7rgӱ$ `b/PrcP!X@sݻOvn32#`o;434r  '=sέFzRJŽ$povؒŶ@[v5InWo@Vb }pȥrcȈ{Ŷ@dv0_6@ X虜tvcܻ~όf:__ž~m^A3~6~g2+I|{j١Y@(9צY))i(B` 7oS6fUd L`|@t`yz@?䤦;um*t{ߵٿ'?d'~WcҞƲ l*6NuFn/Bʬlv6\4'Wb@֫^A(Cl*|Υj~TIS7=sAUVB27G)va~BPľ~PRYSzZp޶?{Y OQL5'7݆``MA FP55',6MZ XP  c_Kz%E@F-.X^L&lžg?Dy5 oqIJVVV }-s/ʎB(TǏ c_KFT:~`U`Q| ʫ[kʎNB(T` U߲5 ox~ˋ5n0H=F,tǜE 2'=}uy 7{5 oqX ]n-Xuri;[d Lܺ|y=}&/-ge(k=`B[Z~$3>)>^ey)?\LPJ}AsR#]E4q5VٳIQPZ ts(k=P`fڵkVZ[N`J#ii~3gWu̟]v5nSosҦM.[6FY;xp߮ml\_fpӴ_P22/D+ӎ]usۣnq+mܹ -BDF%wODg9ows皫(=ݏwj]ZJʂ:4fjՠ֯19sqqq^tVVq7>p`uVhqLXsT{:67K~ɶrsׯ_WVcǎ`DDĀjԨѮ]QF#СCW\inn^VǏ4z-ZR''>}TZI&ߗD+]v… 6ԩӅ Iȑ#hpt"uiݺɓ'PRtme5 ^B(OW^'7j߾}m\޻wob&:իWO}5EO|]"{`ǎr11]˸t5kVox)_aW$<uis5Wi =L-] y;\(~g޾}[~rҥ %'www:J\D͋Yt'NB޵k̙ӨQ#ӠA3f( b Zr&&&Ν޽;%ܼH(>RФXIݺuS*?SSӀwh_2EۑNV~QMӧO +,h 2EW_я4b!6έMv{|vt(PV/X0Iion6~OW{׎-8UE9ٟeZIKem?}Fwaƪf1#%q߾UFy<=ݯV ]w:3Tmn> Z`NkJt~ͮ]RjW/Ax<u{5TSb'(0Iu'Xg%ݗ}ԩC:tk?T 6̍_z)\vhh(Mkܸ12ܴܼ,d}ʢM6ըQ6hY:n޽{f```GAh_V-ڎ`-v"VVV*UT}1lll{nZ~&ZoݺEsCl[G`M?݃`==_@)U5? ⇕4'6̞=Դ7Ž:Hsm[[nmkoN4?. P8SծkԨFbc/Μ9W쌮\9J[g׭M341޾}17(5<IJ_̴4/AxW`ԕ`)x$ZԂ}vXٳiӦ 6lٲ%MPG4,k:!##ZgϞ ܭ[ַ~MԤm۸ EPP7NQn9s& ,eriG؉P`}ԩaÆ/hr rrrģI0•+W8; Z%X{x޿W'OnZLX ۷[u,g #`-Οm#~qmm™k{lӦ-,etkmq7S?vlzM`Mecs6:܈z֬ԍii^TJ`4KjJ XOPah,`@1:ef֭_9`xҥKFoܲe oܸAΝڵ(/7fq+W8wSp&q0Z5睷5ko*١G]aa*3JJRj/ FF5ł*\Ə7T^t5(Rnя;Hu1YWCc`;2Ųz[D(miڦSbEIO\++hn׮]pp09qD:h&XEDZ{@Aƍ/ IC)`-[6 ۼ)y +'i3F:941Ic f[ΝKOq}hggɿ_p&q0Zi<6J݉Q~њ7;t- R4{:ҡףP:ujjjժ7XWRGkΤr;HGG`4+߫y C/qx%qDp:ŵ9 ^wE;;3ݮހ~7(ߟڸqc͚5۶mKVVVh?\xћ&6\pW`,3w44H9Y9ӲԜiR\{!"pPEq Y P@Pw =wXw}ޯs7>ӭ[ŋFFFR/YլY3:z92>E;w5)uڕ{H#88{yyk׮N:vRsk0D\>TOxv'tc^~\Xؕi"###==+&OU] \:55_}QC IIILdtt4ACDYT!RåfTtZ Uu| @!XMbvi>3XxVyGIs'Y0We݇`Jm/}y)t2<=4}^Kg[[]`Jm?V3ng7y̙}j趼åſzZ XڔkID ;ą2TӢN^9a9[`Jm5>5uC׬}`Jm 0? l0k=`VjCI uy?g5Z &twUmz F MZb=AAH wnC\FCC+!X0Hsm-a;@!X끂-wYZ6.ái~prʏϴjga]wUQV}Ēnkee>D| :ZϸD"ǡZQfKGN?XutVo[wkNS+Ԡ'=Mccc^'d! :ZRhpttjE-AY={lߩ֍X9|mNf=5 BOD'~V@!X ^^^P(X%(;}ƾה i{_Χ=5 BOt R3Jyz&11CdddT+ l *ȃ໷^zC^jkほ7=AiB://}胧"V@ Xh5[JyoX8whk4/@@p @U/Σ{܇̵{m؍q??>(ΉY @l56Q~rz)[5~i73>agO&E2@ֺ@V(yv-o~z/}v&qkj z@ֺ&'ΪoMp88a<`8V@O XhZgQ`MBݵpT*22ˎyv@Ktmc}C]ޟV@ XhZ_$xWo1vSTag _l56$;78jVd;@u <žp @6(;O9ayd":yNBYOEl@lQ'/UC@6LI!ߞ3{$;Њ4/߮Iil!km X,>_mp5#;Pvq2ͬTGqW*#(kmh8PF Labm߾],ժ/ZHqT.U |_=ev(ÝvG)L3+*T!ɋz;@І?_~aUE TٻMȊ'c;$\ s:[-R4RPB>%j _M^T+\56 X'R.-'M6!sil|2 ɣ~fMJG5k[8W8w*` >Xoܸ޽K'OFѠA.]L4O{cƌYjՔ)S5j5jԨo^$qc:;;2nݺ[~𡚢<֐^~ҥKMLLzq-ts^P{RLL㛕8qbll, {nnܴX,?3SWV\S3œ9s(&ҟ^Jd)Us*HE)1;οW\15{[̚e@@צM;rK[@@7TՋ^~:?E%;wO?4!!YiQw;v(}kQܜ Z6eݑn;w"5ӧO7nݺuF5vXH"(-jv<(,,U֪UT:3NB~;t*@Qe|(wvvfJJkjF[.u>|K^URka1;^*۪f-56`QOLnʽV޹svvvܜ{QTՋx~A<.7jhŊ«SEUЂi'Np#GM///2dg}FS¦qƌ3o޼"E.'"W8qԉwIo71J}R=yJJkjFX`IhYz`z9*iů`ZPST/b ǎk2>bjJGJ` .XD"zE?xڵkgddPm۶tرcV㿴.rɝ]׹sgWW׼'Oftttbbb֭ϟOs*-2T+(}kQ9 ?MձcG-44no՜?FG/4MUV-^bޥ讻t@cΜ9?..Njs𛣉BLpv#ĥ뗉7b>Jӽ{-[R6}왣cvv633V\S?B~~9E /e}WIN>NMM"^JWd|Cfo+άx4PIkzE>|8\k6mjذKu+++zɣzN-[Ƨ=Unݺ-^<22Ԕzk֬٬Y3:z92>>fSZdȏ,?sNZWBTȂߵk%z޽KKr7kll}kHn)RKѻQ޽h&MpZ Uu0i"~s-*L.Gixx8=[jEqHS[sM(/hB(y%J˫]vukSS!/b 6Lϯ+~o33+/Mv5TkmyIwb^ P9( IIIl4]}#JSeR"٧/k갿ϲժz&&&&''UkEeAd)^[ZNBY_8bsssU;yeL{[xgWbکܪud%?.Y}JTR(@#{=<[Bω<^+:"tQUU*56AsI^wڍ)vDvljP` k(y[CA߲Ukm XCߏÿ:} XhMw׼&3ێ"56"bER x|Ӛ Xh=s}覣l.#,ZllVv Xh JlbehZ]/[!B5hswa \IG% D@$:u b4A~;[B5ho<*Q5[̎yv@!XhʰlvU.km Xd7?M g;@X[@P^|Z\ Xh#;}<Eӳ4km XC9]Xu[Sl@PN1vWFJ~G!XhIZP(61ϊe;@$ҋ=Esg; Xho,Us{]x![À` 7`h"blY~R<# 5Fl?ߔT^&d#C#[l 5FRD5BuԱ@>]v@Д_5\iP@37]~VA$R>3#:`H4~/m㡔ii:;+fyv |vǨl 5@\6\#SQ_ ;@Yܚ_VlJFX,+2` P<(26/4Mv&(RlLn:.klD&ũk4v$;׮)ƛzoeR)l>{J/Mewm" I _5@Hm~T-Ɔ4 Pv7?~dRGo9vB(3(XӿlV}Bp F}tՏ$!X뫂ԌWѪ=yL*vUM+HdL&5vK'61ώyvCW-СZ0\+ltUaVΓϹ`5#`})E[**9>7U0x ֔3d44iϮX 2X'sy [cwŹӄŒl BF3knwww__߰D顇d}y )lG1bc3C@_hٸ`}fHD:00066}zc]'Ϳsz>[BF3+6YYY9::zyyEFFOtwU%~k k}`fǏqww c%+UG.EaVk}`fM:/1վp !#\J` 8X7?͊cP 2wu"2k}`fM1W*T+TJ%lk}`fMw`P\L"e; X+k4l1WYVq4!?9PZ_i33=FE]Hn+V^۵뗭[TW)vvtUѩq4oYmư 4nJV+NnBjp;5Q~/PXׯV^ݟXWߥNEyv&vM%Պ/ZHq8p^|d2QA*oR:|cwP Z_i onmܯ'n߶ZdGgoG(ֵhԬ75YM::549WvٗVkMH ?wwݿlZJ`}^zKCkR?5úv8"EM>rYJiWVLUґQ:_TLjiZTlfSUWڥ>+ίUQ\Gjũ`&7iE TJ`oEoiORzM6mdӯ׮ }:Nn,tv56n+N_rӉWiA>s׼v[Z/op]~;~4 诞=ߡі-KYWL+/χlƄ{O~~9|P&7_n_k5T:|zVm/]bLņTFEƍtg^5]5,o>.1c֯_tR=z\|ČOb'Nmܸ޽K`tA޽aʔ)ܴX,מ=+6ߺʕ_q_+u5n܈BwM4iuݺuBS3gB:gsZt5k~1|;w¢E_nQQQ:-Nyϭ7ЍAz-[T}A ؍JMTjTkkRGY >X^)?ԩcfffggׯ_?JTH$zJ,MG*e? rOp7{~(E…=4MG[1.Zaͤ&iťv=9YDZL1 }Kjdˀ3M5t7*-7U;ST.e >XrG'ծ]>ECcNVLՃ4Ž?չs;ťrrO}iWJ>4jd fCFEjg^5]MQuAzmMc.X[>QߟSĤΝ۷M6&&&;vTU HӧǍn:cǎ$  ?\R(,,VZj5Tՙq)ICO:\g;4`4\wϾnW(KҔ)ɓsO6gnnYyZsMItG.VL{?[1y?4}߾_k]]c9Kj6Dl7*MTjok GoDdd78863f̘yQ8wܡ݋_ٳFi[dVCU<nSf;Z_5XS{oiBn5k!L1F i4ڵk%%]=ztV ՑO.d#v+&L}Xffg:G,"َMY i7z11iq.=)l jg^5]2M2 !X_?;74XuqAAA x뭷ȏ@D"sXL]vFF3 ?f?F4XV-I={vՕfz_{M6тnOݻTYŅkll,H4ԬY3*ZYYQPbN-[VCEݻ-ؤInU. Zإ2 OBWg7]lhIٷ. ._=buQ0?Ȍ<߫׻T^r(ruҁ{E:%ΝQ]7v%xF>xFɛ_>bDAzp\L݊)v_27DX+۷/et/(J[ x5H֯=toהnݨ44JwUӥ2k Go9ۋ/V<6Mddd JP)x~~_q4Kh>Tzy?o6~\*$XJNAAg^#/'<ܑe.J+np;"ܷ4oe]1ŮҔ~o W5C3󻈖p.(S\DMҦtCFEڙjWMҦjMt( #X?>F*,r GEur8?MM !X:ݸOlUX{yZ/p ` Y3Gʅ !X'BJ 66$ P!5a6CYQmDzU8Y,P5a6C֒sU y;OzT k}`f5{mXAZ&[r+q7` H>Čh #+\ ` HX?q&l(3XddvTk}U53kP@f~4l~e{ Wdd$}XU(⋖ǻZE" \'{z3MN?a60h<=ccc٧{rkj KS52!X /% GGGNٺ鰳5h<=٧uq}R |Ա͘J`--0\vqu^h}wf =1OtW-`?: b;*$&&R C}Mۏu;a[[NyzS%?}C~V4O;MxӚM&>ruykOEE2;{LfVVdS>vTkx*+_Frv^Ɋu8j:P%Ν&=` +HϺ1ܾvXy} W[oXn<_O` U|]:a=^!+cVHV( ^< >}` wt9q=n(N.V'IA^X!kH/];g_;w [&*>` "ƥ qǃتPH ~~\wLJت d>q3kt.zYyN52[g/c;9Q]٪>+H|I>@*)^*3RU첯c۱md}` EWϱ͘'lUIrN5T跦/9`ERo}Bοik=E0P͊q\WcȔiA5-G^a tKE7[gE9VGZc3Y5\ ~VL*}L? 5gO ŵKتKIϊc 5X~sɑǜ GgVu DFfld2` X{~ڏ sN'뢇~ίتJqyʍKc}COȍq?_}[@n/ܢ. aC+賗>` $#)t["c/rj? )j@dαo>IDAT12]M7f%Uvk:]^` *iB nKV.qx"'6ꆨS.▣d}[^BJʽG|!Aۭod$: \xk~>(` BP+SݺM9\g]{tyZ?@d>qơO~L fuճK>&\^ʵ7Xs ٳSt54sW-Ћ/%~p`5Yon>IW[7sڹڐFxnӧ[P%'{IM6^djaׄoJbڷߎ6B$gwh4=Nqsᇩ?_~W(Pz=N8E6nN J55̓_pdJ)YU3gB:gsZ 5k>uQAfa/xq/Νh۾bFE]͛G{z띝ք?Y3VZ*\S:hU7nD!&O4fu) >~ qMR2޿P:Lz҂մ!X!X0XgeyQLL* (M8'%]`=~0+ܹq}{ ʔMSAei:w{MM(j(FJ…=4+ş Ye\Fѣ34ZJq|nWP1ٲe>MAA55 ɓSۿ?jo:%LOWiNٱg~ \vw?+rrO}[V%W_mءCkf5C7ՖHnl"2[ĥKPEAi_oO`-;eF_~C7UZ]N?hc|wfܪ?_Հ={-fU  t&::ڥVZ2+*Ԯ]67`LArL:ujs<}ZfM< ;hGfi >?ue?dLS3|SOi쵶m[O-^ޥgW5`Ik5mꤤQ@@h--SfG e*55 M6~~/o(/M*zxC$$U͛@^ nMwgdܤfb;wl<=;A~ŨJ jQ%K4kj=R ';:zȉ͹n!X!X$XSp5j SFAp۶hzĈլY|Ն۷/OK1yHհa}OS65cǚRמ=+d%'7s STzFW^p.T %W n,zӅ䴋fXf3Ttߦ:!X!X$Xߠd,|dpKS.+DGJF^Ox#8lCiJ6ZOXsqMQl ls XUKCaC`-Ԇ` †` :Z  tP55k6k6k9Bm ls@@ X !X!XAjCaCݒa=ůxӊ M55INC}f:wp{f_F9 MHޖ-tdzqbnkkfaс?>إq.^E2w*P֍Mv MI+f+fG?i \[>fˉӎ̑v=bŏ' {P1kϯر͘;%ٹlwwwHdeeu&S:~[:wbIjp8{ 5T!í ]&Gu3(hBAG)ٰDz9'n9uY#@ X@xbn}J VW``;M:tdcccӹ]Q%(P钼C H]F"##@(h1#K://Op',|9T*ߏ9ac1e$HHwnJ et5T49bc[OInDo)lNB&=W$61qGAZ& 1z klAҀ/8%]N|03%P1?\\UrtQ'|(Rαt8>3Pb; u7,/+}:ʥo7DS|`5h/fEj98LvwǶcS] !-( ^ǡE _Era;5YfxŞoj KwC#l{cvT nN\./`5hL& c1I]Մ"Ks>` )HϺ1vIl@I>_o\5.^w>_ :J& Xk85"F<3cByl@@ub͞:.Z XJ;awa~mU{Sq&HY=7!X\x l>]ml@eBVʽn}ONg;=/;9y XKo=4@ߤ<ߋd;*5CN> GEHT kx!CO2Ű >7UJ` ]&ldtW8*` EEFfI^wAHGxVdP ]~rS l<~b [PM&0l@X.ݦDsg;*5A{S$9ylG%J7oџk5LII aɡt~-D`7?-Hb;*5JHlG%{ٛo٪U-[Rreu@jԨ!؎Psssx-Zv ~{Hv]l\K*@A0\cV)VRѣG_}UTv*XCU://޼ysAS͞X&\~~I~S2ްHvTk͵ fqfbjYd…jb5T R`M} jא' &L7n;GiQ\e` `$ٹm& b; Rn֭[?|*˖-W^ƍ;w|IlܸT޽Knjjժ)S4jhܹ"M65i҄Ƨ[2 FDDČ?Y'[kѢ{k׮6mtwϿf͚ٳg7mڔf;z(_QFկ_,w>*Σ8NÒ'O1A]t4i`MwjiiMM\%owwijjM+SbUmGڵxJGVQz֯_tR=z\|+]g*@!XkܚRH͛I%--ufaas -Kf͚3gΤ,u=f^tt z-z)`fff^(bJ4ѧO %WN:5o޼o߾t/CתUСC)\, jo߾x-[)!ݻ<8999K6fssssww|iWSΎ >X+s 7x8W^MH8JZ&PSNj"QթŠL~ Tb o>/Y~JP>'IQ9O؎ԪU+ʲKJ{ʕ+7nݤ}厣t)7|v馟7ۧ~Ÿo7(Fy1c7BP.wnݢ:eVOOO s4T)RGQz*#t8Jq؄FmڴHE*VH!2dHAAM[ZZ6hРH|y~s-V,+x+;wB)6!Efʾ}( tQ>X˧@5F1X Z[[]s7)Rs1͛7gϞkjZjSlmm^^CU0[\ Q_.?J&KD8NY7r]<őT&lǎ\VEj7_^v3#Sr@0,'rb9(B5jhŊ\ Q"*֭[k~`%'Np79B7ܹC_ |ROQ.­Ǟ={Tʋ"|&?}^TbP_iӦHanӧOi®5T2>X+]"_5]l ˽?y)%w[|էOo'(Јb ]6wJ4a yyy;vaCCC [o)DK.-3gΤ4]|Xܹ+ 'O^^CU ֪i۶-mc(h*kB ۋďtkr}ѠAh}~'KJ)&\t^z)))wt"Q!ヵ5TD#37>58$#?iii:tY&#G?ծ]:u:t)k6mjذKtmPE΂ zdԵk%h6c(XՋV{mddDӤIsV"##MMMiYfE ktfMPHN:-[LiuNNN.S>`ɏLh5i#F\U5@ ?WꑕFQ!6lCsN|nZ|E?b!Xc` RSSә"hD|9=P,R& II(S*j ^L[y? ʺuΝVUsM.3S&;vlJHJWxnnj6bbdv3@+t~u*P̩l}~t }r߼?*V Evs룤ſYf{y tfstv8v 澸|Xn:VP?[0xڍIl@!Xo~*+680. 橃|*5ADZﷳU(ul :dPB/66 a;@$9yfP^=Kqs(kwq#ˊ_l/!UxٵKN_dC>_%boNXV4` pFnRd50 F-!X\JnS*(a}9w k8=s=[e+r[ 5Nk %5.g;4` p~p= W-@;gtx~r:[v)5Q`V@D#d4Bwϵ j]>57@8y2T}++P*E[l;VJ` (Go޳<||Jmsإl4pk7?oߏ@gŧYK_R!XJ~r}Aa͛^<l?Y@|?4` 43։M̹sh^µTmtx^B ;7(;2l@-kIh]s2B")UEB VUuW/>*V?: 5{Lj)fcD`|ρ0D eck ZYB mK ;\SckxKWυR]}XNxo(J|M}![JKE"XH>:LQ G\=t\5<N\/e^ǏoRFupkB. XhZbŲұή%X? <kE?l߾=22266V}!;92{. X)_uDDDt4x/[޼j y֔J  RN.:o85<rQYyBBB"##gjv`MPX򾘔5<rQ Yx3/,dѓmVs];zwY-xq灛gΫHdiZ8yN5<rQiX/dqSqwx2 7go(g_i37G9h彇yS3ZS` C\T~buGfm\TGy+1qv{{/˗/5p;ҙ={x-\Æ}Ms|Aۊ6`@wYYNY5SCuW,"OlbX~u^];Mo}Q'=y3\v{*,'On`gϢv*T@V1Cٶm~ԎccC>>i7mϜ9'|+!Gw_Ukx=nsݣP[j5|?TRժUߴɓ'۴GvEGGz"E I:7 mmnF;]7X]QOV˲ԧv[Kj6mɚ{~wL:ʹZ 6 `sf;e@l`ʕ4Gː5<8nƨ2&X'jnr3<;w͛/]^W4o߾]F IDVZG>N8Ѷm|NҖ/zn$^~]bޝ;wׯtwѾ,Y7j(;ͲY|ڵHQ?ف,Q|%|Q3Su_]CISl$}Y kiZ۽{j, ,M ZKri׮vPvS+ x)r|ȑfo* &W>}o /T@Rj?;F(66TRuN-~BŽykK/ռ~=̶i˔)~*f͚%[tMZ> ʰ`x=nq ڠA9=zt <<\39ӦM;v=QK,բE x֭r-[={6ے4H9m6j>>}:x{חws_Zd΄ 9R ڱuo߾Nl?a-k+FE3ZS@tL^kݣ͛H4ʕ8q#imyI\9/]ڨ >8FkU똍FXW;0a}eS^^vŋgi샵u)mG},YDi=]3u˗*UJiZKûw&XQ?ف$ ܕ*U^{-&&FxWc z<׭.ߧ;uj1c, ).!aGÆ/g]N X,hիe{bŲڱQv-k+>d7xoߎZt;lc|l1GkLܹZ4 R^Imx xwe1ڤ.X[6cT.]]l תzƍkٳgͰ0Y`O]pq߾Ӵmy{/V_O.[6Fd3]'%3G=/k?X/&%ӧCPФÇVK|@b_)R!C˗/W`'OnyTY>|x/MM~u6cT&'ݾ_4?-?}~i'N,Xv,駟־jժU?={!MۥDTTTÆ thԴܤIF%\}||$K0`?ڨN._#|+gΜiLqz߽?Q` HSYkZ#+)r=^&sq6]^5EtiRZ,u̺-IJ9 7su(~؞n{lr$֪UE|$3t FhӦ٥J=u'i5u> "YJGr)ӪV}֗|_RiVP&0p mHƚ5+?SZz7nlՆLM~u6cTڒxś]T}.F΍}_NY^55<֝LtNLLnZ\rӎ4Ν;' [^*3[IߞE/]d]߼Nn]Cq$X׉l5D| ϴ$'re,f^{dGvϷ-ydÕ;y&~),:^mƾuUO)ͽ_MUc/EmcO_PõϿo;΃88dKL7Bq.XST"X]Ņ.Q;g]IF8&$^[\% l!!!isTl~v%O'yRS+"""::Z}!;~4z#Q`` ϶}v @AAA~O-Ys~$O'yRS+22266V}!;9k˽. XóI􉈈 zsn$O'yRS+::ʕ+s˷kxXI?QQQ7j@#O'yRSKRu|}ȣmOwv!x:5{5oŚ]- ٳgz衲e˖.]Z:f̘eXԁd֨ se4$>y晾}qݴKu*]ȣmOwv!x:5{ᕾ&lΜ9 N2+H{g[>2qߵ3@p$9g]֛/_|a3ZGg[r]Gm~˰ ܫGK4v:pW>}{MWNGRGnAF5[ !]9$_kÑ眺uv]7WّGۨ/kVk_7]gԨQE͛7oJƎ+'Oiڵkͬ_?s'NlذvܪUÇJVo?޴i TR}tĈkΗ/_ժU׭[M5xN:*Tѣǎk֬Y+Vﯝ۴{QIppp dʕ;tI ~֎uFQY}ԩ:(QB֑Y|_cf"Ed΂w~tR2|q-uR}n-=۲ѲdGۖ{;+V|s+Hw}~B{k@:NZS[`ĉm>۷ooWo߮QȀ&$j>QLaÆig 2DGj,w͛7_t /P^:2vڐf%K߿_R{FQeow#Gnݺׯ˷OI;wׯ_ҥ}SaQ؉<>/_6jA׎ ,ϳlՉիWƍLݕ%=TP*dEuΜ9%[˒e]87fUӝ?<J{%Q;8c i;Mu x 6&'QH04hP"Eԁ{K{-Zh[n5-[{ٳGi=ݖEvkOv`'2lٲq$hӒ&x&9ywG*MɣZ_|"ŵ9/ uG l e.Gmv۷Ol2o}Yٯo1dG^>͞/~Pi^}}}{Ϯ]Yn.^84X[N0A~}۽{7 SNuJ*裏:Ҷ*'>%K6k/j[~;IMRJ2khupĆfNo޴:&D=gVoݕsm^ 9>7t4ـ~Ycb-~bbbΜ9X__56Mq=rm6nܸQ̟?_9{l|7XWZsz޽rҥK)S y?xرi {QfͲ5m^hFdvyBhބkqKk$n~ ѳZȁVj}5فm$V%_?7t4ـg[T>ElԴiӔ#[7UK6&5t+n ()k||>Z~ٳN:?A/Rzg*`-~aYg˖-sΕ^^v_ df\^j;!4XkTҚ5kdǏ6'_hFTw'O8[\9#2S G\Y?Mn3zVN]KTz٠7nܥK'xҥK.lF7&q乡=} i-bKUTٽ{ܯnݺڟq֍~L&;DAζYoݻZj֛駟K ,Yd96mگ_?k~gv>>My `{^&M}5JN\d^KDEE5lPV]CEvkOu'/_~Gf|^y3g4&ݑYo}m.Fe˖frXXX rέF/ [9<74ڀg[F>ElImԨQ%-Zwd}ۍL&;DAn]X6sypi֭[_=fܹsmU^zur2mZ^hF}{tmŒ"X_Įu4[we7Չ'Aah4&I$޼y3)))mu} _kWW}+)η1]ʧ:]H;5]>kπjҎ` %n?_C)~pCY@JE! {Y~Z,.\ŒN#3m9{G.8` .p@7ϨLpu@5;d1'z[p k%Z~W̶Ԝ]~P,5TU•VW"ON "X;>ng1j<:XρHOk!򵠇^}H߱j@9,teI 3|c?.5z2܅?V!X8qѱ%_wD2g ` CzcDI %_;rJ{C,>X%:hcHj@Fz7NU AGf]gk`_6\pbM35LN'S;<K.5q>|_`Y[yzEoV2m1¶syaۛ@HCYnKi@IB!;=GیL-ǟ(:k-/I|)5un߼_ٍgH'k,Ӑ?"z =D_'j@&yb@"Tuxo@zi ` l@R-f5mw|͔stpϨޖ5L K4ZQGGNY[wJP{{LW2ѫ’/hw?^tK4uZtEdqSµ$D d5̗pZh}syZj#=XSUaj8bҢ$XAyϊJ-uH]¥BwQPgڴ_glܶu9y X`0EQaqqb=JHN\/EQupqj\` N ֒!,Eno߼PHJ  پ}{dddll$G,Hk uk2*-X/d눈h%IPk uk2*-X8'(((,,,**J} e붭ze@F"X#XSQYyBBB"##՗PfЬ_` DRG(r`}iOdC%JP@eTvvj\` `MQFƉ|ŝ$` `MQF:ⳉ{ORzk uk2*w .{S1Hڵ-{,:vl;G]W&}1v}7/m]NKM*7nwF@ Xs0Xϛ7P^wϟfqQ5iBݺUn^v/_g&t`}̀ү^T ARHNLY\^x&<|Ν>|֦M֜9|ջ}'*}L(WkvK#[C}7R@F!Xs$X^wCZ%%풲ii{}SlYCiژѐsikeݭ4k_;p{jo?.(ZXO>y+1ǯgSv⭜gϢv*T@1Cٶm~ԎccCDk̙Cxa97_Iz6;{^IYm֫ucCdKIfkLoٳhQh%K>0l־Q6YV~Gʕ\3f 1Z~WpyRɝj6ZJw'/"{b?l}6T@"Xs$XKժUEuV/:ƶߣY&L?t9s:+insf\kʔMS 无9hPƍ!k"E I`7[;1o;w[͚ĵ9rmٶoߎKбc3c+{ &J:vm)_^ս$L:uR FKb5ZĶtOgmI$^^[@"Xs0X_W_RpA?$|YD ki&?{RlqLǏn֬YYuHBʕSX5r7>ۘAVh׮vPI2,k8L6 AMM\Ru׷ݭ TݩS #o8e^ʕS2$KYtncCd [GeH7Ņϟ?Oik}`mO?57ϑ^.X-e1މd۲?kC$%^^d,5:ǃV,opoW@|C9rhٲmIi-I?;{ʕPYZ%G/A$NժUEmrncCGjoZyږܹsJ唀 2mΜo,|ab`m.{mwkt^, ,ɓrh)he5ZD`L֖U:YM GRHP5hPߘ?4HR[I$8{lk}stԨ2mŊ%.OAA^޼yݒ%piqE~Ȑ˫ĩDߵ)'6f?]X"'ci?l8mHO#}vԆt/47l;&+5=/3VbY/~xQw)}Au-`L iS@f Xs$X/Z4g˖-ѫW7jCJzx]" I豾FlrC#֒8U#9s攡U%eVTA* (!Yf=4sͭWoڴvzմc-NIrncCgάkѢ\l2_.]*R[nOݛɲ/o)X0Wvkt7m]ԃҼBf}?GAlwhNFXDwx&X+s%'jn` Α`mYg"ݿJĉU|JʯηoJSSTsҺ1+W6IhnĬv(L}2Yڵ-ʐnu,uhpBfNؖщLdv>:{yh>j2 HaebNJH\b@&!X#XSQeb><}->Vy@eTooua~u2H*uE[}v S)ʨ2%X'^ ,}i/iBRG(ʔ`vv )ʨ2>X^/hu2H*h7@RG(`}=_y:n` `MQF:}g]pk uZ>8Tӫ7)J^/=Pkq5:-XSeT$r" n` . ¡K<baї}vHQʉ7mOPPPXXXTTJgm[DDuHѦLƧRvׅ:"""YVW|rY:` %=-]jE^8]B+B^ꈌU_6/A! z]p3k m$7DDD KX1{bVI@Z+B^ꈎr7˵}:n` MllDHdW-+bc8L^Wxeso7l_]p?kH;[Lίrs/qѱś8~FC;n%loԪg;];rJCη I["X$T3k vX; uRX d0z\"Xsk;Ӑ7nc(~]%_+5}VxoVWr=:KT15:47gCvpm2L{#Xsxe/,.:#Rq55nt'еv uû[S+άXۤ[=58D2TNn%>:x:` i Z lzA#]%f~C` i&Zk>r6r_.x58x;ݥ N [D;~fG#XΆ(ꡉBjx5ܓ|:+v޾6R!X#wu< ǡ -Nm$>K + X@\UkjuNR =[w•ү^y@Dt&:55^?GloƩ]XkpC}Jz6d:b6Fox:` r.tW`}ԁl/~Sdkp?rk>rmk[TZo 6\[]{8dgΫM#wX־Α|k zɡ 7lqn8VlQ7Iu5dk%i/PU dÏ\w05dl'h~yu5d_dj5d먹kjv$V k!X@;41~:tl@D->ꀇ}g]Ȋ.n?f׭]e\%HQRB:Y܈D$JV<܅; ȢvMXX\.us0w` lȎүx~ZN l` n3w 6[{Iͯ:@Fuͭo ]\'ϩcr;i]w#S #X;01#7`a#~Ь_Ţ #X8v[@M]$''^2Ua7/:(;cl` ڑSQIl'l7oµ6c$^~຺Ɲ6/l߿wtY')/Yƅur d#k0Gznm|iOdYA2 ڟ9z .]U'@A9d󣳗k7躦VS2iK%XX.>b`1t6dʉJ|M}1kHߟmT.p#-XùP_*uzՂcE;Kd:85ܑ%mX,)*;k#5J  پ}{dddllnCl075wD[izQ>>>AAAaaaQQQ {#XpGk*5Xϛ7/$$$22R}apok`Me"XY;"XS٭@@Tv+5#5݊` dk`Me"XY;Jk~=lϞEǎ}{jҤ/ƎԾa>ҥܑ:"·f9r+OU~= .7ozKo={kפPrֈjͶmv*'|̙Cxa97_Iz6;{^IYmdubcڅtW&7˗/%֬Yy͚!c~mϓ') `+LV}`U?fNX5{--Zd @߰6jrMҦ-\_Y:k׶Q.K5wIB7o"rnRʶm Fn,` 9yyy~H=^L3aBIK9s:+insf\tʔMɻ /WA=7~^P)$;oTnLV{s޽լY_Kl=dwRJJ~Poh,VuW6,{h6jժ"={۷%?tLc+u7l}7ylKI֢|fueZj$R YɅdHtoSYY"XY;r$X/\_ 1捰:}zD>ݔpܰa-$T]f4e?~|vfOxːD+Sk䬑#SƌVj5Ѧ?*Onȿv^4٪&eO?sP9>w.DRe2AvSٰ5XwGL)&z;Lo8[?vX^B2 wBͣkD@@ kL4qC:vl&!!3dTR… >H9mI>rVr%eYo3ֶ?SثW\r^Qy3۹:K'Lo)+TTA;ֽ;^bUM&+Z>`ܸ{N̟?Oeik}7zĔg"hfn=P)6o#C;d[k XpGÇKpyֶo3gN9Xnvbƌ!Z-X0Rc,$WUvqaZŋ.S~@6FRk i;[5X< E5wH$y+o&IRo޼nɒH"E % y/_JΓ',x{I<]QKW_Xɓܱ[l8xtjdelKk$ٶmveM9۷6SٰjF]vB=/Cc|*V,eËFn,` 9-w?ZR(O&]^fTԊ5*KI_'yJG$PժOKTP&0p mHVYs=%MoƍPӦիkNs3_MEѢE=oO.\pܸϵq*ZnheɺJ͞=\}uOkKo9WJR(]+ӔG̶li?U)nJ,._`mrMf*ﻯЬY_6z@t` dk`^i[+HOX y̷o^I{UN~fJʕM@q*[5ZYw^{<%ݱ*&6^Fѣ/5k"XY;"XS٭@@Tv+5#5݊` dk`Me"XY;"XS٭@@Tv+5#5݊` dk`Me"XY;"XS٭@@`}p6(*;< ր#XpGZVkb6*` ~/ʘ)5oL(I!$X-dy˳]{#Xpw۷o !Zm!\򜏌U_  !Zm!\򜏎rb]llHd j El缤x)HV[)9k@ kpC@)9k@ kpC@)9k@ kpC@)9k@ kpC@)9k@ kpC@)9k@ kpC@)9k@ kpC@)9k@ kpC@)9k@ kpC@)9k@ kpC@)9k@ kp+lo*Z!X@vwplei/֛kc.of`-S-Z\`-ґ:`` Hg%_<-_9S;TꗯM%OX:$153婷hc _9: `` [@W%Uk%0~Z◻jSkߒo-)O1j(0ݟN`-U!XpKWυr֛o6re6{m:w_ڀޫW{i+VOޒL#ͯYpy]xz!Kɹ5kV^fu_E_jc1ت&e_}>}:X/jO'V͞=EzE.Ya>F7w߶i Wֿ}ڵ-uR]|R͛ᲈ[ԃѺuC̙u?LݔR`rJԣqe_dHB>CMehPKؚ0ĸ9sN47oSFeKȥ'+3YΝ{Vf}-,G!U WBCgY de//_ދF۴iTV9ٳm߾,cfr[aPhrM1ےi٬Y]&ԣGBor!0ݻcyVVkĶvm+WNxؗ_~tѓP!UV~=gǬ\ր; Xpkv$FO3`˔)~*f͚%ZY$ \9EOZ#gyӧI.6|h5ُ֮]md1Dݻj7Mjde?Ϝ9Tϝ Tj}<:p`L$T6l SJI޹;7ΖiO׭PbcCe:ІļyhVtZەwZO= Α#GTԊg}\;=!aGÆ/g]N6kn)H6К5SehoCdlXKdڞnqZl&)~~Ёˬ+pkD܍Y;{ʕPt/8XתUErm^+R/mMMՎ n ]%w\R\8xV]d}>yruΜ9%1׮& raDajSʑ`mrW7͛rdHK&dc%J߱c3…lie}<Ĭ߲e͛` 5WISNJէOI/o޼nɒH@i4g϶E~Ȑ˫ MRەw0?2m` 5Wq$Xש5XWHΜ9%aTxe2*UrO! (5kVT$9rfs럆nڴvzմc-InJ@u @ҔHd}1*jE)n|3gֵhQO.… 7;^E+ݭ;ۻ]mjo[[hTQTVk[%%=] ! Df9ޙ;sg~9s9sf1vk,&8j6q@O:NZy 3`ioJsg1+& i3*'(.? V9QX؊U'(|2eYL2lKtUB3UلJK*}* $5؋%5ׯ6{ HM FC_<\@2t"-Q0J@w2";Te[II{ܝ#&*VCDTrr]"|X kN[e U@b X"F *&LЫWGV]FNN53@b XfJϞm7;I;0X8$`/H53@b ,X8$`/H53@b ,X8$`/H53@b ,X8$`/H53@b ,X8$`/Bb=g]mkBƧCHhH5؋X#5c!{M7Ri OAW}6[;_[w|BXdٺuccc$kBHH/]W7a+뷹϶^p4AO1z-***>>}!-DDD> lC,\VheÒ밡쏞bD[LLLrr2<;Cb Z|TTTxxx݇oW]*m Jw߅ց=ShtOKKc`gH@ tp>zgM?v?r蓧Sg:znUgggC3$/'`$r?a+4wӝL@O kl>ggc 9l>!Ё#o{WVKJ)p0%l!pj|CSv5:'0`8MNJa52d:zob\I˂uψM`+t5ʻ yl˹hcsVkg|gVߛ[$n5Ɍ~S㖰߾ Vk}%V p9WN5I9vXl;Ip5Χga+kgqs1|B^Q#晌FY!p }RV8[rRw7pƼ|)!p+kxf+[~VS  p34=&ɘɻp?< .5#p[ϳ' 2Fjd p c!b]Ϲ5׿ppH{ b@啁=n=V8$Z˻fRzց~UV8$ʹGh0u`~Uf+ 5v2ڠɱ FK}|q&Xh_9zlX/#6!ng&/g+5R"{Į b+LcgC5$X]H*c+F8y陡>'(8$E4eՔ[3m۶4hx믿K7\BUeʔ]PԲeRJլY… Q`9}Lh!ywվO>qhQ|fE', =J"[Q-[ȴiʖ-K7>L+tR͒%KѣGX5]hknq+W \6޿?Pb .`$d=XK'ϯY&%s}P׫Woǎ'NzjVVqqq^+KyįJ0ilbM+\n-Z={w}Pz XL^Fޏ>o?~<%{Kbcc[jEE}ꩧSSSԩCwK.G mgk`QFCnϝ;\rmʧ6lX@ƍO<)`-$s;m;#L3[|R)---!!AZr*+嬵l[nVBb `Yדlezzz` lk @b c|z~EoJV+l8+ kdkq7 Љ?s#([xXXh0n';[?ʯj8lc@b `Cn^xqλց9y1๏/-Vk2]Gs:Э/u;l@ 0#'%}Wӯ#zLfg xpq!P^'Fg+Ug~8` 9l5X({ZP.{Z 9 m3ǁ@ޝ15;܏Wd4}[R: v¯jkNO\\{Fl[`$@}z[`kG\YͯZg pqvUmvP܎ʷBslUiPέC٢'- ~̫lGW2PKڹ8L`2sǛ} ~j`Oo $^"Lc~/͘ {!- YBP&ĔCb n$;)eS`/?>aa̿#i?wwp$LZlKH!79tfpn SUíή8EIDAT~geuA?Obbw;$. 9˽wӑ{7'_]鶆=/F> #ݓc,d˶kGɽ~34A\SE`NǾ7pM?[stP;l,4® kǠKIMN7pHh1D$sPoN=g63BloX5c]omgơ k@" oڂdv/3BloX5c AhB~ ___?ٽh C `$DAɇט֭ e-`?#46UX8!&W a-`?#46UX8Y84VAb HDf3BloX5c Ah%C `$DAh|`?#46UX8Y84VAb V%"'Nlrek~1&1oި3 ̒C6 ͒gϲٖDm p (7߬_h"J,ѠAGo7m_^fTѶm-^?4K>m?ˆf[N9 kǰ$\WT ʭX1ʕ̘1w-r?5_6BD`m?ˆ Qu`6s>%6+WL͚UƎM=ԁsӦY={+W=K+V,Oѽ{:СgQuEl??O\)Yʖ-]3".7*l*nGWt}Bm.4i&-_|S ͒gه^ԩp;1qt1]ݫJo*/?g`oX5cMD֯.WWQ$$k}LjD_;nZ w*UO)6u+ԧجzg^&}gdϟB#++3TNW٭[w!S/M}t732¥])M0:~|pWeU3ݞ%yoJ5fB|g8%e_nVy>|9NBݻK(G2̈́hҤ]\醟'rƇ+ȦU{{/^֭PUMi*I]*h;w.1 3B,%g1иq=S RQNTI׶pSMco; k0 ~.~ի?3#1q]:uj%$"}B1c:sU~ʧNBFUo ]jժN/0>zahQRG=I[,YN?'Kq_S#kٷv ߮]S=dHOgi\J`0D# `%gt*U˳QueMgƲ 4^_*k WZ1,~?+=dvX Y^"-[Z| n}UgJ;7ިO6 a_n#߿r_~,TiNf۔o߬hǫP#7߬O9UQ?oyHǃCAWqJ 6UX8FnݤBM[~ B d⿓֗m"K[. <<<66݋^w|9C `$K}Rm-`͢gkRk&\V-hBb xtaޯ^5hkm?[~ewR6YH. QQQ!BfRm-`gkϢmzӐݻ 6YH/99 19۟Ȗ5hkmFGvv6l YpY?\VhBb N<7-pzg8u4[ . 583מ9-pnq7|mVKCb N8=~:3y9[ 58O i~> vXS;9z^l)2o{׵-l$Ԏss=[ hm;-ZԈyѳֲN)M/\e+= vjܒSVN@Qg~Ư/$ oÐV@b N-zڨR'w7+Vl$bn~[ d=_Rp3H]+W$%2گZ;l$Ԯm =e[ 4 9_ml$n>l)8=qiXl)%$2n<1[ RGUmV[Bb N͐S``+6ƽVBb ίZ{RGÇ@ݎ7DF58]GfK'?+{kW`+!gw?O[–8A3-u;8u}AꚛVCb .J͎l)#d'Up5–d'2 >;e[P5e3;:V5睝-P(j2d+Bb :|0l)VkvLq@b :w7kS 3n>gf!}yۄmlsxbBB[(Kڈ+MDGV8={o {;H@笋f*[^}!CHRk={[jkWn =7.[9i@X>d\IΘ_/d8R-'KKڈ+[E3͛5JRo{wN^.?l9NIh@Xn|U=f|6mگ_?iYvXFX*֮*,o>{kw۰&pJ,NTg!ݸdƲ>qի|/_rJ۶m˔)Svm___j0rҥKWX^zk֬-Z,X@8|ܹZN@8JbRT^ro666&aW_^>\|K7Jt(#ξ}/111T~Zʘe7dDS؊fW 7&ewd/~.R(=7ۿR);7xXP#&"]ڷo/>tte[2Q>D9?H?3#py1+{5̮6:T]'p.R(=JW_}em\kГ~Ugcʥ7]֭M6J."M^w=== ƍt]Z؆.t!yyyŊ7n^x1 jժ4Kk~*'̣^zt?NJJ7fS%3֭[i&;;g?P(;}Fe+.z{eYvl&u6Yֿtwɞ¹H#p7$30ϛ)^Ƅ/_hWG;SA.O? {-0we/ E]jpwȑEzT%Yb O:EK,ٻw/?p̙KUNj*xǧ|cƌJdyM\J-YߴSL)W_JgFh>|8/tR喬6,_]Lx" "%ۏa+ ҙ7 $3)ǣkx͒J/cyyyzzvؑ}ĉWRy>}^z;w{f͚Qr(0w.NժUǍgUPBx, nݺ-Z8{,w}wR ?~<99/)S!ѕ*^xzzC NE9G\\f͚ dG)ےܻwRJ4߯J(Q>3B _%a~:9yx0qCf]gjY8)~[ؿdmfr5~?iQFCƶjՊ>ST^V%JHQa*UA6mX1aޝ;wnr*˗{[o 6l„ ˗ʥuaÆE 4nɓB9]?ʕ+H|͛7 2֭[S'xbԩt3J) Ruԡӕ.]>JLL3#evy[ڈU>3BKWiex [Um9cv5LPmٔYeR>%sRGP䆧'ߒ\ kПdgf\~VJҘl4RZnn۷ _͛4i–>t-i e3 ҁ n޼IWzNsVY?)s'>$WL-bdYݥ\,$pުiki -7$Kg p_tq-*ō]G~k%>l#l߾}_~QaLJa+dQ?xnk++>xN'@SHeKqj#rwaؽ)l!Ww7`1ie]Ob\Yͷr s M w5;&;VM{m ?s"[$wlsֱA$YlXȸ{͊Of@rn3)_]!7b7/6u76g~=X۹}\˟;)N:[zpfc6qiqX;ϼ9d wuhJeKvO]da 5[lo{ouP|rOb d]OzR":'ܚ1/̵϶=7mwdb1`W/6borZ'}:2nsnmyGI ~^\5-0i׾m}Xi9Ab =vww#ΊzO% .3WdXLB'| k1 WxϤ ..ۨ`sa~ۆܼ3>?mzHXyYg~ӷr2&Ի;Pϥ%)\5ݾ=wA@/XII?5nɖg> i9pS}JhdcK;{٧t J~Ӂn3 z@MBЁ{}Ҿe[FŶs5X<1X_nUAzԻCb :CI ".5茐XG{κ/DX^|GYH\k!h<@.nFb ઐX F:oڂdv>!Abu 3|}})gw9k$]X{nݺXv>!Abuի}||BBBbbb]t5Bׁ!Abu paH@gX#tH\k$]kt5Bׁ!Abm7o̙?ktƒĺy׋(ST֮ʷAѺMƗam۴E$. 5%5e/P?'IŊ)>uʇo&+駯r 0V9s3 Տo!0~?]"E- m`0DRHKta]Ӊl 1{RKLRE(Y(ʖ4(ĀջU5 Cb :cmb5kݬÃu'+U8lyyǨpā*+U+ǃJvpυCC|ݺ߄|ӥwoG?|1c%sȑtfg7uk/^f*K/܉%Q?Si5`tKǾf;U'Nl֭ue_Y,߳JcCY|"mѢE~PEM'IS,΂Y1QJ.O&MЍsӦY={ ^Uv7U殲bJ!0~o %^O>Ya߅]T2|x.%Z5n3Pw˕+sz1PuB}je2$WtG% /f*s玤B bT߳Jc~tceʔ}{$^^SтZdX)VҊktZ(Ȃc(סL(ߵ۫WK fމpa }"O ”Wz틊< oJ5Ļ6L#Xx,U5}-ܨыṹG[j쳕xOE,H箴b*!*6"[|dǎ;)ر%H׮}R4n\OeJ;-S^U2$fur: Uw,QG+=Qx[BŨ: !=4ڵŊs*+Wl| #̀箴b*!*-S+:LwR1c:sU~M:VbySt)]슲Cٳ޽[9JQ3S<*V5(Wq>JQ3Swo߬hѢTXBٳG6hPGk$`YRϲeXb258qXҧKAY{N{Xz85}ܹt֭[N_@8xCzX%\,aPWNII1ɽ JK;`jgq||UݻwΝ+V7o|aaatTPPjM[,֤:`:pYmFtt}C=8@hw^Z;vNNhMLH[ :DcإK(#UJOOOAzFH/_s:ktx*ݒ .[n!!!]~M>Wv}A>.ҩHI/?Z1^6# Г-5ΔIP13 ƓݖNJvxGiK( O~6/>;cws- &L\iY?d.!v/NK/(µ^)ɣzԆ^FLL ȊҫBbF?<++kNыţL?Jb: bccdT;,yΜ9ׯ_:q_"4pd^:^$-Brr29r?Wbb'-f$6f}񜜜3gYJh鵞?Ys tݢSL7ȮRm۶Tj&\DO!tE*t1kZFdv/I-uH}i,嗚Y[RqNGlƌ-߆5k,ZaY"f+=xHˁK+$2D,ӧOW*IIIc=ܼyScWpz͢sƍ˗/J'e: Mᝆ;w2UzVƗk׮ee/)"""|||&ɹj_nq ) 8**^/^HW _'ea_\8@XpLW2JRz{ٲeW^t SDGGz ])}?{̙3[΄k▚Y[RqNGlƌčoC ,>t"k\Ԓ%K#bΥӑ}.}畝ߌ3q~L [Bix"tQ_l+1IlEeӍ?ڽKB?^QKՆ /^P;6w\zyJLܕ9.zS@x3X$}EV:l4IPPĚ2 Jg$ -[*g ~~~mӣgOMMD@tCl2a} );[sfZveĖtvЌ.~)9"졔X}M{MrҁtbRKVe/dLd36~|Ǐ^JNdKIgjSƒrGTaV^}:|ye'7to ~x"t%bX'=kH}/ԟng}Xze\`ѣGM?IEZr3g?> ))7~޽Yf ?3Ϯ,vFkTm6Xr]OOόK$d%Bc* GM/ sJ&_%|dbƍtmϕ]s[{RV${W:?N!`KLVFY\S$$$P^D,H:$}w?mRKn6++Dx:aKͬ^H#6cϏos? Nd횋Z򔱤xTΥӑ}.}畝ߌ3q~Է?<:~H'=k(Cu/0BO7\X~ɒ%§TWi 5&..NDuօ ^x?T{TrL// T^%|7oޜW^1&"m/ڵ2WlHH1%b㔔Ӧ%?ae +2.Ztڵkit]sɎyk0Gٌp3qc(^^^BbMwhۤ]w~LiT]~UK dj|?fұۘ >ذzj:#=eNʪ532KKJKe 7I>L\'%ی?q-a'ev?KKd_b~*5"R`"Pf@ k7EC9[ 11p:a.oRwn0Jrss >9R“v.檟)a_\J!dedPCt-şE-H#/ *J3T?Z݂o_)5KQe L,T)ßKeåӉ7;rJ"Qy1%T YX~^7e?LBb ܹСCl{dM{3K.YdժU EY"$Ò7GNXk@b `Hl5 $6XᚉQIENDB`kea-2.0.2/doc/sphinx/uml/update.uml0000644000175000017500000000032414206773363014062 00000000000000@startuml title DNS Update Exchange (GSS-TSIG hook) participant "Kea D2 server" as Kea participant "DNS server" as DNS Kea -> DNS: DNS update request (signed) DNS -> Kea: DNS update response (signed) @enduml kea-2.0.2/doc/sphinx/uml/buildCfgOptionList.svg0000644000175000017500000006167714206773363016370 00000000000000buildCfgOptionList: build configured option list algorithm (Kea 1.8.0)Get (empty) configured option listreturnyesno subnetnopush back host configured optionsyescurrent host reservationnoget pool of assigned addresspush back pool configured optionsyespoolnoyesassigned addressnopush back subnet configured optionsget shared network from subnetpush back shared network configured optionsyesshared networknoget client class definition from current configurationfoundnoyeslog debug "class unconfigured"nobuilt-in client classyespush back client class definition configured optionsfor each query client classpush back global configured optionsreturnkea-2.0.2/doc/sphinx/uml/assign-lease4.png0000644000175000017500000033444714206773363015246 00000000000000PNG  IHDR^[V&o)tEXtcopyleftGenerated by http://plantuml.com09iTXtplantumlxUMO10 Z* Bd-6vj{PޱfU=d߼ϼ/e括"+l10@fс9J {D̀[T!ÙRR i+7h^_MAi!eF`k4d Z1iL,s:v~zxb@Ja'JY݊_BLӷ ӷI0uI'βKr*-(O ,O*#WT+"Er46$HL%TZ:KN&q.v7Fe.'/N=Ό y!'-ӫ 9ye_|((UdQ&S rhW5 /6y dKPt~B-V(Pa}D9h*2ЍKέK}*۩)y_=Y֨|ؚ ?Sl, :C=tp*֒*ۤ)uPZw,Mҏ'b֦ԿN3忚ٵogP0<<׸6n.h|2&H|O8Ч9vNuξ@:D7ӕ4wsيc.X`(r% Qh0Qg,,y$Ӎށy42KŽuQG_jc1IDATx^ xL$ZJծRTZJ}JQRTZRk־6 ""%"Db'BbI #31F"13s޹s>Oܹcq1 y`233)a`yr֭ gϞMII=Zvei>&&ḞIwٳgg̘j*ի*x񨨨ܵU~"##8qB rm+WȅIeeeM0þ~k׮pN)/]Tɵ^^^sΝ7oީSrNRSS}||~M6i-ZHk,@ 䯿xk6jԨ$iW_}W}UψvKHJv+L4~NX|yўozHIIyĶ?\ITTT…iӦɵ\ٰa/'sdccceetaϘ1C]zjJ-zm\=z>+WuyyVZ=|f͚/^i{nf_z%///,fϟ@(6o޼f͚E :XbbVZgϞ\ݦjGb}ٳDOlٲN:vz…;vT-[[iFlg40ge}ժUsw׷sΫV ϓιqrr9zh ܻwqbFvoM Uֿ *X |b/]$4H<ݻNG}۷o/_^/7h߾}"EBBB@4ȓ3gԯ__̋^HDzZ޽{… <]}7Ӭ:uꈙ?*UX QeB& snPrOjhFG<;vc=o,6l(V&_yz y揯O>QlݺU :TyzŋhBC4ȓ'G(޽[2)Pns$Eh#LnzݬYk׮i,?uT1+W^y^{Bh`׮]beċݻwO2eʔ+WNtRI8EW޲_~UVףG'''~և|bo|͛՛k{9W0`@jJ*զMOGDD̝;WJ*.]UVRXu_n) ̛7O= ̚5KosZ5pM*͛ԫW"?5׋8Zj) .\,syłf͚)?)EPl߾~۠AOR)UKn)VB u4m6KKڵkjlW} /$~Ï?.~|uֽqㆼ8<9I[`x:}t5@?6e*Tq5kִx ܾ}"o PGYYYǎ0aE O<);󏘙5kT@Ӯ[)"tTyf_uo=JR40rH>SGG٥h@O̘1c,4ޚ6?6WwӦMи+W?[h5[ҽ[mGݹs%J)S&.]>x˗[͞V}S/طoV4аaÄeV 0@DǏFX8q"5򡉶muBjv+*UR}EGT<ӫH@LL_h^{Ml"~#/I_W١C1޽0_;wXd7IުUDlٲ={\paF,s|_OݭjժY<g̘||O?ռSS_рuw/ZX1;#(ڶm+6՜TP*omڴ\@ ɓ͛7[d\@锞Lz%^NDžٺJ~zѸV\9##~% .)5Ǐ__~f'/bo$ޅ|%/Ux*}&EE]Eݕ.]Z}{.],rꮵߗ>߯QGիWyeWsg4HPPuttԜT2e 3݉^_QAvY|B4ȓOeˊyXv_+8w\"Eѣ[GDD#رcEvn}]\~d۶m:5pbŊ+WNQ~m8޽{ uV_ 5k9{llݺuqҥK[<Ȁ&bʪnݺ_'gΜ<~` Dijժ5r#Ft孷3=qzR*Eܹs----?d>y5kb?~}= ~"0SEƍ 7}'m.YDM6cƌQ(Iŋ >\ۧO;P~4vy?|}/Gʶʕڵ>}!y`x_wԻwo @ݯs4#[qcW0lݺUIJJ29M6b?ӲeKT}KM?]:99jVCaF^~e͛2P0ymB2eZh1t'Nh/S@_|9NM4Q۷JN'=zEg₟Y.<ҠA'|hO|rm!C] 4H=#~4jsQDEEu]y#_0p@sN.T鄄(;v}Kns4`[y҅ŋ+x#%|'JNѼ~$ @R~9n"{Mt钺l2?q@!x}&9((HTyɓ'1%u&~M.;zi9r| 5mZ"[…j׮l@{gg%KCEw^Fȥ[g_=O^8qHOz -Zt͊ ?\f꽟cժU1q@XXXݻZxRi/9KܹB%㩇}={yݚ+A4c4mv5/\z^ߵ?zf*lРw?1͋+TtZmUY6uj~,Yuׇo! ƌ??|CzpuݓEqis!_ ߏ{~jf]o_;#_7x&)<*+#S>ˏ{j4R4%e(ӢF)W̏?VF#&˖-=nܗ;>ުVUjWޏ`֬boaan/dkaa!v9^>38&fozoeVͬU덒%_ׯC|4lX:?4TE8av46Io92x覕+T}BVppUZ&&/^Y~Nj>UdeŠA]\LAѢ_mB}뭬, kuk?~C:QJu꼺cG{MT53)FM;>mͻig7 5noxφ㩟^X>bAg._ݾGw4p&ѐϙvI.]hGۧO[Xl!ŋ'ΜiceeӔyѓeuqqY_w\G6lUT@Mn>~[sAZݻA Ӯn[_rnCߍgk7 v…<=Ǣcmx# *DMje+Z_H:/QT y@*\R6iREԥg:,>PM;aQlzaoY=…O5ѥKwy5)ib %~#鵤))?ZYYNR9⩝|zRɱg\u9`wRU 7dîu.[%K~.)C])\bfFjy_Z˗5Q*UкuJ.iiiuʤh@#G)T*6AGE4M;PX4W_Z&So7FEX]Oi}pPxŊ{֯x5k: yH1N/x 69Uoko| r9msV`[~~~9k길))]nXx_}U'5>>w\r26oM=?c0zU+T/9Ξu,Z;  |zTȳ iG))?6lXVr}& tی'ۯČSJ@jjJO: שѣ_߸An^-^FZv77Z+bRڿǡN-pxD6]jo|[ضoD-Aˎ;<==EO4 z.\ƵYOn##wYZZvuo1߰ab_L:u޼}{@kUR_|hiӺ˗.sY;T_Ac @.iG`G.6KKW^)[L֯_aʂEK(<>p``/.ljU'i2M@ǧ edWPx2e98T惂ԫW"[k_Chzn6Vͷfi<`¦lcɆu7lذ^x*M6m޼y˖-vvv5PʯZfÆ_/]r;sfʕ3)'5b ܭP!)S' ;oX;轣0'M~?[s":-[F4?vV7pҲ^_"ضm>h}Zzȥk(рhEϯt_GFFǧs~ko//db @. h% &SSyF4hJQruuu0o Ά8'1113%8hf׈ڢ=Y0h$22RRMq$>>^>SyDD{pxxxyg@q69IMMT%F Re[g mƖFo-mݞGM׮W_Ϫ|OD ;lضg[yr^kWz ?l}#<&7wXɾ2Lw/59ؿD1tq(ƥ8gwBxylD1!hK-+ @JΟ8% V~'",rޓyq\3$tU,G4xIɧft|ݙ֩R䲁wSɑ A4W/׺shfNL^~t\yOwعݭlvmrͰ&8Wp>B..)~KΗ׹ʒܼMLg gsnq!ǿLNu}۝3rو98QB5#tiӾcYd#<&|_quyX-Uz[=c ',q"[Ke#}OoY ,jgʝmeQ`n2i%ަ<$pB5KD`jXT32%G9o,,ʝI+񸃽'_{hfgָf$ܓ=~)"\VCVM]˞Tҷ839U.JM7 3-9kfw:{ϳn_ݕzL ஽HKO+ wӯngM}V/\}Ai {!/%8xv3"=6iOb_h.s80<pO5-DE4A4`俿h2YOJ5-DE4A4`qti!/ #uq~gM } e '}Z~#Ϛzh )?$>"33v߱c 0R=gM } 5iR"["֭F-[n=/FbE'NJ{^s (ɳhl-RTufEDx͟?EWj6y3gL4X{>hܸR0HD___'''n֮b3ui7S6]`9gL7q### D49 &ؕmc 4⼉#V$)oh_cΣB?iA"= (؉P<ȘT  ;zߥ5.4y`qgwٯ(/ :+\ё(k8lNKr0D݁Np)e 0#n( hYH=3{sN~yX.fh?~tn(`37e =&Ʈ#~9Wt5PSJM;|yy7t#`:Rn==kssD. SwR9;ZGp Ɉ(C:|R9neOC4(etF7yC4ȤܼsjJt֞cr3"`4bO\Sr@  ب;Z:;d\`<݁NpϫRnݕ'RoppWV;R` 5&Ε;uwca  xL(kh37e  xw_qΕ:uMj47`Ti׺zg//wS+4|F==kssD.0D9;})p؜e#\\`N忯.U:>v\`~JM~;  &.S?zpCn?=*=dŝ ep>B.@6yXzgnt#ugпlP`'7=vn+iwyn( ׈qMrm|Kg40sͤI祡c皃h@ q' ={ViC Csg6[S{eрՓ;D S=.)#99`>ʕ)[q_fd< O? /UDѢE{k&J4䴨QwĆ? 9`cew9F-,,NC{}3gXYY41yƍTZaYIX[7+]dٲGۧO[Ç7K L5@4h`?DOqlQQ>VVV& T6hP㏛*u\/z… yz._f4e:v9^rTLRxaD ஌ KNZ!WiܞOFjݭlpA/ x{-z%K~Ԝ_|^<])UoРJQFGgf{%t\s (DfG!J'e._^t'gVi=jVsrm h65>CИ?6SeDyҌĈ9HuWz;Oj ,MCtלeee%+w\r26oMY{ 0:DFL/^OFtGG.C}(o/?B-^+COUS?Tݔ9<\ҜFh@ /ZHXzR DFC {T}4tzF:v9+:!7wc3~OZ!*U;8̿qڵݻDb ܭP!)S]T3WcCE/GG!V^Xb{/hb佛+}{ Ԯ]⑗^*ִi]$C{.\H)R]|xGuĤm*֬%t\s (DM;gEStf|p|}K`G}/}{w3P{6F]*xU+_ꕔ䯽2shܺ{H~r?騝 US|.:_T'HO_.3EF7Y;wwqŶSW Ytot(J4;eu̓?:r7|R y/((DGDLe$&ǝ ?tÉ|+;Wxn?vޙ։__fJ#`2Uu$lӉ)+ ~N= <r$#`:T W<]Xj4f_qz[pkom枝]Nܺ+o E4yD4UjLlo?>n_qjQ_qAc\Xjq(•,Jhhˑ/=:w8kjݭlCL>L2I0Diwe/:]͆l/[~_ώ!im` A?s]!2ޥjew ϋ+>|*=>IxhhYYq'7ypwk?Piot;p-y=dDGD$]o^Ӆ[g$ܓWzS.ZsDymf gDym֞qE4  |kϱo8cSnob%vOf$%˫RDxRnܾbب; ^ߏGLZvi_;R9IK z'zn5CtԆ96`W/jrYekk.W݊h3ףv !6_%vع?} 6?ͷ+ȅ:99GFFhFjLl#CgRa?. D;Q*X{1/mM.h7000444&&F~7D@_h'w>wڥ JK?7oS!'988Hxڲն3˅ljw$ޗxwY!Oq#_V6g2KŇ]wsׁ/^SfbbbDj>>55yE4r{_x7Rsܫ|E w^^;P3h^#?Y*U KQω+n/Tc;Wxq6S坣g]u{\G49rt; A(+#R;[ziw7Ih6D7d[G+Yǟ k&nw>O+ ? `b/d[N/!K8ih<5Stnަ],  `^n9{SF͏9tB.$=6E"[Uz\6]G=s,hv'b-Cx^mr noYx~0bI-rܟ[x6EF½SV8ovUj\LwA[<'D(/75?N\ RJuyKGr\Ɠ9z֥j猤d'=6AtV5{E k;d;'נ_<3g< F&ݎ[y/E))<0qGAo9Uv7A.@ `42U?,u+ v|ݙep[::|VʳgD;}qlz,31ʺͥj$GUJʝ(h#w*̽fSVdTr 0<1B~͝3r y͞, .sWv$Eb[Wl.wg o0hQ^Ε:WN Uz+|#husNdeopw%Yף*73.rg 0Ps؁/#9w02rnn?ȳD+v,q ~]N$μsN,'N[= VFR+V5ɩrϱ\[n79"uxSUq SQٯ Ee:" "@Y*(*@B@D tRV(@@٣{蕼 &sν7ڦUtNcЖ.c7`/˩E/;ʟ{~}҆QRh̓'ORdec^:_zZe~b8Hfyz.iG#riѢE*TtrXRݡ̚5S]Lk\G;vvLNz1ԯkjIXCer9ȮaSMMbO1jK/KZ-.mۊjjZ҆궜 gahٳ[_L2|[o;?̝1L*mڴ) @,GbAX/lٲw!!! f7fΜva.ڵ -Ջl8f٢!1@(|r V[c|pRD8νN׼t^ϴC/(*5kO|r 'f-lյGvm_>(]WO-.1dvjioΌtdצyUkcRK"]:yj~C~kK,IJJjҤɘ1cv7nܘ#6f8ѷ4رcPPвerE1{$fwbaÆ)))6z)?bdʕ/G*6T0, i׮}ݧ8-Z߿_lkUV\\\iцif}Wfl%y6aװܯ3laUa,=ɆZjհaC=wfffY:3IsY='11dv[ni y P:żׯ_bݻ7N[z}ݢXZ=zk###;w,+nݺb[}lRD+W({&L/~ٝX:MVRE&ңO84nݺz:uO^F|ʆW=f٢D,~ Zn}.VYի'6[|NYڕ9s戓ݶ]AVNĴŗ?VE2;*,%ӓl'O>mܸ67KgF:zr2>ifi-=_KCtm9e/;0yyy/^EE)))iiirHoGt|nnΤt$EvbĮbbbOטnt+ĢNHOO? -Mga(ILLpaElaat̝CFQ]LɌi-jWdz%IIIfwhUg$;IOS&6]W!+3"oڳǿ?P,} `e 3Fif>e/yэuڷo_|!Wa[Ns_դOX 6?^,Zurp.K.kX =깤?[ CCUD}W'gDpL)a=ߴGh>iޡU^Cwדv}p*ǣ>v!y1_w pbv6M,&#.9]hGiTj ج ;׿ /N.<p (o޳¼_(hM>M ,#2N:R; >ё*`UGn韛arPV\R`6+;7Dꗼ?U̞7jR]!T zE|D_  C4cIs/œX{{OϔWWtqn0Α:)Ε;b1k|~a_:}NY%|\ N獯|P'w|K, m%l 7k1h{ӏE}PZpH:3|P-w\NHyφϞ^ !yǯk>0n]hGӅѿÈ Ian{3|yir7ŽG~}WS {ДE>M%;*w|8;pYN> Cڭ]#w@iOm} 9" ]7mn;~]K( ҎF堗>ͽ&A99I){|٠ש>V^H=2sW>[{+ωp9a XߝrTQ징\)/%]SIزoװv 1=) $10A>< ŭ ZK{95y}~aCa'~^Op D8CSWjH;=ŭ$3#\'#C# .>uI)ν*W&og}s]ur7ύgoyf cAv<N,a˾MO{GvrHWPxr+RV̪/Ovώ\曗!A4ӋY:߰K3q&VFs\yۻt:gu.ԯk^^^I@ D@WPϦ <t+ba#9 ?ɳ;{߀MpDD|doצŬ Ȓ3#@)<̺O[mvQ% rldn^ZFy??兹ycp/D._ s qt`%Mğ3",]U긾ty[̴tg~j{ϛ_g(Ȋ[>iކCo4KC\z.{c sr}=/&!o}cWp-[aqvo|5#df߼g ;pyK1'WN1>xuVWiX1@&U7uӬu}W(Syb}wtpٿCSPn P^>>aWޛۿu׵[YU*׷|sp 2=깈9hz\螟)U^c8oo׷VcM-~XP((_tq~AA/}VT`UMG _U \>ڊ3bPėKlv%q~#3x~g^^{9ɡ4A PRa.Z]VUj_VjVi%ja$Nmݽ[K~ {p]jL\kalyqr2c_F^bD-vr"l;ӏ@3ZG|.fFT|w l}!kwk{|{j{r1+#iрXtKe'lψ 0heoqGg~ZS*kw[JKIO ;1ow-[j;FK ;{kD4@i+hFsE_^UU/G[m<: &}tv;1ou:! hZ4ZC[φ}vds^qW! h'ϹTjZӕhɵu6 3[`D6M}WV7w\])Jm}ve(mD4 ,dzꀀC[]7Ot\+nJf 8qB5R3<3 " h֣@)# hDP(mD48J[UQQ~L{mosNL{o,Ӻ4+Gh[F_rDP(m6Faa+VP즛hq0Ooʴ~-Z&fZwft9`+_پc6cYٳ-GK4hfK4j՛jժt鴨(0߮]Kӑ?쳷L:f[zʔ&n:fee϶-8J-wP–-v[aah>#uEi+}ltm[VR+ѣXi}#% n[U+cv]hMG0LG#C4@ij4bŊbgڥc [n_/UVUoz{ -m-=<~jĉ~ҥzp-׿mygO>.*ٻEn: .FtSmرc=CM { )ѣ}vR7KGhc_j֬.]+Efh5bh [NΞɓߺ&+WjҤE댣!ۖð2ܳGk؈ Pڮ YBǚvi7Kٳ'L2RJ |&;w6n\ߧ,m"kkw4p`w]k]F ;vۏ(?&իWkvZ޺u9@~%Wڵk)k+yfV;|Pq;77D.\G1fioz b9[d/9VZOrM,E׫o0_ۭ>0mIY:{h 8JUV,k.6u݇juΏiXURqܹb|#ѵ.ցMjԸyII[oȐ^ݼUޤsmśf ޺usQ+Պ+Q<;poVJ$аO?ߌ`fX?Z}ӎyRl4`IY: ^Qڞm(vWʳ7K{rVt%?q]r Z/,77qn6р'e0tlq׿ҥ EC{{n۶dʙ?I5mIIcðe6p(D6[]/7o޴իW{ZXUTnңG;N^}7?|ؽ{ZkߴicÆM,q,~iرAŀ:uj.^732U?Bbؔ)#(a7+Ghc(, mJ*bVkpEY:Z+)Vְ45jܬD61ah 8Jр.\غ{S| L>.'gɓ_ޖO7l[o X MhMƙ3yy{MlgoftmfA['#Z4lJh/] 4}mff+8J[MF4hF4@ C! hDP(mD48JF4hF4@ C! hDP(mD48JF4hF4@ C!i~#}.vpD6-hD \(9gtV-lzuo |bD+Kie\ʕ+###/@i!ȊMqA>[Ȑ:nh=t/W_PvB6, CDs R'^/ bcch@WXrd?kK׷ݣ^}47j_J¼|yb!!!b]ḰJx׿*8qDRR(-D/;wkwZ/O>6 rR ,+.1K(u/^ 666--Mlu!Wy m/ys]_ʒ7MRRXEFF@s R'^/ rrr/@i!K [iWdȚ;n|}owr[ٹp*ρoD[df' ?ʠA7Vg#7Ry83@NEE?;0.L(?vʿ- : />>/~ըggwO+/]h*@NEpn陱;|2?55z辷Zw9y(PρoD|3OyowMY>?(H9Smh8/(|ҼMOwyK׷#\}aN<`;q_m.c"-NJR)H4r>ǿhq@@15 8J9Smh^aN>ݛ〧GbQ–}Y9P(?vʿ- @I=xmsyϭV<A(?vʿ- @ʊM\kTφ1kR/〲)H47\a^~[ "KG)H47Jftw8\y䔟`;&yv-[c @!ρoD`M~FVj]CpG_D9)H4fgdEܸ$:]{j"F9Smh 3;uSOoyf{ᓗ.@NE(*ʉY r9ТE*T䎫póìY<==媊qƷ!P% pWޛx s9Оbr26Tw~xvxꩧ&M$WU|#-PDʑ{h/bQhy+́ ͖o_V6{iiCKu=+]ઇw}@voWLYy[TdFakw=#@=ɓ Rf3gDEEon֬62::{իWoѢň#7o-:v8od,,l+Dl+ٞb.n+mw-M1 .wk٣5dOHddX? ~a7)p&I;ƒ|~k=o|&w>Жj+byv=<|ۿ6рٝ/#7o.훘og ʣZ`x ͵ -Ջl8E٢!+2~ٳgk{+g1j{k֬o1=ZKOQnݺu=''omАg h@~zkַ?K$PBрMŋ;x(kwQF}V:tO H;h+5k~'Z}vV]o۶m|~ޖ6T/ThLYX[9S(5DR;۵u<9qۿ pL6m4o|ƍ999ї]wձcǠe˖~ OwA EQ$++K, cbb4i2f vXʣyxxX+WNOOUOgah]Y>,]vwzf-2Ygggzx|Mbㆆ lyPj ϮiO~Gf,>Qhs-[7N_ܹXUXnݺ^^^rJ{w„ %_ٽ{w-0Ի[ܭVZ=㵑M6RMu G8qR,Pu&uԙ>}z5|A+Z_T}f,͙3GԵۖϐmPuњ>""bظa-J 8KY߿ȳ[tF z2JKK3j O𼼼'w;IIIEJQ+݊7biCKuScY-2{x~xf%&&^pAtOFvox#+oj 42>qG!_{Dp]7Jmщ p)NY%|<!P:۷/89vaס>Os\ 2n7s l"u>`n7b< )H4ĭ -Dȧ eE9SmhIaĆ6yn.@NE#z{pP̪r)H4>͞?ԇ_s l"22rW'G)H4 /.?[)K`;Xgb>C9Smh@I? m%n 8@NE0/7K=:Z]!58@NE7܅[ IV\Q)?vʿ- /eO~<}LHûH\!;%ns ~!p?W^p y Pޝƿ]~@4р0|b%+bbU/7tvW[g̙k֬ygo9=Oz{=OEZ],Jmw߉5jXtijj{-*RU\32ԫW/~+w}$ (Rj;#*^c Ph@Oĉ3fW>~]Z5K**P ("\!Ph_psժUK:ta?>#F;w߿mVc R;?\O? [hlyF[7n_5x şuiѨQYf?]ܭ[ĉ V\i:]vrw5jڴib 8#!Pw.H D,n+vW կ x^a:m?g^C+Wc>CCCԨQCrrrlSd3zjժ~sѣbŊkϞ=yyy>lJij?~|D]lk: EBƍ/Dž3"`NS>M ]Nh,555$$V-clTPPp}b(66 H"B^^ީS p^DC4 pNė$8 (w6zqO\C"!`K^J{hJh}/q낶D؇h@r]Q @ egIgnpTDP"DC4| 4*10LQ @ ůŠ#rhJh}/wIJpTDP"DC4|YUPQ @ Ebpdf%@ D$hz!~,ʧj ڪm6˘/~獯}6%l ͈Ȼ(?I&`Dbpo|SeXXИ{FL[:>0§i?qj_7KtqKY;(?I&`Dbpt~ի<vzOQ\_{5,w%Xٵ[#\ŏ5^cN^x+B4>OQ\Gy|=$w\'鱾;}h{o{߀fĬϽ&nQ~L4W= :ʔб?&59}?te!y8"`'D(S 7=4 ;WtI;#-jںUw`aN Q~L4W= Ȉv\(uYOfK׷oyf+Sb"`'D(S 0GK(S9ɧxm1㎞qZ؅h}$ 0UO1DvGf.;B1dFȫI#S#Jh}$ 0UO1DFz6y6Ap0X;}>Q؆h}$ 0UO1Dx)ffÁeMu 8 Q~L4W= ZצUg|_<2rƥSgn2Q~L4W= n>;|韗!w8i=뻄-w dF)h>ѫQ鸞rKI0׫I3{tr7`h}$ 0UO1DMY$W٥Sgw0qsD$hz!Nfӓur;ͧicOϔbD$hz!@Ir餲ݗwL{_>(D$hz!@m|Ӌ=zz5]a܇h}$ 0UO1D(Iv$W}.c|/ dF)hKز?}sRu >{߀r+Q~L4W= Fy)w߸[("\Y5R#(?I&`Db`?Kw{w;P dF)hsC s$yQφϒh}$ 0UO1D3]R;ʟ@^q'D$hz!uBFiQ^] 9YBA(?I&`Db`]WK[X`F3(GI2#_C4+.llVlQ\bP~z܁rh}$ 0UO1D$Rֺ_OQX7U\Q3{P(?I&`Db`ٵ[})K#\.>'w@iD$hz!$φ^!wı1(OGI2#_C4N縈u+(k|h}$ 0UO1DS#ŊW{ɸ8!!999"?:vܳAd"`'D(S VP~ dF)hzGGΰhSO=5i$~)&WYڧ@AVO~I;h}$ 0UO1DElY ۷UGBʍxȶpjD$hz!&~n奤Ku,? n_m۶VZV6oެ}}};uTj&M?~{VΝ;7hРz1͛7)2{9vTTTϞ=of͚yxxL6M:'֮][sŊH=}FGGw޽z-Z8p@NݛQ+:C4>OQ9I)^M$l ;UTqqqqww';tgΜv;vUT Ē~ёbAjb[oӦm6k֬#Gxyy}Gۅb/7n\Æ L̙3-[ݻwHHHBB¿w~:Gw7m${Z*A4PTt!] 2h}$ 0UO1Dv01|y7n FÆ SRR 7뮻ı-[f͚vDǢ., `'D(S @yv}?Zl9n89sjԨQT,֭xԩSg}+VVZ=vda+iɓ'x 7n|"c0ٹsg1ROQi+SB^߰kCarJ `'D(S @9d7NbPPG4>OQ&#*޳KJq۩;Op~D$hz!(WtF/7@Afۺg%prD$hz!(WtkwJ`3~\ dF)hrسAsrn $WGI2#_C4PNgdųn[`x®CrΌh}$ 0UO1DDȈ{Z;{G͐pfD$hz!(ή{߀KYrn̳ koQ/wi dF)h@yYI ;B@i0"~n E4>OQt[=rج dF)h@mbQqP@)ʈ2GI2#_C4bE*֥rJݦ_OزO9 dF)h@Uٹ~ k܁pxa̖pND$hz!PUv2E$;` D4>OQ%ŭ i|^%eEs^  dF)h@= ^{';Pv orNh}$ 0UO1D *Zr/~,WᄈGI2#_C4p]~܁a=t:Άh}$ 0UO1DN-)ûixstac/Fp6D$hz!pj{G.z߽ X ԯk* (?I&`DbWAv[ sra 83{6U dF)hyo-.|{;˽&#I?y gC4>OQwwBMزOQ%+6I© dF)hyo-τ iœ}GÌQlsy/w\S!`'D(S рZS[^Mt|k s ցOG|D© dF)hIeDůN|Vl٠z۾H\ G&` * (?I&`Db%^^On<ҩwp*D$hz!pR;OBUmn^%yN~k7S#`'D(S рUzsI+mnVp A4>OQ 䥤'l N߸gu/O>״o{uz\ `'D(S2L{4m+nyGs*>I&`Dbo4pl []i2o~as^}>pD$hzрXti2o緸:l4~<r΃h}$ 0UO1D4% xΘ'm!!!b喔$jHaN;tr(?I&`DbhJ6-gb+jˎG=d 'A4>OQM2}ʕlh3,y1 'A4>OQM 8->;*(?I&`DbhJ6q׵rNh}$ 0UO1D4%G_.94e\ `'D(S M%^{ZI dF)hdsh n}6*(?I&`DbhJ6RZػ6࢈{mjkںōuVjkuV;\nںWցAD {,e"{mܓ!^ɛ9OHpL䫠' 4'Ɉ@GLiEiiAA']W%vX?֋I۾~պx4|Zj{ŝ;w*Q@dD B~#䣁@.]722*S|y6mQݬ={Z^w{mn={vPk+*h*,\j]t<`l*ee7֮]s l Siaa;r"D I2!?S@@IU+<:|7tĈW^`A;vxyɓ'G7ߘ1c{{q֭[B֭[geeGG$='t)/k (up]锷gUŽwƎNc)t}0xfs}~ȺԮ]g0;wf ?#UVAV\-eZc (; uxVM}قmþ:vƝ;>{vp@_MVnG>ݼ|yٳ?ԣG{M~4mj]766633֭[^XW^^^Νz-ٍ]d2sss;uTv-[ܻwёR (n}ʟ-_>o޼ (6P={hhhٳ1r9Hv͍=:t@4I2!?S[s9jkl1ɖZvnks-dguޮz*Gc۰b/go}I4orΜn?}z}9>*ʍe̘fMLʫ]*SWD^p҆ DD wty_xw1ABM6n^yG[7S=j4b4=nrG[)|yM~4pgu>}rrrXeÆ *Ub7|}}Cdaa!lo>v_yw|drh@xƍ^^^` TR Ǽ~:;΁:^Po$)hܹ\۷/R../- -)YZ;4ѴzԲ k.իWa Ky/eڵscWEр6^>>JvXKӋGr7tho;}˄j9ړ m֬ʕML`ukעEc,]<[/P{|E7ʣ,.[.TtizxM󣜯*m&,έٵ) ݓ'O*Ul\|!6nѢEr+pр1ܸqC#7PA~hDȏzĔd45uHe˖e7d'X.G hZ=jمT6;w^:ĉM:'GH|}-(׮#hSӮuԜ?޽KrW N*ٳ?g{xq޳vʕbq; _[-|Wm/!BÇ*4=рenj뵴dw2.+.[LRC4 _%Tk4_;%]nݺŎigg'w]V'Ɉ@GLIFlM&&CBEE4fdd4lXe]>m׮mxe\e,/I+WKrө] {)vݺ? kW4]6Ck=W \dԩSSp-;w~y#v[k9+"n{.*ɾm͛ ԟ<9kl\Nëh }G󬬬-ZݻAAA=zhٲ. :thРellSzzK р[>w;;fDDr40yVZ%&& w)ҴiSv^^^R%/t hA~hDȏzĔd4zlذw9TX2e[.Y2-ƿ%^~.ӧw%"w@M˴*U*=x`++L0g8'!!ff=իz:`G~A=5kXrNES>ڋv(w1..;U4]ڃ|ycu=~z,V|^k bgyj?`Ҥw/hoU8Dhyܴ<{Q͚6mU͛7*!yQ4ݏn|ϛ7Ol߾rmzo۶m;vyOppG忩$ӨQXVT>0SSS%XV-GGGn{{{7kXV ʻXYYU9E Њ$)h@^֭ R ]pڴQll)B=,k*|vW-[v6WZwN݁Q^n%۷CV¢Wtl_ў-=ڋgm䡬ڜ9F_޽Dj:rc_H.e V&(zwQ%%]7npʕ+ 1t1Z7-O^9_-YUkeŊz[Kp='$$U :l)|j?X6D I2!?SрО?kx?EʺꑑG r4vL*7-{eQޤڥ^|\E2Y[<U{Ec{hl_r##P-5Ջ]ꓥi06;;Ȝ.զU_zHC~hDȏzĔV4@X1}!;w@4Ґ$#1H6ݏpq HC~hDȏz @#t?Ö;*D I2!?hdh `vU@dD B~#ɦ9U@dD B~#ɦр̍!|$)h?ؒ MEU-*ZjMVhg\*D I2!?SN5]<ϏrWA iO QrR.aî+6UZ~ikGYr:ڛ#[YY999y{{ߵ'3ૠ 4'Ɉ@GLQEDޞ-Ɏ#>p4ˏuBQ`߄[}CFEEߩlIAXO%D I2!?Sр+;99_-n98ߡy'W(oBȾ!->>N-U|[i$)h͒/~܎5|&dߊ2***99N-UjNЭKBB4Ґ$#1 dzXXX#v?TZ*웎W(oBȾ!ReS<7>@4Ґ$#1 su; 8KDCa[Ŕ@4Ґ$#1m{L*#cWxdžC* D I2!?hȽYZX4ߡNMU.~ؙU@dD B~#@M8n|lKqK*Pt׷|$A4PM5)[1'/x@4Ґ$#1P}whv-[Us章' 4'Ɉ@G 4ΡZL^+_nކ|$A4P4˃]w=uW:)kB9U@dD B~#@x|oy2U. ._.sΗ@4Ґ$#1\Pf}s;|sm71V_=h!?IF4"G=b !y̭qm;!c ԝzkPV|"_=h!?IF4"G=b ,/W ͹ȴ ɲm*~)' 4'Ɉ@G 7|?̾ٛ흚HiiaFU@dD B~#рdr̽7{mq՘'|H :[ HC~hDȏz c~? wm?16ގΰ93y_h!?IF4"G=b H(R(s{|H|$A4 \&gƣ'wx LZfy@4Ґ$#1$x'3K=W4nU@dD B~#J )AwOl/U }d&U@dD B~#M|U=r]9iUM*D I2!?h~P;#W1bի,XP~;?^?ydѵ 3&**-ZTBjժnѣY~}5LLLXgj{׮]۷ 5]IDD+UԦMc"B';Yby(I@aG\\\~7nÇBQv͘1cԩ5klҤɡC }lˆ ZJkl&!V{%ruQ h ܥ9_=h&jjѬ[̙3z>))Ik]l3fo߾&&&B]ع:v8pׯϛ7AejA4_ j_gpgQl+}a7lPR%vח=e~}]Zo֬Yl.r- +am"y]WA!iрT-?OFuڕ-ԕZ~v;w:uJ^`ƍ^^^ldcgAZ h@[Kz^WSnJG(_%m6ᇷ5{:7'O|r4ZW"Lnܸ! h`ǽ*!D "PG2oݺ5>|x\\P,ܲe.[K-ТE}uj /TeD0(flhrEoYZZ p~~+~?QM/.]*'Mvz[nݻ hqtV{atFK4G3e#O*U($T [o>/ej /T]|G셢wVVV-z}ݠ=zlْYɓ[j8r4|I^XqCBq%LӦMq>̦)4dveH;@!iDZ~4+ddd{ddd|||ƍOvgw6mnܸꫯ؉vԩuΝceG*D h@ ?9U{$-۷7op{+Wnm۶-Scǎ7o͚5366V_alK]MGpttW٩ MWbeeŦ E h|?]!@aG III]BA"DM?۵kׯ_u벮5j(OkժRBA4_ p'.|d?OkV2=+yddd^9j=BNNNBBW";;[,\Uf0d?%&&&''sE?233e2Y' ; Z Œz; (KP,7-(Avg\&"w7~z?$A4{&_d|م>3]WA?!iO QD wCOw'uoy2j}u[ $A4=~٭e "\5ӐLq|$A4 Hj[oV|>d-qexf_h!?IF4"G=b |& \*yR9;@|"3_h!?IF4"G=b 0iaѶfD-+>ѹ|$)A/ iO QDL?WuC`Cbݯ'25㫠 4'Ɉ@G DcP,AUg@dD B~#צWuL/& ;@aG*3D I2!?c@^FC=G|:dDjtۇS=*3D I2!?c\WuUAgorS3lʲg@dD B~#=WuX g#yR^z&_7{wA]߯VyJ@4Ґ$#1 }8%?_%$')o#\<7Y%xc<۪i1w F)o4Nz HC~hDȏzZ4(ұPLwRKKs3n;|׷FbУӭ*i1>W] 4'Ɉ@GEwJT^F%{zrrSَNu:ue?^a_ %@dD B~#Т33ϓKy1|c__peFMFw{CV5=Yrb 4'Ɉ@GAE]Z櫆!ϙ]Cҭv7 _g ͮFJ}NVt${  iO QnZ YN'|3iejȓSo b[YzSQ r 4'Ɉ@GAE:5ܴm_l^xoDpwn/񳋔?Ð!iO QÉ#czXG ^)7%=x;Y蟗o-jƸgY@4Ґ$#1 9;e _5$)ag\`[Vc VَGT}l'> HC~hDȏzN4pe`ILyv)[aژ۪]~j 8YcV:bhb'hN.4i9Iׄh!?IF4"G=b $Ȳ77Ul[GCC+ h>IF4"G=b $`KnU#DuɷӖE442? @)"?IF4"G=b $^|l 5\~ h[EkDPO Q.|Dhhׄha.cx?a! iO QCy2݃h  _ޞh!?IF4"G=b !xqk|U'!@C+&DK[YY999y{{ HC~hDȏzB4hNB4V|M HC~hDȏzB4?GOUh (]'Ɉ@G!DLg^: Z5DPO QCeDKyh (]'Ɉ@G h y]|UW!@C+hJI2!?C> _C4$! <>xmZL&c_gffrxΝ;|p^+HK :~&/j/cI^MN&jj?nj]w+U .dD B~#|4pg[T܍}5jԠAOOO {Hr9QtyȨL۴yGukݻj]S{A^MN&jj*,\j]w+U .dD B~#|4fO.xpux@͛ѣС5K~ZVxպq{Պc&'}}5Z]iB-] @"?IF4"G=bGc~>Bח_+W,'C֥wGt3fϝ;ㄇW I+Wظq_~ֿGªXx*Uj5]'ۥR V˗.*ӧٟ[[oѣ17{ͅ(l?z߷ok0iB]ϝڢhJI2!?C>/'9Mqw֬Yl.e_pw߾}쮿K@>}rrr 6TTݸ~:6Pg?rW.֢R-ㅻ:25*ZvakU^ulR쥼8gO>钖|Xt\4`l\̙ӧؗn]/{Yƌ l{Ĥh@+)z9 ։pvwڕ/&HH\ƍ߫KVnu IčDžz!ݜŃ˅yfo*c֬~###,|(Mj钿5>w(]'Ɉ@G hD?(GlPL@@pߟ=y2= Dsno۶Mڀx ^s{o_\d.䟚`قU+NcaMр]X u^ `Ȑ^{)/׮6hܸ*(je[W?%;v,V#h _Kus:7F\j9ړ m֬ʕML`ukעEc,]q9n5%5_'w:OCwօ ;ͺ_꺧R 솑Q\Ev#,|(Mj钿5>w(]'Ɉ@G h@kcrE9`_p쮷˂h`ҥB}ҤIڣ[nݻ[4>uHe˖e7d'X.G hÇUTb̝;^y/n9wĦN#T$DeVBD.>'Դk:5ϟwRj: m^/^\aݾro~U\'w:!:|x^?Ğ#>vg[Z[ojԨM?Y Ph:+%kPNTT @"?IF4"G=bhG*W-ZݻAAA=zhٲ%+O>W^8|hMxyy>|J_S4lmbR>$YQTDQQnFFFÆQܼk%;vچ}E/s 4ihre?$W9]avnZŸ?{h@;8lc:FsWZM%- ZsىwhWKrRR<ٓ>o'g)￟XVMrv]Ӹj_URL_W,d9s99 q63Y^T/&`G~A=5kXz)A_v;sWVATOZ1?v^z[*>qv}oKt.B5mÁ7oTF)7a)#O?~nkR}r4-WנS[.X!E~hDȏzЎ29={9WIHHZdgg+>PDcnLX 2*UڵŖӦb WVgKYaa$PC\zlٲyjʽ{wؽW[6-پ:ߪt:l]ǖ=zgC XvХܔY+ҥϝڢچhJI2!?C?hI6V>q␝;7oB:57o5wjS .dD B~#v4S|Ux4pwغ{vfrSFۣGÇquݣڅS .dD B~#v4¡muGhh @"?IF4"G=bhG9iv5Uh (]'Ɉ@G h@}bo _C4$ Ou4kt$#1ģ/mgU]h (]'Ɉ@G h _C4$! 勀|UW!@C+hJI2!?C>V?? Ͱۑ}h!?IF4"G=bGGLZW[o۳5(Çx݁V[n9OYW```TT 4'Ɉ@G h jU}ǖNNN6PH+~?VNwb/$! ?yh(_l}^S]83v}bEEE%'' HC~hDȏzĐ2ɊeY|΋gKGٝKnfߣn%''gee HC~hDȏzĐ)#*I?m;֠;keHC~hDȏzB4pyܘ>|V^Fֽ |9+ P 4'Ɉ@G!D7`_=$ϓ=>xeOnh!?IF4"G=b !f_}/w(A@dD B~#x *޷.~b/!iO QCrS{)R{tdaLwD I2!?cxXZx _Fzf7YgOa@dD B~#@fGߢܔ+,NxW!iO QnΚ|t,'.C}6=2 @dD B~#@'.y WADwsyʧ?&t $1h iC=3 !ۇSܺM}v)=@dD B~#@ƱPI|e.-#O} HC~hDȏzN4pE6*߯V;44xIYN.  4'Ɉ@GDw ZBNHfOĩ^?75y@dD B~#p+7ܻOP2mC4Ґ$#1 ed[flH^f6  4'Ɉ@GAE̥s<*ܔ+-N5(h)|7@dD B~#Тs}& oFhCO}IhA4Ґ$#1 $yb4_7aʧ?& (B4Ґ$#1 0NMF<]f7> 4'Ɉ@GF~7juzD iO QCrRjIN; CKK+C~Hf@dD B~#Ɛп*u.}ۇS] iO Q<.WJr]58@ iO Qy >M ~w;e bPh@dD B~#&p[䫄dH Z[nMI5!iO QD7d|eedP̘|7Hh!?IF4"G=b 0nN9W\& =d瘟SF 4'Ɉ@G ~{ыފ>n>3!iO QDLnZƩ3d~wbYg>@A4Ґ$#1go%{:@j{|QG}t$A4   '+-o"um8w@QC4Ґ$#1 d'rOrp56N6(-O1`1~Ӓl$8@nv@A4Ґ$#1^k/[u?Qk?m+A<م>3e3{Y_xXg!iO QD {lmFݽ.(A36Uˮ~߬s?~;(q@dD B~#р7U'=%VS_Qw=F.xoHC~hDȏz 䦦Wk'+|lcGSB;䦤;@B4Ґ$#1% $</l+{_X[MʹlLz_FV]BIJ* HC~hDȏzĔX4V ]Roq B4XS$#1% <dzKhl@dD B~#"oBhh@D I2!?h` 8l6~~~lm?mHC~hDȏz 0&DWlgk(i@dD B~#р6!\;,,@$A4`M `yZ 4'Ɉ@G mA4Ґ$#1 !HC~hDȏz 0؆h@D I2!?h`i 4'Ɉ@G mA4Ґ$#1 y?w]$ێ':h@D I2!?k@`u.)Pq6nVbmn={vPn>Žӧ2e_=###_^*Uh%K^P@j/Ru3/RɋWsݯk6r h@D I2!?;㿱+9rW.֢rw]75*.߿-Tlnvslub3fa/}{U\ƍQ0Asz%n޴a߿~B<>i 4'Ɉ@GDeW}".?~0qmذ[Vjwi,li-/x 9s ڛo2ZрbIwm)K۶-Tljݺ^.R˕.Ɩ- *V4IH Hh!?IF4"G=bt' qfW2uHe˖e7d'X.G hrrMM֩Ss{.-h=eս>ZQ>r%6`ddv}1k}|45D iO Q݉›MLʇ8+h *ʍ- 룺y!wpƾC{-;VKhPvG ]^ ꡺jEZ.R˕+%Sh׮e3Ǹ43nϝ;^1üZ*,˗[r4MS4Сk֬sDŽwr%]۴ivUBE㣩!HC~hDȏzT4 /xO֭OJgMel\˗7V>,t>|?@ $ dŽ;r{2uҽqd9vbB4kذVZWcgo\ֹ(_zmչD0x@kdD B~#{o$sflf9Iqa-/O߯W7=b.oGrmgz|}kS׆q c{E}8/{] eO*AB4Ґ$#1ڣYVv n5&p Wb3i{{{"=R:+,w,=#QQQOn;}C!l=V{e菏vHy?HC~hDȏzĨrRÏԮz |Xj ސL;99X0D6G]P= `[+Ш~3mdisNMF}}(竹iJO,A@4Ґ$#1@gv4;Zzȹ^jΦсۑ'4q>{sBMN1ȓ$ hJ~Ϲ2c~O 4'Ɉ@G )S5{}:>uoWB4,,Q 9cWwφc{.3ygG\}x@eDx^]s&#glx?% 4'Ɉ@GF~;be8~Mo<E;GYsvEWʣ[. kWAsn.סD I2!?黴{COgvn끲e4 > c/;H= glH |ܕr=ܥĺ_@dD B~S v8[fgo ^jxBuk[M}ŽtMĠGw:t3/dŽ/@dD B~/9).)kbp  dצcZ1u"hWP=1/_%ic/CIB$[G9_]^c(AOo3Ѯ|wdŞ n3+U@dD B~qwCth?!7%B@4yy<Qkv(yY8Ƈt$^Ŋs(u@dD B~M9)!ܻOulyϝsKFPH@cdGrوՇ pEěgo4oVّ1t#T H @ipNǺe؍yJ@~[oĶt4yBWtxtXP Hy~VIOKjX?HFj*`]fĜu%S{×VYO}ԛ =Y@Őa w=5ǜsvzsqX=B9ZȋKqn?>tsƉ<#+Uݿc3$޼/(RP5$#569Q OY/ J ž) A@k x%tj=̰Jϯ޾,Gj 5UA2RPzSKlj̉&(-T@[Re#h7q{:,/,~ (la2sb9{mNr@BjdT!/&wݚژAUI+Ro3nu%y~54菢L:0lI2YP]_ : X׫^>LX10 [g~;:z(ȾkZγtXC. W:\i/z\h ~{w/T((  X׫B ᆗ[Y]^E3c܃Hw]Nra y&w_XuU-e74it &RP$#5$AQq N=K^yj@3}W{^1uݤץ^#:V%-L3گOZՂNAj*dlynQ-W=5$++_w޼yzͰa;Y6vut ts.}:X~^IǪjۏBL2o@'N\z?66H` w*(LI]uf۲#cp8d{Ɲ?u}.=G@}E^aE߸uQ l~3V]<"r$7Lr MNNX?HFj*`]R RCtx芃5-DNNNkۮ_F?v;!%mkg:Vmv+v\rnP* 6) XSP~Gl:C7GnIn@H @}P^\Jo6d1e~]7666u04O$<kX5zc牜c|q׏n* 6jWf@~T)Avs x8=ÇdFp ?zNd{}UoTn/-ɡ }wItr_<Œ\x |H @L&sťaPWeɆct%mUhF^LrS.&3# ^V.H @B)cLZ,C%,srP U9Krʼ bdXi0< XKqcݡ2"TS\׮V<:]:NLcTUnShoJ~T^XQoON14'\C:Pȥm9B!S1;po+#a Tʻ^QZ{-^J0TIAR7@!ҟW A8ż{/s ʾFl/,vz?.&,ᇭӹeçtL]?zM'f òA$6RoO*e<?u] 2dB^Ɵöt@1g<ŘOMtR!5㮗\:N|sˏ^=_PDT)/.ŭmb:^YO|ϨNy]AH @~JD[OȤc`"?G9rl綖3`7m$RPVRC\MX:Au 3rc/ܢjc2rD<پw3h^@A" Wp綖Io:'pmt-1_=pݓԜ”􈭧?p:3Z`-$+@zܹ-G>/cP}.vbUW@jBVxS/_5J+!w$so6dтTh'm$W RPvȰ=fE1V9Q 325'?_lgvv @zjĶ^ j%(*v;3tr}uw#wu4͜nYh/-$W RPVr_'ȣt Tɺ&k4o?4.~uOOy,"woh#$+@zOq9f/\5-ĬEQ{:1Ae1M2p&-#U" d%!5h] LZwόt TCPT*<p;S&_f䆆I Tw=!_ד,޸1z͛7t-(h`E8sTm+,_sw;sy|O!ArBj*д^AR~{tR>_X*.x6'ܾ;@yHntF_ΣP4m\ 4ꮗN~P*1ˉ"5P5akOkD芃joȒp#X&@ѨA* 5h]/ٛtChmm-z*?5 LAyj\%mw/Ӆb:S/?p;#c0$RP&%e\X弌cb'LYI&%''5kԯ_qƗ.]*-O ,ZhժU{d9rd ڵkpʱcL:U__?&ˆ<))TرW^LK׫W˗LImYBscϻE(9q[*7zi Y jWu>ږ۷k׮\.̴Ƀ~ 2ٳEPPлwJSu533sppo D*I޽{>ѣG˖-kٲ%ZҸVZf"O>M6ݿg~> IDAT22dС̎,X 663++K椶1g=Xqh&AQU [Ɠ=WwQ@uA!5]/yK O6_N<<=qy\*~p 6$>|Hړ㰰0hYի@ 64nܸ#Zׯ_PPImYxY[yASnSBn{Nu:C@jvH @5xKwɕHckkK&!!!`ƍR8x `СCS1]\?~uֆdULj<`llLڏ3&55T$[ָǿ^] RF'9-vr^<>BjTd@j*^}n ; dF}EٳgSw8p̙RS̟O:%jƐ k&ml*|c7<}4yw^Qjׯ__~}͕JYYOI0bvz9'.V]%enUA 5]/挓lEEE:t011 0`@N1cFΝ33v}Rd e_y(+5@իe˖/^|S~~>!~.KN:999NJHHHKK322Zp!椶Yw.zu sΙ̥vyLY_ x%tU @wȝg]:LȉJB&ݻw+׻w'O0m۶[.={.[ :tQFXSSSlZ>3GGG1իW_5VZ}L F~'v"kҥKVVV:ׯ?bĈfqjYSm(=Z,ʹa>8:zCP}-?HV3%{,|ANU „G~ ]+[ZZZFF}]IIrrrژLcqRin"w/#q?x ?:H H @j 9۽L1PoZ)xFi~}254<>: 6H)H @ Y[}b6At;ʥl<6`YS,>L5S !5' N=b)[Eo`G].щss "w--PIj$,U%|)6> uY_P"3,omJtur54ޥ UkRPJzWk5`̰(Y4:Fbp[zynUA&@j*P]O)}UbR[54ώ: |_~ӵ`ZV}T7HH @ A? Qҵ_X|xr/`_^W@$k BW>QEf9/xw$Dx\aB9g!_@@ɚ@w̡Pc ] cl]˷$Q Y 5T]/w{Of uyG蘢LN9Q t@gdk) =ɝ@j$k^2?>@MKre<L\Go5->$k jk{×Fktةeڃ0:{1d^} q 뮗54R3D%8|}IN>U]=̥kuP gùx1+Fa]U]dTP-w4綖t4CCO2Э$]ż#vܖfnɋIZɚ @^Ina?/,4*Dе#&weRmӔ$k8%zBbyt4FU7oZ'\X :(-7xI| td͇T]/dQ˅|>"/P9gJKɥqoRťXo8pUP̣l Y!5(s{}ѵ/;(LI7e(-JXfEWvdqZƜsFt2dTP^8|@ '$E{L7vAjt^ ~973}q41#] rŜu!%߱rFBje漻¾b{!Io))nwLvj~e/n(|dR9:' A5ż{/s BW"*0H dۣhII_H{22e<] է(}\C`&Ej$1gŁ}xՇZPM]ZnO^~14R  sN1旷wmu95p ZPkA5ɐ)Ю¤ JKK_TcjfK|T_@ *Lj=+44499~QYJh`"(@Gt-XQZ&4 R RU^K,Ʃ%] slT/+ɡk@a/_ ]qW֞&+fH H hWҵkSlZ9BiGnQ 7D 5R 5]ES^C}^P̣ ][yzF~k׮e+TLּZ:%_SP@ S}y|zr߶mF T~HVJm))Y0ah%IUJ\Dj3]N 8|2$'Ϲt8] )/&5.&&ȑu[_?YcM֫ї_vs ءQ:Z-nٲ@T,rd%}rs~yȐ/CB2dYESZPFt-h@.V>sX;v֭[WZehhػwoOOO>11q„ 4iRrr2\fM7nlll|ҥ +*-ݱcGI^zyxx0&&&'СCC-Rp7Jwfԩ111)ٺ5Kd6mb6A3OF٠Avq8R~IWr+m||6lصkɓ'KWZV '͠AD/9. \.yJeoQ+r+z\\\\^=##/iOC!5RT650w8r",7lXefϹw7obݺuǞ:+YVZT,8shGCGs62+>xp59׮]-5{̙JBqc}2^jdsu9;&ۥU+RrDϟt{}S$qqFL`ȁ$Ի!_޽OO/8eYESUP)B!##߷o_2#F2#'$gϞAAA޽VoGFFСC?|˖-c2nݺ5y JӞf"3ɂ,Tr͒{%F8U2.**"IK6KHd&ѣe˖lْ9q[)qPd MI?NfŲՒ9!"L6YIFF^W^&M?-J=v9RWR*700X`AllgVVšʐ)L y s{DneQ_cxIØH:aBY7oBmWT2o@ WΦ-[6#<R+ 2wu=J'%]ݹ]rvLjZxy&?sfhO>g3+os3 tj:R6tS <+^ݻw7lؐ< =ɓ'6'N+@po۰aCƍRSn⋋@-u:8AµkիWwޝEEL=g \-!k&UgX1]|L7'SbR>QFLh$ddС5|?ﱗZ)^4 w1YL H]/\„ԟ8a#HX <5k_ YP##VV‰OlذÇ5kȑ#YE2nEA1Ǐ3W*vBDdȨSΕ+WƍG^3gZZZ|>Tc!H]ICccc~̘1e?!kq2H T sqfQW_uk׮y mLԩ'OnbʥK;EQ2&eȯYx9;&k x`S~͛7Yr*@ȝg N}}}IuEٲOɬߥ[߿$ݻW<5qFSnO ,klLkժsF1ohgj})Q{H=p?zyub'DDN?ɓ'0~;vLK[z=+uJ;@9ׯ/8(}RUQr=ےyWn#߄Oz޽ONv'G\3S$|t:ujߓ_Yx9;&k'~/|YkrmAї(,H 2pC^\ʃ koONqc'uBXTTԡCȰtԉ3:wI,!%SS`:urrrH!C DVrJ@Sݐ_&MΝԔ͍lI||<bڊԃ"ڴiC?LΫK;!"rz~'j׮M-:JԕH=iNJHH gddpBYSۂJa}_@UTcj &M9.8 |7ogBdMJoW cl|9;_[pt-ɼJ9C_֎Z]KI[y ǵ &!KNY3 5u&o\\:N>?-={.[y|СF1{zz߿m۶u}ԜAoÆ #?]vt҅;::hѢVZÇ'Tv7SO,klXhYDg}FJ*}իdMwq͚5zղuBjSRj*_M^(+{+zҲڷoOԯ_Ĉ)))fX 5P ݷoxMJ r&?n*Q(+뾕&?ݨQg{qu!}TK>wڞi٫Wg& R0[1YET5ϘaAŋ,-:GS:5?pkhw6Mm9*+5P15u5}ȋ;Yڱ/@j@eEr[Xߍ#)^I3קt ԋ}R+ewcj4P xyȖc`-yL/@j@sOB:PUhDIT}Rž˷PMn @ p&+{ŧT}R!sqrXߍ#|}`x/@j潹m9*y,P15uZxjNz:PXߍ#"9Q ~¼d:J`}_@Mʎr!&h@b٫t }R5&?>ũeܕ;tP\Gr^y=b)o1<H Ԍ,׮Ve_& Jh)!_ 3Nt *}R5$@uc}7@e=vrit uJ| q'Pwcj>Pe P/_[-  P\GcI2|^C jP\Gb^?[Y$q}yyYt@eXߍ#`tpNfmŘ@q Pק]:L(xob}7}!9Lqc !"U"P1wcjgSː%{t *b}_ʥ<0{15u, VI1 15uvɆc^ r PIFua}7@{;}fdG1ЁU!]Kݤjn @ p910{P c}R*JˤJKyyn}gFl?Mԋ:r^Cžc:}Rz(U#(*;tQҽT=P\GkW8f1gؿ%| X 5^vA1OT# L\ މ:ݔڭVcHWwɕ&?z. {2d@ b}7@g x%OsleKn4LUrr]16'ܿSWP@a}7@ǥ=sn?>hpu3jX+c}_@>jW_/ۺv5:(qKlj25pn?>ΣbCb}_@]ru3&G.rǩe~|JiձIwR>6#'%|r]8~1Ʊ/+nF Fnff#7&nS?6 ;Y,ڱP\G")龣W=\x|3Ѣ?Fl*b>MpAL}ǬOxK!@o:z#hn/O. s[>eb "ѧ bIIQ踌9o4̌y>n7&,Ӌ *5fSBa}7J5 i^wM:P-^fλ{!+ɷ?v)9HFS?wcjXfo>3zKIM_ulƾb}_2([RC :Lβe{_8=[*/C b}7500S`X 5@f*>F-z&P15u20P-@(LWPPPTTTZZ}ʱP\G)S0q BL6p8s MNNO=@9wcjX3e `Uڃ}RZTQ]W^urrO=@9wcjX3e:`_Țu}Z–}RZTQD=Ӆ _EzrfuȆJJֱ^fX 5HE=h;wcjX3e:`,N $.@gmn @ pk``LQũoҭy]0666-->~P-@(@۱P\G)S9cwj<;`W?zЊ@gmn @ pk``LQӖ@gmn @ pk``LQLR%ږ`}_+{ÇݻB^mE=h;廱W^y߿kC״ʔ@_>EvR6Tʁڊ:`:(Ѫ@"=з]Az._%Fʰa K^mE=h;廱=z,]U\.]0%Wɓ'B!AT)U|r.OEvRV9.j؄\=7qո ےus&9lٻ{J%W+%;P#}:!5HDmڴ<}zƍI~]˹s6nQ^h*tcDO~k׮eSkj#ɚiYkp)O_>9W";)"W"79D(c\={;w8W:UT1+|pšBW-2cX6IBW HlS"ZYEdVdj/P'@x۶E R$Z&Lڽ{GfBi9U/RjdQElUnl֢8,"m$IN'kbi졬C|M%Tk*\G s}C6TF2<fz圝35RS ZF;R+dˉʪZ/Y<eed'ߺCOlX2p[|{a,Omj̥ofE/< G| `4kVz9#'PnE/H j)R/$}{ Nn&M/_>S_XO>Cf[, >?q˖cLTSyVsA~qMܖ*U@1؎;_~^zyxx0!?y|!SSS`͚5Yƍ/]TZ>]hѪU {,8a L4)99;vԩScbbSuk+67ml gō9Aڵp8"[)-_֭[%*>>~ 6ڵɓNd-+너fРAB\.F(9ԕH=i...W˗/,>uk/"&u'hcɳ'9qd-[,YYfݺu >|p֭۷oQ&EV"y%w,))ԓJf#Cٗ_v[NFd"(* \ƺukC2L"܎K2N>ȑusG;RFs6$f`p!Yi& I݊]UEE9Qoc{&f]pd)݆z ;>|˔3o<d8oYN^4bD m}ٻ?4ۻ{ c1|gx̘N۶ &V"Q4n\sϏI ٤I֭?9sƂ]<~g5 e/gVxpj׮}F~޼}TwѢ1`@O]^= Y:zt֭{̙H̓Ҫqq94ԀuΟ?q)L)hܖ*{&qL7oGFFСCNƾc2&^L{iaaݻQlݺu2[ |>o߾dB& dFAO 0kժ5k,2.((Mc$,WmD]%1f@ÓM-5J=zhٲe-[dGVJe"!wwwrYᾬrN6ӦMcVիW)|Ο?_U+)vΓ͂ bccL&++KC#|94YWԣj#Z6GSԩSo߾ݱcǦM~dAڷkNԆل3J$3c%%%}!߿ٳgds%Jm͙cv5yљ?on:cO8 J`a-[6[~ν{G;RFdFذa-;@jX,9˗/klv(}W_u#7O?ڵߙ7oiڰa]^rJϝ;Gn>$]vv6 5{f>]p7wsyyH@n֤(预'~zL Z)۷/&!#ٺ"9,(xqV&VYےYPӍ?~ ;tPԀh~AfkK JBz2A*LܠSO5K+9[6lË5kvȑw,_eR"ǏNJ;!"ڐ]reܸq9s%il`` -R^CR'sI{2eJMM8tԡɹrEGMF^V" ,ӧzRigj k%Rϳ1gŅy*"Cٜ9e1cR" Jb&9ɡdXNL_; Y{ $"gd`6]/]oas;_^ocnZ07hÿrV,w \bIv}&ݺ5gx&<UK/&fQ5kڰa L}@@[Hlm6GDV-ϋƷiii⚃R쁘zr!n/tT'71ҥ8?~h d)vv}`05d 65׼y+g?QO,5 -Y+Ső*z)r޽⩁72O. sqQ/YŋgϒT{e한)JZv٨Q#}N:%jON;880GՓ;1+U섈i3xǓU=y&ϱcǒ R{eAJd?{Ke,J:͗OCsʚj#^/ 9qȚS)P Dy1߃a:(Pp`ƤH!rKX镧EUvZIHè-/#$"YAN^t]y bͯgh;Ʈ`F&͝Nt49vZKkyɓT}qacٽDTj@7 K6q{MO[ϠI ڜb[s1+ =5(1 d}Tѣir;=j^D5xn:@Ƣ=Zk`R&* >ݢN_g(+y,xd=X~?&lԧķ%k%r @pB.K6&&&ua;dȐA\4f̘ѹsLQlQQQ"## ЩS'7lşY^zaaa&MRsej=gB Y[zPD6mH3??ϓy܉Yb'DDN]qZZZڵSri3 Jݢc!H]ԓFBd@vh…:͗OCsʚj#^/ 9qHՋzfY\ZqիW˖-/^[2ϧV.䨎3Qj[>D\\kL y0{?\?21bJG;0Z_͛7!d5cRqhhWWWKW8rys]\|u.㯵a[wݧCLt:OϏT/ݻEM]]Vy=jb`(>~ɥKc> >o>{NIY-i֥K))?ϜكLoolV;~F*J ٜb[sΑ'66O\sP= ^4hP[7Z[%w`MGnVu:;ܣNqZVV֭QM[ Ya!Ǘ$}?${y.{_r}քT)~aS~yHI܆pyZ00uM4'qB 7k[X\ѱc[2͐ǷZ[_}Dm#O__ ]"}[..)f&D? \6qDrn͛7۷/i277ر#914iҶmĩrZӭ[7 V… cH|ATB"--щ_퓵.Y*f&sl 77W|SW^#HBB8#---##YVUbtEEX7:T_}]ډ7-++KX,#y:>Yc*W'y+s KOONTČc"(MSRNYđl]U{yg;NW_Ś⟯+eRGn$:y~8G FZɒRSLyjGB~)-X+俤gۘB|"ku Ru ]ՙZ},ȿ/~lUi{ 0%!-m{xaWuau\Ejii7Ȅ[zYډ"kf`kȉ)99F|g;UOğ,T] Sn"LNjΎYXXGOOOWW::: Y9-yAjn!5f jjEP1%8PΉPd䤧'%%H AAA~~~^^^nnnoMJK \ZRBO%LX?(3埀UuYXXpSdɾw~Sv13JmeL>(N/<IJJ"k rd^}'=H @*Ks `K|m^˱ƭU ^]ݫUB,P)X b +Ȟ%{MRwX? 5PaiyI_PRJ7;=A8q Ʀ4C~n$x 8H pT~l˄6cQ 8#`\ L;hF/,Э@qGnɋNtq tp`P~.@j3v|~QO(cQ 8#`\'r8r]^ $~P~.@jy杧?=| wx+x 8H 4p9nZiAtH@qGyƷYt@qGvuԌaF7\A8q `Aq?,~M۰~P~.@jURYpYn@qGH44=ʨd%܀ݘPM u{A ?X? 5B ?]gexPMߺu+ULPsڵz BAnBpPŏj8H Ҝ|?98-nఢ6#UJtlՉT5ApPŏj8H /m jڋTGj*RU'RU|WGk|w}zq Pclذ&M 83fLƍts9/$+YjbbܹsT?~RRS mm:~z]\\\!={Z"TؼI&5k/X`[#UfϚ5ѣvԩӠAVY, o;X? 5O12(P (m?޶mG},pI##ѣG?Ԕ@qÆ |>Dr^.jW2"U!ČD_MVQVV6xm۞={6<<ܜ,)L>˗R7d-dNNN$}k x MMMccC9B6S= 7`ԏgQk _mUCooo</VZUTx;w&#  $ɮ*dD>>>k׮1W^%O޽P=K~]摨qDf*doīEJi @ 0sRj,u6ٻ E_~+qpG>k׮:u"M ~{&Ϝ93-MrIR20$F%O z+++^LjϒcB@fDԀxϟ?Ϥdm/} o;X? 5N 톬x锰,ԇ7nܐluuuǼOeHErIݻߺu>Ԕofff$t=ztÆ sss IȗޥK7z䊤v%I}Y,,,t5|?T 8>"AljjEAA/_ޫW ٛ駟xzz޹sDoTdm+Ji @ 0sR!#ج86 ضm7DkoӦ9 &NHjZn}͛7;;Gd&ML<9%%EXjWoncL~* 4ɓ'L}TTԷ~wܙīz֭%=ٳ={2^r;/\@<͒Ji @ 0sRj ߬fQdɚ/_h<%LeVV/$+9222ڊW^IH홄 MU7D JU/keIRo8(AujX? 5^:?6먙!PX?pT(X? 5Ҙ@{8G50sRK(/@ X?pT(X? 5\A8q PEvLs ~P~.@j@ǥwܝn@qGRgzt(6qPpʣX?pT(X? 5 9MG \xG T @~~ܦPOX? 5!CLMM8)^MFR [YK/X?(~.Ԁm\\ݠޢE ]]]O˗/[TJMM ?o>俆 qh'izKfi @ 0sAHz;s ݠͻ{ԧ5<)))##n+55U%''mE ;h``I/U!ͤ{˒nP[X? 5ԩSI R$(.1h4ʺ󏧓t3b4GRyyy7o֭[֭gϞ1Ǐ4iR[l9o޼h>..n۶mk޼y>}Cխ@aa޽{5kFV}ђAcQQÇKԵkիWWX˩SZhAرcsrr*dovffEڶm۽{={fj4+KM#|^4;n`4GR&L u̙A=ի!Cٳg3ˏ?<5j555o޼)nON~M4!.]41#&OLlFcEVrR7yܼyR4''AFWWY/D `Z >>&zr2c tϞ=SY?~|pR߾}=zTKKƤM6$ڵ+ m۶/Z^ **J jdu+<{>uT>}1cBqSͷlᅮYӧ?ٳr6gϞ*o|8a P)Lj޾hj ѫrC֔ȋUzlX?zjL|+)))s$/--6mZ 6lcǎ#G~ɿI4>cƌ oݻ,I=dn1@^;oU^7Yˋ/&OL#57ܼu*YYf+d S1%8ѹ/_dG|آ")EVoUXX#y[11QQQ7#T 1YP7QMX?qj n/~Eߤ_|>?==i @ 0sR7 R~=٠_OzP1%8H pR,H (^$]Qv~Iu~P#`\ 5͂Ԁ/~P#`\]jşOYNƨΆm߾lzɂԀ~=^]X? 5!L 5EfV{ 42q#V"1aM4޽{ezɢ^ӧ'ϫZ`Q[.OcJq eHL֥5N\ck{.,N.ۿU嗺J zhN^;Zl!;#?ٽ{k%)Uy"٭"guK-X? 5!O  kťjN9R+_歩9}Vmܹ[3j55P4:}8y{[fsAf~>{a'Nuݘj,.t[]V_Ց7PnrfX?(~.@jC꫾ ?Ka@q=vԸq#U{N6n\ж~jǎee[N:Zvȑ ϟ?Ef%E~'EVRr48=y6lХKk-[6'=Ӆ7gݺmڴԩyyF %yQ/Xzo~km2n=Ƹh^)3} 9(U#<)ћ&OѴFnOvOO`{wRS3g~cȃ]5iҰUƽzsg&'YV &]L=yΜm4%e޼>[32sNZ n[W'K5X? 5!JN YϫX2544Z5ƍÃ_ϽHFk&qA S~[Yh Imfv_߉dաԀ}!1}teXxi./\S^.6N$ ;UԀM}6?w{{ GWWڦ&4acØY^+_Rx@lsמ)sHSbc7~j@XQў ?YjG==Ԁٳk$5p MOOMѫ?B(kڄeFҵ%B@p]g| ZR'R,Jֻev#~xo׽{窝#pKH?׮d1a}ݾ?I?5xpr:,r:/\W%S6oRo!5P^999IIIC,]{r͇/1lAц-Gl?~yO/~{ 0vm*U_RĉԢEիԀT\,(h-Yŕ+S\\oS͛SrRV'U^Ppm???æ6S^B8p?evФkza>'VUj 5ձM*bcȃի_yjh4jܸѶmdu 㠠 ///' <Kw~Z[a8@˰DFÍڎ?q,BAQўA:Ֆ&&FÇGw<>~άzWH˖}mӥtK۷ꔔ+V'}BIHdEÆuٳ :Et۷ݻgllaSwLN*yB8nDd=eٟ:A.ҵjRܸqX OOᇭ.]٪HFV"}kBB <=0n0꒦xEۼYbt%ر- eu"YduHmc9lSkRzSW99?hb`0Xӯ_{`8c`Z^[Ml޼[W'חFV-120I.pP/9jǪW@"/oecLZȑ֮-~Jbc"kV޽oԑITW^˖ϞUR&MFatѐQ# /N*IټY[Kki}du_|ѣAa/DHr%;C[{2͛7e~CQV|!}Ӊ eoǭNڑ֭[ܸqiSE/(x~8G FZɒRSLyjGB~)-X+俤gۘB|"ku򋊧>H@x޿Z;ZvAjƬOs kՊ2S)99$fXR\eALKsf>iH_>? jv"Ydu(ٗ)!444(()q@i˗XX,Z_'ES ';е6y1/,c?ye4+׸xV 5"+5 8kԀJO 8qNJ^-Jc=X^ (f7ȐW߬k R,EV@5@j@w6<;}? t-V[0lMC84+_W^MH T {!V 5R9R$ wi;Z,qܥcrq>RH TKBIתY` 5P{'[|2iJrG _8FX.^z?BjZ<hOתY⩁ %rzxSL!+">c%ڿoGm@ZAתYfQ@EȰk@B^T"vuS8(%q; j Z++ R,L 0Hd-d]d$`UN  2ZvJpc^:?}`D7pRoWŽ+ufQ~j{yyYXXdɾw~ST˄t-ThoM+)\"(. uIt7 5v8FjE "SSS-d^}'ԩVqxލP5KR&d'OmEex)V lj slI< SJN UuYXXpSdɾw~Sð5/ӵV[h3vtpFnD3'mn?CV ljE2S999IIId-AAA^^^N@/kߔvvt-WA^E!  !c֤:m۹jnOyҵjRyiL1;q?I؉jEgo:w<aizGz8{9$6NOO'A2YW$7=%K;y7>7O_I.W݀$Q0] %²]y64G: _̍n<ޢİa9pX]OWG< 4uzzӸF-ǑO]Ibs|axt-gHss5eWZ >)[DתH Drzr%J򢒗.Go8$J [d'nJ$.2i; _nJzdeF_t9Y+69-LRE"ܟZT]Oz%$-0כ.6g?tMKH17ZVt X*;$:?/L햪;~޿ZT`%Q=(.߮}1QiN>(d+Z}o*r^,'dDa3`۴ָ 5>~eIת'=JJ=~eVQUA.&Yz Mv?FL:Me$R iA;ϓ;bng A5 5ײNH DiGanN ^Ӆ$ ~fbe%u,9c{?9=(9q|W<~uF-lj~&p?},[)v>١1YRґZ5pSvH(M02MkߺqmpŲT$#'2@ɮy~?,pSarzVp$ہ;y/=F0m\?`˟'^E%}TRҽ f]~ys>S8^AB*W vDDqE8Gnw߆F 9(MOct O7Ԑ[o<Y5<2E'2A~]5)mZ&`_; [C+xdz%g}SW[h9n\ԀB"A!5gL0x:9Ykm$z9)|f{9aSUy)tp |dF0}}\7eH.$d%E_c[v"OZAZ]lc~(?tОt̬_M)ȹ>[Cת?jJnD(M(MMg[L̴"`QA u|zZyQ *4+_}35_o~(P'y&bc05Nc7ܦȼ$ L'17E~Ew{'-Nˤ jRR$xNJת?jCkKwnie_rj4'y/?"(-Ef`5.n&a~e OJxL{ےI 91{7;\DŽ4W~{K%ݦ`^x)Cj|Rf2S=*Bj@DR<1M62â,GbnE'˩b_If.P eA;[|23^׎ni=/LIpg"j[(kbr8׫?L\ZS3Iq`T_f{3Y `xT_tCj@^ 3eI8D#)SB!9qV]:`Vӥyt@)J䄋~NIF[܉ߺq,qa=r]kUwRb^wAj@ _[V!5PWDiˏXt%J,;sLShlM+ɟn䮓:3 8Ud4NJf[Љ ;#q>lS2x&}Nڒ%sR? dqFZ<=tMr3{ڋK>&~TRRw^Jת?TA^tb>+X|eeC1M$ l?u !1uP(͠DeyT4"R zt( $3? '''Ũė+I2ST dI4qR !ߟCpPMH r ZcwP5y1/boY<*Jt`5܈8z9S4f/$**,=,{^v(%n㼒lq$JǦ:I%#aHT,m{|La~]BdkO* =^O@j?t-+ 5cDiUZv22Mʦ eN$+91{790XpIYnߊ/A';%_qz8~4wP6N׋N˄r>'H2,X2&'(s xhqm9] H rr{ے$x0hWsZy/ =ҰcHMn y?Rد?p 1G~ƙG15vA.v2Z=Ij^^XLoAjr2ӵ:"q 9Dis:MZt69|z:R:y]E6d/^=v&m5GPZߗ ɸ?qL~#eW d 10&g8?~/%qG2pR4υZV@j@ǥZ곹f5= bL(8keF3lGbi9{zKzD @@~J!: n k0h4<T>,s\3ǧ 9`륳H IA| w$* bC/,YOl/^vUl$(. Iɢ+Kfie幑 $m|:ev`8s{I4qyv'n:KpL17SlD̤_ʅb^XvӢk.(H|[w`<72߮vn(LZ{O/+|qN{!ہK }1gOKLISABRxC2u6-iqm~8A9vEiK!&  L;h>=tZ_5W^PDCɁv6e֞?¨8Q?;VYռTRo8,]H pYaRz['{/0i;Ѽ/e=9ڿX}>/ޗnu`h/ txw2asw3|Bek@} 5 '6k`&ډ}h/ed/>772nuKKRlzcYxexwSZ@j*LNh$I'JKkA3`,TUiv^s5}h^QF`N]Ҝ|zQPs\O ɧfM+LJa@x={LzR]gN ;v ?;[f4m/1i;u6Ƣ c4Gbj4+KZr~`8Tg*RJ!5E&}U4-aD{_υ?xo 9Oد7\z5_4:Aj#=f߱zYS ZL S KՓw܂dk=쇮 &l 3wxY}6yүˏK1i7٨8))qQ|V멁g^~8gJ)yR43Diן\ϴ4G]1,Ȧ^vC1"[\^TuJ=唚b@zjv(Tg9oCjا,0޿5j9N[}Ove@P\ba(;`8SyJXsS 5Ԡ0mf'.`Ϗs?- Slb?lAQ&m'48[LA~={rRSS 5Ԡ0mijjJN݂跻(/L u5F,4m;Y0[Rl1OqEtZAj@ svoq]]] ///>OՀp ^Mߡ1uj@taES55Tg_[Ԁy!=RYkp~aR"U˝RSSL5ue_bPRSmH yuԬ?̠(cDsr?)55TS?"; 5(5uކpJY^ˤz7n;s\'m 9w $KWjj&ȿQ7餣sܹ۶-&VmٹsY5[SrB& 㕕=ҥС}|=~cR[U.Yj IՙvK$"9|x=xĀzIØd_%RSS %?.%ICA;y.0bERCw*#G~{0汗JR.E[YEdk^(?gs `RA@"A"WP@"͊RV a1"`XX -!@5?霙Hl2ygv޹r/TU#|?+C+i22|]ڵ4#7ߠ=xmժ]U6o@թ?*jF4ze.CWRjKND;BM{O3쫢uaO?=||}׸z^?w3vhzqɃS̓gk.Zݻ7yWN.ouYD:dG#F=y(m{6z=4Ȕ[F?FSXC5S֫W':yjCUp[ݺjiR+}vZ2B3Hcn||W׍2sJa/iR3&,KkmBCCD7O9t~<6=+K?燶D_K:vl-K_~:ANWg7n 1jlY}wWiҦ&ĉ;/t~4iC-~(jN7U?LSKN3gBpp4?7_^ad:nܽ3fiO[?&Y:ѣY#ʈhO++22"**2!adll_OTK_;uڵk>kҤ>+k~_y#GS']Is[&h]?ya_7V:{vLn.^ϛ3$!򯿎 54,O.(l8lxюݻYxxȉO1M6HK! ;_+O@Xܿ]6ѣEO6[t:hԨK;QTɅ7ȸjj}qV_~QC &cSǨzbؽɿTڶ,2;sբE5ep՝8Cإ.2\Dp%*COW"WhhHf+|h`ABIu">22".n,&c @W6۔S}Ԩěg^;wg?xLe9?ܹ /ضUi=zʕU0K;V矧ʎO>9,jT'%' ri7ɕWhSӕ_߉=d͚}l422 嫋),,,((윕oeYeS߹kE-{mlt?w/;5PXraٳ3c興G頭7D[ /XNCo xApǎ1DN翜>P>V'KY rˍ;0_yefhKd/ N픑}^hhZ/LL|pj1n9;^j&}:|]r/H+K6իnh@u664qۥ"S~hJ|E~/CDQM;v/ԉS͚{C1BCC&Myス~-yJm޷/Eqۥ"2Sh_jW|u1~̌ 68u֭]6))* / R]v._KE4z짪Gb ^oAA9_<˵}ZdPi/Ȉ5/p9s&Lg^o5nTDW~zet1hAEUDeJE4zS%(R-2 (MJ UQ6У*.?-2 r7 UQ6У*.*&p@UԸhJ*)#J\*P5n#=bHe"8*P5n#=b.BN$ʄ9h TEۈ@~⺘Kx\.WFFFrrj;땫k;`)Xh TEۈ@~⺘KRPPv\2[^crrrrr7K]h%eAVȸh~.x^9K^^.###Jzo UQUqOiU.ȉnʶR^jvƛ`4P|UmZ9dD_kVV( ^U*\"{^$؃\\\\MRW0:bR?H4r?U *@<3(Ƹ* 3DK S] *)8'tWFh| ~UZ%EyBgPqU@*g@*APUR4ȜA1Uhh`kDD[# ֈF4@45  lhh`kDD[# ֈF4@45  lhh`kDD[# ֈF4@45  lhh`kDD[# ֈF4@45  lhh`kDD[# ֈF4@45  lhh`kDD[# ؚ/=hZ  |[?Y/jh`fh7F-8<-Aiߦ ̎рv*Pi5y7Ff6N; y;/ &Ucܴ!4ﶊu:m^1N1nT  | I3?lmܨz!7 >h֛G,Ht;?j!:5zfuzFd\ ̬OgN=O=9sSeVƻs ̬dJ>O(xwWK3iaxEJ,&Xh@%%{m[?|$XhR/D=//O^x*hR9ev w ̈,. $|>999##ry<㝪 D3K*aN40#""""֪ӻK̟z䓣7lxѼuR=wϝgE4V֬y."⪠ j m.j6azU_ܩSs&áUhhȍ76WMޱږ;:?m>*=8w&MiSKկOXj7dE3T=zt۷Z^dز%"X+У[&O&9&U/e^Y'mZhޭ0}ziذ;eeNJt@^޺𰧟kF_Dkz4,O&>>㏹FEENGCӗtZZ~ʕzDGן7P_р弯:[иqC;pcENqMvmwly} рT.7:ѣYyҥ̙|zGFFDEE&$+~Ij0aDXXĉ;/t~4k:}zפI#ejJ>]TAȑԉ8xpyC U'>8vyJ2WOM]V;-88x1azef,=%;.X ^oNXXf{JΜqEFF YرUk assigned : assignment assigned -> assigned_expired : after valid lifetime assigned -> assigned : renew query assigned ---> declined : decline query assigned -up-> free : release query declined --> declined_expired : after probation period assigned_expired -up-> assigned : reuse declined_expired -up-> assigned : reuse assigned_expired ---> reclaimed : reclaim declined_expired ---> free : remove reclaimed -up-> assigned : reuse reclaimed -up--> free : remove declined_expired -[hidden]-> reclaimed @enduml kea-2.0.2/doc/sphinx/uml/tkey.uml0000644000175000017500000000027114206773363013555 00000000000000@startuml title TKEY Exchange (GSS-TSIG hook) participant "Kea D2 server" as Kea participant "DNS server" as DNS Kea -> DNS: TKEY request DNS -> Kea: TKEY response (signed) @enduml kea-2.0.2/doc/sphinx/uml/main-loop.uml0000644000175000017500000000231014206773363014470 00000000000000@startuml title DHCP server main loop (Kea 1.8.0) skinparam linetype ortho rectangle "Main Loop" { agent "Wait for next event" as run rectangle "Event Loop" as run_one { together { agent "Signal" as signal agent "Handle Signal" as handleSignal } together { agent "External Socket" as external_socket agent "Handle External Socket" as handleExternalSocket } together { agent "DHCP Query" as query agent "Process Query" as processQuery } agent "Timeout" as timeout } together { agent "I/O Service" as poll agent "Execute ready handler" as ready } agent "Check Shutdown" as shutdown } run --> run_one : get next event run_one --> signal signal -right-> handleSignal : got signal handleSignal --> poll signal -[dashed]-> external_socket external_socket -right-> handleExternalSocket : external socket ready handleExternalSocket --> poll external_socket -[dashed]-> query query -right-> processQuery : DHCP socket ready processQuery --> poll query -[dashed]-> timeout timeout --> poll : timeout expired poll -> ready : handler ready poll ---> shutdown : no ready handler ready -> ready : execute ready handler shutdown -u-> run footer dashed arrow means priority @endumlkea-2.0.2/doc/sphinx/uml/packet4.uml0000644000175000017500000000470114206773363014136 00000000000000@startuml title DHCPv4 packet processing (Kea 1.8.0) agent "Receive query" as receivePacket note left : input agent "Service Enabled" as isServiceEnabled agent "Callout buffer4_receive" as buffer4_receive note right : hook agent "Unpack query" as unpack agent "Classify query" as classify agent "Callout pkt4_receive" as pkt4_receive note right : hook agent "Check DROP class" as drop_class agent "Avoid same client race in multi-threaded mode" as same_client note right : postpone processing or drop rectangle "Process Query on its Message Type" as process { agent "Process Discover" as processDiscover agent "Process Request" as processRequest agent "Process Release" as processRelease agent "Process Decline" as processDecline agent "Process Inform" as processInform } agent "Callout leases4_committed" as leases4_committed note right : hook agent "Park" as park agent "Callout pkt4_send" as pkt4_send agent "Send response" as send agent "Pack response" as pack agent "Callout buffer4_send" as buffer4_send agent "Send response" as send note left : output agent "Drop packet" as drop note left : error receivePacket --> isServiceEnabled isServiceEnabled --> buffer4_receive : service is enabled isServiceEnabled ----> drop : service is disabled buffer4_receive --> unpack : CONTINUE buffer4_receive --> classify : SKIP buffer4_receive ----> drop : DROP unpack --> classify unpack ---> drop : on error classify --> pkt4_receive pkt4_receive --> drop_class : CONTINUE pkt4_receive ---> drop : DROP drop_class --> same_client drop_class ---> drop : query in DROP class same_client ---> process same_client ---> drop : queries from the same client possible race process ---> drop : unknown message type processDiscover --> leases4_committed processDiscover ---> drop : on error processRequest --> leases4_committed processRequest ---> drop : on error processRelease --> leases4_committed processRelease ---> drop : on error processDecline --> leases4_committed processDecline ---> drop : on error processInform --> leases4_committed processInform ---> drop : on error leases4_committed --> pkt4_send : CONTINUE leases4_committed ---> drop : DROP leases4_committed --> park : PARK park -[dashed]-> pkt4_send : unpark pkt4_send --> pack : CONTINUE pkt4_send --> buffer4_send : SKIP pkt4_send ---> drop : DROP pack --> buffer4_send buffer4_send --> send : CONTINUE buffer4_send ---> drop : DROP send -[hidden]-> drop footer dashed arrow means asynchronous processing @enduml kea-2.0.2/doc/sphinx/uml/update.svg0000644000175000017500000000672714206773363014101 00000000000000DNS Update Exchange (GSS-TSIG hook)Kea D2 serverKea D2 serverDNS serverDNS serverDNS update request (signed)DNS update response (signed)kea-2.0.2/doc/sphinx/uml/request4.uml0000644000175000017500000000324514206773363014361 00000000000000@startuml title DHCPREQUEST processing (Kea 1.8.0) agent "Entry point" as entry agent "Select subnet" as selectSubnet note right : hook point agent "Find host reservation" as findReservation agent "Add either KNOWN or UNKNOWN class" as known agent "Classify (2nd pass)" as classify2 agent "Process client name" as processClientName agent "Assign a lease" as assignLease rectangle "A lease was assigned" as ack { agent "Add reserved classes" as setReservedClasses agent "Classify required classes" as requiredClassify agent "Build configured option list" as buildCfgOptionList agent "Append requested options" as appendRequestedOptions agent "Append requested vendor options" as appendRequestedVendorOptions agent "Append basic options" as appendBasicOptions agent "Set fixed fields" as setFixedFields } agent "Adjust interface data" as common agent "Append server ID" as appendServerID note left : on success exit point agent "Return no response" as drop note left : on error exit point entry --> selectSubnet selectSubnet --> findReservation selectSubnet ---> drop : hook set DROP findReservation --> known known --> classify2 classify2 --> processClientName processClientName --> assignLease assignLease --> ack : DHCPACK assignLease --> common : DHCPNAK assignLease ---> drop : on error ack --> setReservedClasses setReservedClasses --> requiredClassify requiredClassify --> buildCfgOptionList buildCfgOptionList --> appendRequestedOptions appendRequestedOptions --> appendRequestedVendorOptions appendRequestedVendorOptions --> appendBasicOptions appendBasicOptions --> setFixedFields setFixedFields --> common common --> appendServerID appendServerID -[hidden]-> drop @endumlkea-2.0.2/doc/sphinx/uml/requestLease4.svg0000644000175000017500000023707114206773363015343 00000000000000requestLease4 algorithm (Kea 1.8.0)get lease for the clientreserved addressyesnorequested addressnoyesrequested address = reserved addressreturn no leaseyesrequested address is reserved for another clientnoreturn no leaseyesactive and owned by another clientnoyeslease for requested addressnoreturn no leaseyesactivenoyeslease for requested addressnoreturn no leasenorequested address in allowed poolyesnorequested address == reserved addressyesrequested addressyesnoreturn no leaseyesrequested address is reserved for another clientnoreturn no leaseyesactive and owned by another clientnoyeslease for requested addressnoreturn no leasenorequested address in allowed poolyespick candidate addresslease for candidatenoyescreate and return new leasereclaim expired leaseupdate lease informationcallout lease4_selectcallout returnSKIPCONTINUEreturn no leaseupdate leasereturn reused leaseyesexpirednonocandidate is used by another threadyesnocandidate is reserved for another clientyesiterate over pools and subnetsmaximum attemptsreturn no leasenoclient lease and lease address in allowed poolyesupdate lease informationreclaim expired leaseyesold lease expirednocallout lease4_renewcallout returnSKIPCONTINUEreturn old client leaseupdate leasereturn renewed client leaseyeshas reserved address or client lease address in allowed poolnoyesno requested address or requested address == client lease addressnoyesclient leasenoget lease for requested addressrequested leaseyesnoexpirednoyesreturn no leasereclaim expired leaseupdate lease informationcallout lease4_selectcallout returnSKIPCONTINUEreturn no leaseupdate leasereturn reused leasecreate and return new leasekea-2.0.2/doc/sphinx/uml/requestLease4.png0000644000175000017500000074624714206773363015342 00000000000000PNG  IHDR* u\)tEXtcopyleftGenerated by http://plantuml.com09iTXtplantumlxWMo0 maE?ҡK0X M?$Bm)8mI(83H>RTD8 ;^ST(%} ft/nπ;` B f~H tJ ~=b}E *0@M Cee>PmbIq@ł㩿lC*>OӣWtP myFuR(A5PX#C_AS |IgI"4bL j. meA0>WtSUr*2J-!,5gk`nO@&ʔ +n-/4}l"([jFduYokmSyb]SMWIS`Qfi74^oZ4=7 Z[ 4=T4FlVDsl2%Әujf|2؝ƾi, +n*)(vlv,e;\~Ee;\e;\pN4N4mH]!uw F{!z^%H !R @Y3E#"[3HK=\/ߩi4S8T MpwI4&Smwµ]_]WUIIDATx^ \((OԴVLe.3J۴^-VV/5LM,r+V"B"ZT\0 Wи53s/Wx~?93s̝3LJcC *'T"ODPH?~@%"JD *'ꊊ9r)? Wv.3O#G/ UlaNNv݂M&믿PǎŞ:2tК5k cǎU~~~7n{ںuC=ԨQ#S={hk>>-[\fvKIC]мys . ^}Ν;שScǎCUr"wVZcǎG}TL / o-[&&EGGu] شi2ӴiӔu>nm=l_\p|7߈&M sQJN<)vǙ3gd / JpD2pM7{zzvȑ#yyyZwqx!n>,gS[n- {ϷmTJx 'O6];7o(9r7͢{Gw-WKĤKYllKl~Z瞗_~nS7o^TT$ZkIPQq-[J?n&$V$QյkסCvTJǏoӦ(ӈ,2,Pk۲شFk߾\޽{K֭u]'ފU|'~&MkF7裏:J~Zti5:t萟bm?''VGm߾]yN:NM2kʇMtx{"@%W^Qmkownj#ފѣG?|[XX(od "~ll;I5-XaرbOvK4駗^zI}衇/#] O zK]VfիѣG/>@SA(>DԔ%DS-W_-ڵKdN:.w_z6lh Slms:t`ɮ.?) []"@%ɂ=zԤIQsR6XӧڢE M~ӧr>=a~.}_Rۋ]vm͚5{-?44-$dl6+5tqn%77WԫWb\O?uĉ$""TRW{רQLOoկ_FJ^_{Y[믿흢aO?vm-[JWyyyjP{「0**JM_USE J8_رz5jjuOedbE_DD^]صkWuo*"p,OT"}7&\&]L>bu{~|ٳcƌiذ)n׮]+ "6Oekb?駟x VZb{;w4Y'JY햨wS^^\Ǖջѣ֪7UW]% F]_h~7]P=7O&;BY=l'g|bu?Jӧe͚5B<˷֭o{쩮H,׏ۧ.Ο?&)X~k_#F0~|g|Ǐ?cdL(Fuyxx4h@S~LF) }Kl,ɓq{wDeI@eDׯ/ޮXB ._tRoN8QFڵk˯Dw3VF)00PTذaRw|X:fcöٙ~0aG]k˗ IjbFDl("5YW_l~ T'*>Y :T>裚Ho}G򭸟FQr ,oqV~ɤJ?^ӧiii&ώ6m$^{J el+++T !)**LXҥKM6TfK4iРAҿ3i$Sx@V8px;qDV1cC~~H?- RJ'a۶mwU)emJ)vv) oР鯟g%5kԩSacڅ>@7n|{T? 8'*>YpwLM6wyܹsŝ|A95$$TL{l^^^(J)//VZ?7Og֬Yf[n^{59Iܴ˟eaaa]&&ڷož2%gw}{'#<#I6Zb{e.nڷo_ bYļo)~~H?/5j.D~Bwdb edd-%+>T,Y"rYY# ߳gOQ/֭x<^?˅8|bbb&N(6ySŌ7x8bG J$sI#FД_>8YhԨ@'Ovwwk~Raڴiq}MύV*XQd>Tzg۷S>|s&Y溾pss{w!o\(%6-*?^;AvKoV^^|UVg=sJ;w|Ͳm 7ܰee~Yr(y[}cNJ/e~К'|RILLo^V'H9sǒeb -\P.GSf,Qt[rRR5DR\\ /TY?2U_@ 7;wT?BHQTT$&|/((N({n uٳĉ"MmbvKlx.5y?ww1??۫35vSX,b- )))NhS 6ҥ˹sH?PPy>>>ӦM o|||zzzNNv@@ صkׄ |}} 'ni޼ygw!!!f911Q̞](P-~SSS}kUV~=z?^&f=)_}3 O8@^^ޮ]RRRbbb¾빯6uRrO2$bէBBjPXXdCGO1BGOV *%^ JIH '@@Ǜl 35D%444<w^;~T ❣?7V?;v*ġk{n}{( 'Y7 e6dΟE[q0.xyKߙSPPPXXݑO9jg'QuԯkV_piqyyyd`O';{OGDU9V/Q5볲@Ne$v+%gƛn_~d`Og]l R ld1\kAd`OgJ:e]sD3֛kv]2삂B Lq-K}}%5͉999| ֐~8͹%n&ʋ'O1UPtttJJJVV_5NS;l ؿd>ATRi"""233;(E 4G7dM]n80ƍeKܱqw {{&Oj8Z8!]蘩aaa111999 "p#2̦k㪫|}ccqZO QUӤI#,Pb}2jrhh]vvvH?Ʊ.]:̝;Z_xѻ}}=nZ,'==ȑ5 6ZOCBGO ZfMVVvH?ƞӶmҩ~zNJLm][ǽv;zt(yAjzoرu%(\"znhߨQ=& >ҥ)V*K UbܸbXqh/|Oֹg~[>xא!s[kkײ>}LEb˖4A̻n2~w[7OEGm!W'Wg'A #p2O٫L&w߅ʷ^nX- ccgҿ_ʩm۶Qd=ֹ?&LxY.,cw:,ڞWċo\TNM \fྖf-\T>qY3ٳGիK>Y]I?Y[~u=O'Ӕ~ d2Net~j-i"/wqcNm==+fׯ7o^oͫ%`8u X ]I={dkkܿ?A]I?Y[~u=O'Ӕ~ᇥ&)3‰7jB3umb֬w ùV]F oFI?YJWkO&Ϋ %7KT(Y~M RVIi>TW#*DG.Ԥ-D:T'؏iL?h;N7_^[8yrsM}OTz'V̰%qʊ[ƍ`Q'6]ͭ jBI?}|px埊%@-\*sBfmݺ|ic-H?~NstCp%7нAAZh2ghQw۷Q{nn5i ׯWPQ&M ~.QK&S?[ؙ'4k[v-qʅՄ~0ϯ\yP\]\ƍ_j'6D񭷞VgcSի֓8Q~3D 4ͦKJg۷/1L~; ȡ'6+=vBԡKyxnn:2b9&nO2\99Ie̅(=a_}2m6'i0DP[:yWϣw})ԩ],"2~8Sl{Ӟ~M;cA87 m,͝) s#fsxxwYYYڝ "rrr"6ݞhUE(Ӣ{"s'sOL ;(E P%dggY]E$䏓+1!IU?D8e;v`ɗn~< uI]zzq| 6~T yyyYYY |==%O&M|ȩSDD|Sppx/veb'@ PU3P f9g:zJ7-b?ώ. -2&$$";~T!J*===111::Z~&$$D>$*.t) td+.;BSĮ;D P TvvvfffJJJBBBLLLTT5А+AػSͦwh'\9DW.:_#;E=NUNaaaAAANNNVVVfffjj5kcbb%?a3D}v•Ctp.;BSĮ;D PE$T^^JOOOMMMIIYs%f") EnWNbwx"SQYW8)`{xv•Ct5)'T'*őufS !ÜS^O  F7iO⅘wUe~a~/-SNSʍS3.(Gg(ܕjuK{z*H?HG7dk,w)`G#kvڪk 6k6,{7ZVu|T[ pp~Y ceۇ2k+.V{A{w%.:WF K=-CH?xG7dFpq1[=Oj%o#2Blm)`#.np~TGPMhb. OjB2:\']> :#޵X}h2Pp O+';CfWXXJ~8G^͛'_O8qԨQEDDDVڷo`9i̘1[񉊊RU#tI!3P ~0eXڶm;wN:|rYݡCz~Y2~8ĉz񢰰wÆ ⅗Wjj?q~d2رE{*W@Y,;vܶmѣoVQXTTp^zo߾owY֩SgH ,[CK,Oa-%Oiii+Zh!_x{{;wUֱcT('oذagoϟrʄR&M7nz*{ڷ/~arkaX߰Qa駝;wbGSddd&Md޽f͚]ibcck׮]n|6++K]hb9SIƍ Tf'5j/  aӞcǎѣ#{~86m:d$<<722rΝ>>>[OJJu~r# Mٳ&I>\|ժU-[}>gF5o<%%E_=H?K>iHaXrss哆G*=gmb„[[#QxVڷo=XYg̘[n9MhhҧOWWg>YԿvÆ g uD̙Q__X-җ;PJo?R 9?wLsMGeq1I_n-6=R({&kL&Sn7cߝ=ysO?pŋ SRcE_Gu)^{{7ȈRXAϐI%fl?7,,Kl@bFX\VڃIi'b?.U~*D=A-V߯ϞhB&kmocbfԩ>dH x.]:pKWjf!ͭ7(hm]k8KRҗ Č+;vlݪUsF,|La}rlv_ATjd6TK YI c>{ٱ#R7og+fZ5G _7naBDuúuaq-de}I,~z&4A c KӒS>^[7oaBNJmXKiST{4ڴi޿vZw )s%!sO^ OOO'T*WC S4Jz[^ϧX⌜?ܦ9:e~'7nה['^9VPǁ7,Ԅ~-җC"l6I?!TG)233c~R3P:ԹR<?\ 'B>Np2P }IXѽyyyjH?p<2PrO1~O@0| TP0~O@0| T9PE0~O%;ST*2PbpeKe>bUT2}*m5>\pɊ~/-えS3L a*?9w.?Ÿz#GMO-O1^T?},ʽ{UT>u*#~ԐQEBĘ2$DDD{.ޏ7)Q7[[P$Qcwウ;ޜi52U\ TBBBTTTxxxhhhTDn#pq0Dgz?d^ޝ'zXZ{tJ~R#`e:?2R2Pk֬-sD{be5F&K=Ƶ觭@C (-C&!ҁ5w;ˬ]v-sD{B[SU鋵vH?t5ʱr^}xh(P%dgggAEt9=N!F POK=`p[}rx˳bNž>bp1jD6bO@~•A2<\'972.A.{b`" p#F[, c1d.K?\j#p8O=U$^2\=hTVVV^^^aa=U$dC bmvܹ:ujժeytttի_<=U}Z %ǎۣG,U*T\!B_y5ԗ[ }*66vuCy\]hqϟ?u~1I ̀zOp0USuB3P\'T.9:Ϟ\jU˖-e9s<<<5jԼy~=ܼ9X_K7,txh2Pbߴi!C( <<722R,}||ZnT2?{T!&uK@ N#ƗV{*.h۶ń /ldGd)ZozNxd1kۍ1%ڴi$^̸n~1coݺWd4vbhմiAeX_Yװaþz]}u3Q>slڳX u_~nPX,\ѣTK0ٓ{B(ex\ T5ޟw9g#2\'6==cG1>}g- __XAϐI%+C1Q.:p`xuy23ͧNFQ(]LZvX7`cTǾͰaaov T|'?}mn8#/rVlG_ՙk^CS}enҗ[M\l,v\[ !4!3P^ˊ]"OSU$Nʿ~9ڶ-BQ!Eҥӓn |p쨟W :t߿UDTeu֑ߜ7\:[j՜#9fX߰7U_RfĶY Rjee,v\[ !~. }0|PqY ׉͏2rPcG|;oXOϺ+VC4',X0eK?9pki#jѪv:enyXy!88l6'&&fffX{CGR8]|SV;Wdꐹ'?8NSUgy(9ڹ3J) ۻ_c__oyb٠?|IwM6~Q?x/dɓE 6P_ںdAa6Řڵ;K={),,,(((<<<>>>==|d#5 Զ׍;+!_!sO9~pqT8 駪 .Wx#?ܦvlNBl7,Ԅ~K4f ]\GZ=d`#!a('?8NSUg̸#.)e)xsOb\ʥqػSCFM:z a5LF!$$$""eǎY+HrZUq UTƬI@~B,I'h{g-/v9\kG7dj2!!!***<<<444e$J7;rd#zʍ/:-<ï#kuٕ*r#T,챴Nw|dNKRFk֬BYD/=&͕ǎY+Ha!ɽvR;yL[ Fj2db3xa.;eVrUrk.1JIIY^}%zL+9WCʁ]\K'UAj9{|)~w>yaPNNe,D_cG,p.>w^4cH}ÈYU!2.]URU!'N'8^i2P.!d).9sqHlwv3,q%ơk{dQn}uGwGַ \0S-K9 BH'[Z`B5foR\i@I:6w,ڪ?T⊋qэzFyޙSPPPXXWwmVWeƑN~G:QCf̉jQsag'# 8뚕3-".;;xH Zl}dtw+Ҷm۹svԩUV˗/:tW^``w:tHӑ{"\$dj 3ЫWy'N5jx!N۷_`4f̘֭[DEE)eWp= ?6J9bZ镶f)ܸ#%aTܔL;n۶mѷz(,**C K}xstHӑ{"\*ld&Nx׋wÆ ⅗Wjj?q~I`wq邂$H?Y,I>]?~sGT8ov}_>ܑ=ax_*oJ+VhB...wYVcǎ]|9G:AW#D`X@onnn;w%uRA˖-+**С%K.:*;jg}?$Ħc5 d;}Ǐ/ ]' 7رuӦ3o2;]iY*).nǎH}rE}ٳg7OCcCs]f鎍r|ϑGJ>>[OJJRU3~:B|?L0UPtt~;Rqii۶ń /ldGd)ZozNxd1kۍ1%ڴi$^̸n~1coݺWd4vJִiAeX_Yװaz]}u3Q>slڳȐK۽{yu.u:U6QbJSkjܸw..[IW׫E'Jh:_֜3gtNmE?Tt9ӷ5spu3֭[6lX_L:|[HN)K4UQh xˋ##]*z=UXY~!!l;6?y+-6[/Gk?OKi@uk./-߱c-!ŅXlj9ekWN5i ѣG%77W>;OT+#T;[|/)9& zǞsħ=xڬLqr֕yGjҭ ?}wgϦg^\4 tPrabVaaKxw߾xQ-3|Tlu$qM7\:fl?7,,s3M՗h:M\ˬsIk,daz&j 22"ntcČQ-X0^zoft)FE[.=[ol)|2;p.VXkX"qk-ZtVH4+UJ=_ˊ]{0)Ⱥ }ظwN;HHHw#Ngg?6bԍ `s9gr%m4  OO=u߈ŋ>}jk _^nn%] ZwoYĶѰPif!3P+z9?Q$cenC.WV|~;__^fXZGڌ+#rO{蘩aaa111999CŠ)bon_:e۶?$&fF:C!gKNOJR>w@ZM~^q1jVS5֭[gs ץc֭Z5G\ϜI3oXXfꛪ/QB=h]֨Y/ts) pmbk-/^5`HR[^:ա|YBZhbmN7K֚!qbbl(Byami[UfĶ'&{"kc.27ST}ʇ:F}B89Qu}DDem,Ģ;9nr6ϰ%~-KP^S°?՟өXcǎm7z'{o7} 7VN_̕+?3\azv~I .C8Z\FȺ kO^nex[h2B|ŧ=dPl!ao:ͥe޼uW-."OX`\˖~rMipDySWĉxyy7T.DHK M%WqenŨS5/z6~nni8C)55iHk֬_ }k!2-R\֌OĉMJ~$g{iJխ%$`p9>/nmNr;h]I{V>tttW8t}iXq RݰU?y-1,,ǹڰD~Z.k2 hx:_7bՃkٲOnC\}} 7VNKi6}Ӧ 5Z;EC~3'A86{-O]t; :ʝŰ6rrwWFS)!!!QQQk֬6y_-qm=yr &?S^T\Òܳ'uT?o~Ublvhw) ץo^sMQSjǎ3oXXfꛪ/QBY=u_2ab$$bPnJOJsر/qJ;_]Sr.ӢsZ3ΞMn/Qѣk Gr*#{^GI\5G:GUjzdjVOX,Y?al.g [bX9עoDcNFwf4 Ժu7pc59?ҥ hX4}ܓ=gN{-v@ۯ1?ݻ(ikoe@ҩAziBi7>ҥ(Ko_}PZnn߾8΢^i˖~Yzڋ}M K[a5򬬸>rd\ֺEWD?Wg) ?ra{'uiƯ)+X>>^߿UbŢ +!.m47h׮}a7U_4{ؿF}B@l/?jmW)>%SiY-C+W~~N6Wկ_O,M8(5ճXzꏢa;R}/ǧ{!,toD t*xd*}(gIJX,Y?V8\YkP)\/QpG3Z;֩^PP)q!մG\}u34uZkk;M2dp,^~*Zqub曯g ӧ)눑ٿm[Čck%V36v|-oq.4qϚ޽))_YJP׬o?eKعs[X:Eq۸ڵk䗍K4!3XMW}'VrݰfQ#G>+c_Λe[QeYI? eTz$.ͅ[4_O,s#GhꈫV_K3Rر'9&/jBj}S% {֨SfggeաCr}va.S Cpsi6Zܑf{VGaWoDԑau ,l {o˴qLo0\&\V]G:n [t 1Rd8C#:wsfVnחhbܸ1Qf JI?"=y˱bǤ9{k߾oSkԨm `)XQ,6V?q7vӳLXZŎ__u99IФec֪%sš5((h8nQGgu8*$JMM]k/믫QBG8єZ9BX>a\G:NlNBQQػ(wQ5Z%SvTHLTv:'XǓVfdy a'.\e/(+묖PQ0ifv_w`ܙ}?2\v}wA!=93rKݹXպKg}lYK Na?ugIɖƍFE}hw,Ν룽K)?LR~P!RSWd}w/VqUN,$!IF',m-h5I_?sύts"gqU~@RQ+ҫjOLG tМ0آ@ tBݹX;;vl3rbՁݾM^|qw%LU~ץ5WW՞k@89` C'uk~V@;9VBiR\ص+G. 60pI>5ƎѻwWjUpӦ4i`ͣÑ#[7hP? o֬ض&bڤYp*mاOO///j?I7mh5/uvWn}EwygTf,c*KkE]H=2124 £'ukJ`GbC3Ν!^r3gd*-~[pdi/]&A9!ܔ8C8ryȟ˗j΄|+g?Jg{u/?]u)8xEzU퉰.0RPBxHz\|SřstCsQ&~_R~j4;'LXJ˗K7!\;_|ԝ@!9yjisz`PuY燺Rw.F${$굎͛??t(I66=/sa4ЫU~j@sa!;aL+PѢ#'ԝ]~}>8퐪[ 0dTin0$uOWQ)i'13D0b|WFԘ4zF*oB.f,dZLLL->W;@" 9  5FNu;_t4J;a/#Kyn^姫dZiёZ z9?nڢ󟲀r2ԝՂL d~2Asl] qPsOS:0Flod{l姫Jyyy甔eFϸ9ˤ rtSP.PFszLf:h+aPu:Rw.vǒ"Lf2"##iζldQ7tBڸ 6._+m󟲀r2ԝՅL2]? 9 .A8rKݹةSeM}jЅ2[ǷPFƼe6cbb¢srrP74DB{\+?*z_t2se'O/Ӄ˗onYĉ;tТE+W%PwtSP.PFsB뇱345$C:u4{nݺ@'WAI&w}  .ubO?7p.o_#n|88l2bbbVkAALw&=oZu۶m~~~999?cƍϜ9?xyy߿>iwu'B0da @UUU4`r-{ ۷/-Xvٳg'N8|?ܖs'G)H_#姶S'{g^ĩ7 qb0W^^^uJK5j4FTϞ=w}ԝ`醁L\zڱcG߿8p~NܕsSQQQnLV7%_BiTy SwX)=NOO/((w59r &\~}}}7l`qQZxy͚5K%J@)yW-?8p@OqM6lpc O6-7skcL>e z ى,B8R#MZ.X )w6-T\{~.//Nu|xgRRR 4iBWcaa!_G~… #GHU%J@)yW9S~*++Ȩ9sfPPhԝm999Ikc0z*#'_7/bbbqJJ ޅ%=ʳ]vƍDGGm۶utЧ.]t}ӦMj-yW2Mɻ\n믿iii:ubWZոq-[v!;;[ܚs?WV+]G~de g,Z☙KL/5^}+P2nt9ass:Vr ('v'ZN4jO&=KKK ݿ_D=yW2Mɻ\LN8!] OWEt¾?vǑ PgⳔNZ:uӘNf:Q{2Bb}~w!JptCw%PGP\~{fdgg[ĸ8K%PQ.>zwNN:EDӕNZ:uӘNf:Q{2CR2z҃WD>}M; J \\՚ TiiiQQQaa!]dffe|JJ ][,8/n2it҉J+t L1tJd<KZ e#x;;;`/7._+m:NQ:Q(</Ѡ)'3otsPw"c@2@CYA>(T)hP5ftU0.u:ĥ(VuL}/:YuF''JN4yߗxðC!Y5mUM;1 ӍcI4&•2ycP,Ū@E1특n{&aʬz>0H2xڊx# -Q&7ymt%04ubH>C_o<{($] I n0tm홸r0lPM Fݹ'=RC٧ 5ӭaLVũ_>q5A Fݹ'=R$w>w"$g#q<#޺Xּ2{rgz<dd]cg' @ݹ'=R\q km^79ɔ^PPPTTc `2LWf28BQ5 7˛kZl!_~P;S8]zhEg>(:ܕs1OznLZG DsEFFZ,lXE8AR[2ytpk֞X{b.=ݹ3\Q5 =,S~f+PN~7ki1HUzn'ubH>p+M]P>O#CR⇭^cZ 3]$5Lc ӝm)>Ta= Ď ׬=UVw^z˫]V>(-_nAm{{`}+:]DKzj7oٲŊoӏg6m؇}jȐ~fb:cx:|'-_u/.4ɏbwCҥ]@dXvU{JObYVOOۺM7yIgtSM81qa؞̞=ArN&oٲva-Z4qfI8 VJ|?KXba]Gf~_T~v!4ߕtB:8\qtڢ>Onf{w|u#/XX}g⢢" 50Hj^@;OLttEZi+ RQ׸qìݗ.oC*-<}z+[m;RV6LN)-uk+W7G~Μɞ8ٳ' |/==7ϏAG.ᣏޤQ.^)tBίJŲn={o[7wx$?(ZrW8*ÿcp򷈺:uXzEE._#+DRGQ2V:Pw.FIc(پ1Mr|uc xw[96M.C B16Ӌ6` <$vO?M4bQ=NѼc \|QJo """233 2 !dzW^~^"eYF ϝ!>5Oߑ`nhGMfA:;~R(?鑺} N^ox'ӽִic_ho<[~֭{rԖ^nڼs=NN~O0OM)<|.-qp r|}li3gH[1Ef~~le+ަ֯rݣ](m6[ZUͯ0BbփŒ5gd5HR~ƍ.]ډOcpFusL,/MC7 b7XīQQQaaa)))yyyEEE١7TR^\Á=];~R(?鑺} N^ҕ]sΟ?iϞuw0ovE}XUzݻAz>II+ض;a??mٲA+ol?dHۿ75ZWȷb'I/JGvL=!!Mvjv(uI !ӑ.mk{tWNTXi݇})j 4|mۊixRM7uhܸuiĈiÖ-[tp֭_.жwa5-!4<6jԐ}yQ oҥ]iBbOfÆ/=͚/㟔F kW!.?_Gs5kJOUX\N=%wڑ>ǎ.J'Mz= ڕ+q't蜜Wƌ.ߊ.J=It!z78 =~<.JARCtdhUPP8j2%.ǕEEZٗ.^̥m~agujtK,B!N̔/rb;9U]m [$NB\{~Nq8]chbmS;|oU '=R\R6+ =zta1pl3{v/J%+΢C S}%[9zKLMzۄ]V|j֬5Ѷv !dz2Utmڢɉ|( $oB8 yI*t”HO?[BIcרEպdҐi1]RRqj]&'СH^v$ۛw.JjGScvu(uI !Y ]@L蠭cWVTɗ#jO`lߪOznF-.Jϟٱc#8|8lptuؿ؇.Ժ(=}zk@K/=uLvB2P'I5jXXU'68HYYClgw[GRa22fGmp2P{wVP~#u5(mӦo׮=x{{7l`;Bif͚n_*iM4ZuZ=_t):uu۶{g7J?Izb}޽$! m߾urW m.JARCtdh4  ' ߪOznFt8~<ܹ%. 7+@pUc97I 7,.IO[v89tB\^I !cd h(ԞC;~R(?鑺} [[gL 70b \^I !titB^ʟΉ,Gű/=!o2pPG?[BIc׸r [gK/J?ׄARC:2yfGW=wVP~#ue[gb?^5LqpQzM$LK ӝmk{tq*i'13D0b|1S)L79f,dU'BuLL jO`TߪOzn5VZLRg`2ցLWDL7 j䍎Q.\z&xbhntԞU)H>psE)nYE}FYo^Rg`2ցLWDLCB*//27%%bye qsI<u1u4u7u:jO`TߪOznRkK)H_w!ŕ=)]GIwm6$5LE ӕ6ӵ=:@Q~7yyy`/(6._+m=Q;~R(?鑺} JϞKj膛Gm_}!ީB%111tQSTT$}I!kttmU^^^ZZZRRBk =@~T2¾di.NG ?[BIc;`|71O 0$3X'&L&R4ZqGB zLWtgtBLs=:B.?JG1G%o`BAǒaW*6t?M),,,--PN ӝ dʎ/"(tju+<r9oU '=R\hw۞39ks7_yrTo,|]0Lkގ` d:?鵰m,g%N˩U33 r9oU '=R\fٕ܄vHo|DI8R#M z߯Xk=y eW/ѻ/Dp` d@űm 8ϣ*8 VP~#u?N͹)0؈7<~Et9JW.0tQJSRR+_ :L2vTZj*PB3$wI 3 r9oU '=R4!.ZtzM3{oq%g/sQ+2j*b  1IW7ǟJG10,VuW,rטY՟/ 206^6N$S}ړ<* ܝp/Oӿ[~0[t8diKD weSmn:`PߪOznrw^0f?'\?&B]t݀r!on:`HߪOzn2߰q SB!8H Cۮ郔 FHU)H>e?g“&A&td醡mWj{t0ʊpضC###-Kvvvaa!?[BIcOVq4PM̢i9J7wA$醤mk{tп3 >dpvBwXւ|y?[BIcx J(!(7?UM`9dhΐ#*F]b֬v/W=-yίn8:#lQQF,^STT$xwVP~#uxw *\Ƀf~l+ n$!ӍGL4r|sqhӦ_Α/GMmuIDAT[ pt8G˯Gψ>P;&xwVP~#u])ds)7^$!ӍGL 9eW1yy'OfʗWiT~|Yӧ Gszfu)xADDD\\\fffaa ?[BIc|eʩ&ҿ[~($] I n0fGsԿ<}Р{7>|/d o7Cm께 姨yXdMN{=Zl1fKrCz≇ƍ{ʕt=k׎<==2G|@=8:U5ƳvZ^; zx[M(?gwVP~#u\&[; 2HtmdI…on/^:d 7mdɒ)g]}}O¡oʕ,~M^~`B}G., ?(GMM]I5kt͚8 3;~R(?鑺} Wm}Sfty\$8L7m3]ۣ)-?ߏ5H<-mŲL…o|sS(!$5u7)?mٲ=|8-&IY ѵkǐij]MvpWEqz܎_MG%_b7P~ğJG10'eVp7$nfGs=m߾Ν~ a_9~g/{;zh[l3TVw(߄VnԨaaar|VZLq܎z|ʗH8P~ğJG1'Ξ:u;ڿeְ1-*I@A,? pGӦmly6-Ye/;wnU~SNJmQ3fVGBH6)-ާOOw?YOR ~jOϟScPVCL7k֔Vص+G.26l@/Y8/B~#G۷nР~@y| 'P~ğJG1ȸ뮻ιs޽w.1-JH7 bt6ӵ=:/ 99V^}[ܓ'3Bm*/-_ &}3$_;Ϝɖ/ vqҟ^s=8:REE8S]~'(ߪOznx+WL<{E&BIK@0HL76m3]ۣr$6T^Bb-3ӈ%+Q~ŸJG1+{lᥥ6'N 8{ΐW,W1HK[uW}ƷOQU)H>4'O+RPVV֯_~fT0HUd6ӵ=:syU^@id5u|4<<'(ߪOznxݻG_|ٳ_zLK+O۷_hQ^^}uad'6ӵ=:'}Kbbb&LаaC?BCCTX7xٳVk^^fs2 !tmjS7tybp8Sb>^X?55nˋi} BLFL_M!?m\)G>`^d Z%%%0|oU '=R#F0111as캇A"=ݱ"TIIPˣ&;;;l\k :C:Q7?[BIcOCtIץ\ZG۷oll_e]\BH`tmk{t0q)fdʅ}D]*>^L8{Qk׮]׿+hN 2شtmc8U)H>@EEEӧO;sܹw矩ǴnYffvE$ =NOO/(( $0tc6ӵ=:~ VP~#ul7^^뮻:vաC;ct]j.rXBV+mH Adi@? q~R(?鑺} _>.,,ɡLԌ #,)mB/I`Ʀmk{t@.?JG1gK- ]|FЏhZVa2tmc8U)H>Xfdgg[Di!5 +R bt6ӵ=:~ VP~#uv\TTTXXHל999t)B?Bjh5^A$醤mk{t@.?JG1KӒl"#-&\ `2`tmc8U)H>0 @L\06~[BIc tmc8U)H>0 @LںX\"<炸ܝ0 @L2x J!(7׳3;wVP~#u`0Hxm3]ۣhi֏ \(\kl˶U)H>0 @Lz,(S.>hzҕ;~R(?鑺} `0$<`QuE}%>bn?[BIc tmN6D`txU)H>0 @L:_FړA`33U)H>0 @L:Q}rէfA8!;~R(?鑺} `0$<c}( 6!;~R(?鑺} `0$vgjO lL{f*P7ߪOzn I`5 ape]㌻C ׬Yϝ;w &&s=z eM3fҥK@@@\\[ |oU '=R $pmjt :vP?tB.qƭgxmyjܹ~;=[nm6z痓?6n̙3?JKKg<@;~R(?鑺} `0$'B W~zEGGzt'%%5jh\͛WTTٳ{|! ?[BIc 8=@8=>2{rgZv_S[/&1mڿ3k5r &\N?wÆ GgϞ7o߬Y=@;~R(?鑺} `0$';޵'5-[^^^m |dnuv_ip g2-ϏorBxW[YNBa-:}-@_AM49{,ɴ~/~ .GYfJO}:wVP~#ucIEp=a Hn?]틽x1Q<|w /|: ?=5ww3}-@?~m۶ƍhZغuk|.]t}ӦMU)H>jߔ%5uK 8=@8܋-[֎=Ef:=qb3-y7ؾe+V]USqڵU{wܹŲm=6n&My`V_z)//vZ=u h=ztg(tig6/Accݛ|[: OXMRSmР~V-O?/ƦMa?m!>*-[d+ ? !)v&y@ zyGov?GY{D K%fW@ $Q ?q;[BIc~XiOJ$'9]yo{qTTlbԱۋ6]&'ЏqqKZre/8rM{ w߾:廛7wxjv[{7n0++ֿt)};Ri[')zPP`p!__[g;RVK7u$;={Қ߿y~~|G>}z~ ڐ  uLĉO>P: }U[_!?='$?+˴]Gً@+ݟ?[BIcN~S0̨y +` Heߔ§2GR~3g Oڒjʔ1^^^/j®]Ԕc6`ӦWGrv[{%M4bQ=NѼ G\;,?aa{w%בPfwGTj:Zʕ^ %%[$G3`wUwVWiOy$?+~Km}cbf.Nh;YE4 C!&:vHH.|oU '=RzמX}]Z5}tP IvYX5jܹ’͚5W>>~RSWM թS[dU*QIb͚Mhii( OIΜɞ;U??Y^VhomHvB1xgO0zEv7J!Lmڴd .ǧh,^*YIU8zC^~M8Yq՞ {1<u9=ț4$+? TMm~is5kڼo@_aa#֛nиq֭ӈj  _5%??_p''UgZ8$Zg„{TEE}߼mV[Lg(v~3=.]u9=Sdov#Ʌ 9^yv[e㮻z?s?N/ᆭ[Y+G g]u5_CXS go⃊Zd9d2*ߊߪOzn'@\o$'*((~i=Ǐg?VV泯&][Z;|K;I[\#PTQ<)_h7{o+_!DJnݧ]W(_Nѳמk@[[BIcpbqɏK 8S{bEρ =i؃A8pz*p")蕓̆@2'Ot:yHf.VVP~#u8M̒m|퉅dj(y$']@!=VU)H>c[<ЊX_RZ{b@QO[r 8=@8}B @ \v3+~R(?鑺} t,qk\'~d}%_]퉅ur/kfXbGJa H ('p\JGE۩o?;P^Q5#_h㼻W:|7 2cGIn7( Kۡ":FX $X8ܝ?Y&8R#Mku&ԞUj7s*t.vP7#\_6{{I&>TSl+hw A ;you ~HCCtPsx,H @!Ԟej7s*U[x٧_\ ݞoЮ|痢VX)i'===&4,(9 <N$cW}3sIԻ #ϧ ^p&T_l&ĵcBVu"111=SU)Uמ 0Kw6(V{2%_G^^^QQѹ_[nMT$X8 C\Zqqq,222BʅwHitYG=SU)Q"Y|ɮVA P{2L0q?NBj4Ssx,HF"TcXff&}X,qe qsIitYG=SU)s],.14ם RdJ\{b-G\~b93#^NI]'5Vl|M^^^vvv&8g/(6._+mFg{tOf.VVP~p vׯYמpADბs=?VpAWii)} +**lOpotѹGg ߊߪON@zK-KI?g%\ $PUUյkիWݻs lbٳgӦM?WߊߪON߷Q '5%s-<5fKWpܗu-ݻ788o߾" `ڵgϞ8q p\J,~ ' "hNh9PH,=:xɧ٧/ 8An)77vؑ=_SDWߊߪO 8@ModDٻ_jǭ>#] 9~p_vO6l`'ܦM}||Hf.VVP~PF^Jy$111=he?ߧ<Mǡ. ྜυ~ĉҥ*g+~R(?@G%SB.bѿV'MS$wC]\,.sA !Vg*@핗&eQBn[.;;;33C @+q_6(nchpKrPnnooU ':9)kSZl6jOg%Ӈ F=ayǨwM>iupRt%`;ŹP3V@j7s*@m+A tkߔ妚_OCoP'JԦne+O) BqθnnooU ':Q7!+V{ʟΉ,QXjĞ oѿ&cu*P.<KߠC!s@O?V@j7s*@=\UA  #f~R.00ߥ+nnooU ':Q7!Ԟ ճ.~>O?Sͯnԡt%Ѝ\JP'&$ړ!UҞfUUUյkիWݻs lbٳgӦMk& ti\Dain:g[[B NMHաdTn喽{ۗVTT]ٳ'N>|' w4՞[?\f.VVP~u@]=>UX)77vؑ=_SD ;՚D0Orn:s[[B NMH!aO6l`'ܦMvz|&k}7ؾe+V͖̘1Kv~fbDS6hPUCycӦ2Rfn]7unmV x˼" U*WDiKu *y 9xy}yf31AZ)*Kv8tkS.?z0=޿"+Uӄ @8;7oo|,rDwo|:PfҽM3@%O~PT}5#"XVeOn@'L4@!]ng.T^mA A:~HAp51fCΥ9Oy!KR,n:ޤmuubbrt{d\T?y1XssQ|45.Z'0E?74T lmeaa]dTWmbO{Vn\!-,q7>o`ͤJ[ǟԋ!J8A MVQprODz }rwO9S*+\.-X*# },,z:;׈DmڵK +4,EӁ6*B=)JĚP{B{<ƫ@͜9+((~޽[&s]z\.^\©ڂt  4ji) 49g~Yƍ,T3׮e]ݻ'Ns^uHKKi&@OO$a"]7cttUSDDDh;]'XsV񵧋|pMeUw*+u;cz,WY(h*11gϞֵ0 !~ݻWR=x𠾾`\©ڂhͽ*f> ^AkO<7k[]ezLhy x'DFbA֞TogM q۽ۏ]Cv+W.pssGG?^mQw}W^Ot)Ue@~#*K2;E"Zxrov {/.n@+/Q.vor7o''$sԟԍXY n4D%QF\ȃ M*RlU6t; rC3p*a pxֿ9 BSb,& E%a34UW]Ěm гm_|˖YWw"3 ~|ha/u?sfcƌ8MMmmeaa]dOA^/3nee;.@[GJ2^ EEo1+WU(֐1RU]i^'l1$ih8j#ܠ!f'Ѱ!!55u }o*$Ceǧ{J冾}HLMMk͈<;DŽ$s"Q)䃤9HT^mA At>C1vRm Nxd A(46ӞZ}H9ؘgggN߼}^ƍ~@^<33wΞ=#?z!_?FIIbϞ{|@wO0d=ZV! F5ܜDǧK*[?ȱkhDž kkK΃cty{Lf ޛ49 Tt'8 wLj3B`|$F*a :rft_{MJ#.: V:+Ŋaׂ)/i9b2%[N@XXXdd!hOBr$88oث^^ZT^b֬I̘1Hdy#0 r$\UDb^:g\v i[JXcggNepvEC%ܜ=<\Ӑ?EOg>|xӥU;8EH?  =Szo #G@7ԥTcY 99 Q vIThn 1R {'ѝaGC#baZVXuH q`'®4UW]ĚEEEs>P?sZ&󳥥>>FZDli9\v"JCОZ}H9@Gf6-5ʓFkO4b,v#W[P~BNc9p  Qʾ,iqh^muH q`'®4;^v9k0/!O1t;Jث-(?!SuZށh4 vw"bo]]\WTTJBERۍeD9nܸ@Ge˖~Y-ZԿ޽{v>؉+ N^P5i}dT0Ԟ}9HT^mA 46؉ bpG~xQؐp'pe++K.\CCC]~DBG{H9(4 'Dv#W[P~BDhOK>]M ;OKaPBĈ\'fffp0bILLn6..#**ꑼmj# @a=!Esڂ"D{3 v"*P u̙3}||ݻe2Yjjjz;W\իWr&!t5k†VNlZ;o?E /H%Q@IbFANJLLٳummmkׯ޽{U*Ճo߾]^^.///&!t5kV:e=!zEsڂP{*Oyϣp7hР;v >5!!{zzw}N "u҃1srrrtt...wss`!ZAQ# 4l*˯#RX|-X:41BV'It;Jث-(?!z' RLLLZPP;flnn;={.Zh Aqry^^+V۬UYY„ܼyGyFbAxa+P111a Ŋp,(V:GF%ԞN9HT^mA '=I)PD~"ww))) vvvܼ5^Aý*fNfԨQOD$7j$D BQ`Qc 'v#W[P~BjOx("?vjj*KDž LMM+++YCAD?\9\q`㻭n-6"Br$D@}YC[±phy90~`XB $H%СTVf᝷ >z??]C3z4Z╟d2YfffKKߤI8AD/4ߪK6b_'_/7"Br$DKRE""?",))qqq DTt;Jث-(?!BI:WW!Cv#9s ֭+9ˣ7:eӼO/4~mhcc58=hЀU8EE}ɮ'.""b-xzjҥG!GWTd[^yeqpg>Mϔ3V.OjLuF*Jf x"XVeO(l!hqϿ`Gb-'ڼ>5n)zg~!JL8]fk+ [}Z&@8r l+k~ڃztleEy;l&]U:DR=à4(;.da1L zGᯕlSr#!+.GbAD~>Dv#W[P~B:O?8{,6v}KC{T$OρIrT=lHN},ssRtAwY(N8]DFNȍļ+?i&Y[[eEy;l&]U:DOQ/O\uوIy0CmpW EWJA+էhcqlj~P5AtW~>DTt;Jث-(?!EvɬSR҂!W/U۷h+&70\~ҩؙ3Ro_x|iEES*t!!..N+?NbOt|޲7>o`TU5=E^hhR<|pQQQEEE~BAA4"?% ?f$6-K93 ' ͏;6bj OPbDĞ=B?s?6CC!NuuNmѲ4HklЩؙ3S}I:#cÇ~~MhuU"?"!Bf殚ٳ'5޽<++ (9NK7_?-1xvLtI9cBطo_DDDrrr~~>Ot!m O_E3%ъ{U6},}iڂ[.@PurhTn(/Owwoee1k֤]f̘x\oHTGAo_Y98#GrVW%uE/e ^e$?EOg>|贼MXv dӶTeH]|:fUChc'R f8ش, V\9 ~α-* "9HT^mA EEEf$@",D~VWڟ-e7S\Ikbtwhj:y!֭#-K, ?.>)Lwo|@EUCckOw%T;w.d7T09C;z iMkhE󭺄>/>/z"6H%PZZzɸYK4T UUU(?!ぼ!"iYbAmə8p0XSQ7H%PUU%a1履ږf8SB1:TzmWSҮl0TV$6-K9-?f){W- Ҡ8cqY=y v#W[P~BCSS(4ҞZ="qV҇*:#%iYbA5)O vSX>tlʠWAbj O@JfhSB1"P{B|6\y[bӲĚ :`-no SK)Abj O>AJfSB1P{B/iRm}ش, 4ݨ6vnq6E9HT^mA 3U3z l"# P{jT FjOhz1JR 4hǎÇwuuMHH 񞞞666^^^}Ħe5At3Abj O(46ӞZ="MRLLLZPP;flnn߳gOmmEOMkFWq#cCsڂ" l*˯#RX|-X:)b28 #/]EFFԪA Ԟn `;%%edήͫmHlZXsDgb_c! v#W[P~BĂ@DDDI Ŋp@*V:8}aWԪA #՞.^L3?L:ئrO"Sqq1 T"?EGGۗ .VVV(AbӲĚU34?]]k]DpCsڂ""#-6fCZ;v!hO `їRF6:~-3sCo:\O{DE} S,mT*'2rСG+_DW^ϛ?]Cƪ~bRSAh b͈O4k&oCTT'wXy Sz8k%`0`E[F$]Lʮ̃9m%۔28w#iYbA ԞbD|AAbj O)r̙>>>^^^AAAs2,555+W@`mmիr? Dzh@;ݫͪU[E͖wgG'Ukh>?vkLf V^.P ;HBʼn$ rC߾dSSӊ[~0o޴M=7W'9ot Kdgt}=s>>sFm);mJM' Tm'ܘBٵRgD{~hhR<|pQQQEE} '< {5J5hР;v >5!!{zz5!YOjOz4jʔ)v"ہ˗/HYC !+W0i$F*a > {imm][[ ?,? W'<}vyy9ኄIh"I:T6nx0τTW46~Ţ7ofesܿQe$,lu޽rݻ'gI L_D IRxŎtUS;{dT_R&a{&=G'N|¢R(Tڲ?%Ln"n$өD"qӰW'?{scOLxhO^P(Ě\:w̘1loog\h9P{һSpe $F*a > vJRngA_(  ^߾}9d?q^u;&} & >z?א]Ckx(\1:t;Jث-(?!ܫbrkQF1?{A \C~3I<`O…^/…sC̛7Mu*yscl|;׳ e_&@*Ro:@ Abj O Y&q7s b-'~B1pPBӋuF{jhkw;`@_ e8k?A^:g$/ cdƋVԯvy1)"35l%ڄʴC:H%D66Gx?][Ny1}kxhw*DV |v=[ƶɡ?nt5%vugIWktH9Ǝ4vǯk? )P:kLf V^z٬Zx߾U4SS\._?TIз <>Դ"CݲS---9΄5a҉DYŒ[Ƕ_,4F>z1R {'ё?,C[Ŧd  Nb, IWktH9ƎdvG Δ$igrwOlaqsjkA*^^`NΜOd;>,{oҤEsؘgggN߼}^]^<33wΞ=#?z!_?ש:jcD{JwBu(?!Fn^T^mA At16^>w*D#@%~uWÖ ] &hWEFFj=v4 {5ؑ88oEBɩR<ݽŬYv1csr77gÇ!9pR3=zؿ޾}9dл')czʠ$&촩ۙǚ2H`lĤtv3W%:bjOKJ%Oۮ#W[P~BItF䧤3qnj~B1. TzzzLL \^!aaarPXu aa0H`a+5:$cGJ(qRm--d&<t^^]]Z`7nd Rgv-5Ndޚݻ'N꼼9F}I&ֱ''|)z1R {'ѝ/Nbt0 T~~~VVVrrr||| &/6sHL *0``h=v4 {5ؑ 9ˇ*Ph=A!hn^T^mA Atactgq}w*Dc(Peee}~~~nnnuh^mu YY0H`aÝ+5:$cGJyv(=$Vi|D+tbj OT;ms<>3՝ wz H麺:(+++EJK ImwtI\RZ  6:܉ O^Cb1v;oO*hn^T^mA 44  w NH0eʔ]v˗Fdd!CBBBkʕnnn111$xFĚcHlwG*hn^T^mA =.񽞭DcՔ3>; (NH8r=GGǣG†\.ے˗/[YY\tCjxFĚcܫb#@$=I{#BzM#W[P~B:hOg'}44uFTJS! 39fffpC2bILLn6..#**ꑼGxFĚc\9?gwxj T顩3.iO`#9HT^mA Ԟt3Kb\w*DD2Г̙3}||ݻe2Yjjjz;W\իWr&![6^Cb1Fo%y^n#Vд2iOc#9HT^mA 'NZ0^T  䐘سgOkkϽ{T߾}\^^^LBݷlFĚcdM\'{7s b-'j/Jb}`Єlz:aaaОG,GFBpnTDv#W[P~BԂ^L@{Aرcᮮ $<>>;&>NHzrx7agg]\\looᑑDC8}FktH9Fw=x╒mQ;JEZm:"nTc@htttt4'fYe2`B (H%~P{BӣST*СC |}}njpgϞEM>+  PWW'Xʖ&͛,?ݷlFĚcmFBn 1R {'Ԟn =aJJʀȆ',..677~tߩAF;A[֌5 ݷlFĚcda1oS>mJn$I@>???777 (.4:hOkm6.N6 =mFBn 1R {'jO/&o~Nee&Yl{zPŧ@^jj*KF LMM+++CߩAF;Amuo]xFĚcT/ڬxd / FuuupTQQQVVVhttttdc,n 1R {'=v}!_ЛIcϝ#aNcL:>VQiA&effM43J 1j}挥Kԩc!ìPJ^6UUtt K$6?$UArDf ;ʓh?P1dOdC]nBٵb7'g*4(;^T >x۶E:/#^FB:+5: 9-^-c.?]4ع-Mܽ-U{Y˝1ܙ. 1nt#W[P~B~Mʶ|ic9iiK711w//)iYMM.;%罰/۷ׁ[,--gbc7 B9u*\A􅍌vv-+ ѯA4>|XPdUUNdCkk::A+V27%2U*Ĥn駃ӧ Ś#dWiLކN$ZAA$'uq eJ`[F$]Lʮ̃ym%۔28wߩA 0-ټl^\(/du0)/ ?2Ѥm~]Sp;b҅ 1xt;Jث-(?!u\t^6V-޷oQL7[ZZܹsTLH?ܵO&NI JK +/O($!bDbRo_}|iEEƭ[GL?7oZ\ܦ꫓7%3ij:meeq>؞9E^^m_f6D*ώ6zpnLZ3=EM?44TT>|Xd'EXYc,ڎON#.:_'^aah͹_{^)͙k7/lle7)ag| i$ v6N?bã3?P1@%{Y/\ 19HT^mA y?63&&&9yvv_|͛d~_zT [ݻw/\v YRس9~ C^<33wΞ=#ןɬ32v>|XޤIϑTn= ϟp$[?ȱtHAg4lv@O'S+^#S+}EDD$'' ^H:~ ;0\Htm>*YM\{Ư'?ewd ͯK몽z}4;\" * V{"RLLLZPP;flnn߳gOmmEOm*"&H%.0 /Y{jh"h48&u&NSǏ6E좿x Aܺ伿M>93Os{8xFG7~oVlMUb忏&Rc'bE䧼`O_*Jfx[-'M+H9VUuN 1R {' T_G}X>lZpuhvuk[N@XX\i=v4 {¿m>(ESg$;Փ'Nh5](ˣBd}n:\i.k!a Ł;񹚘 ;гs}Ķ<\vDK)VVnK{x^ܯߨ~}n46nb64"Exlj:mk+ [}Z؁3nAQ#n 1R {'c TzzzLLLDDDxxxXHŊu\ 6jOMv^ANɕhq2N|E)xFG6"(ڷQ(2R{=.]#$|z20B gUvӁ[,--gbc7Bg&LxjɒI4Fi|++yfd촳mi_0NT.u9_H]Sd:28w9HT^mA FJNNS] Ș/6sHL *0``p%< {DwퟷhZYޓ,0U O^kCJG6jB7ϝOz/?h| W\pZa<.:TvɬSR҂!1#z4_$[~8DOD{xP\^n-[>0W^??y3ZgOoll[gnn V]Ûmth_`o/as ͙3y̩ްaK|yR6:p #EtLW?}Vv}1F~3Q1bͨQ:Ug(? TIIbϞ{|RxwOC,--Ȩ&֭@؍0apO؛]̙w3ԩgq$矿7ǎn\vC?:}Z7F۷oѣMu۲ SuT41q+IkggP3 ](']_:fڥ"?Uo< BgH':_569:miIIbnI4޴ '[.@(vR7lo_ "0H8EHWM8P}2\fk+[UўJ%ObpR {'rʔ)v"ہ˗/HWW!Cʕ+ኁ 4“A{g8wҗhXyTt`-*Wkttms:nD#"ֲx_Z@\97//{̙/c66UAl߾T9@59}v=z`V&ufN~b2!?9a\"k_O_|q4wߝlٛt0^{ڵLNKO߾Oz OEE8;yΝ̃rFy `tLD5|'*M 'W[P~BN9 GѣGaC.O ϝJ y3Q鱔 Hիͮ]~*mWg(? ixczJ8׉a~3;=w>E,%%GD=m>UJhA''gdub2 6̽CI]LT흒IDATjXUU'uE{xm7^qt{ ɐ7u=v?|WM+`(? ̇;@77z1Ԟ&Jث-(?!Hș3gxyyݻwdv\WLBݷlDwŚ|ͪK'#ixFG69gr`~~  ;s/&74"x---JK6ȓ&=<|Xmr2{>>t(?+l;5 ԗh( J?ﻰ=!o&=vy33t޴f3Ta>IJ$Clb8ڂt LLLٳummmks޽*o.//@-a/ "]11 zxx/NFJOVM+5:9hǎ%Q߾lԩp8zaasg  ]H2| ^e^dS*7aTlS']k3 >aUV/84kku>@'ʨ+[tBfhC_6prcgHgc9iB Vz&0@ԅv61T^mA A:}@>xۛ 1::###pAXծ׬7uXQ_x`m$'o+UF}(< {mͣEPo!ı e]˼s8'lTVfm7nd 0^{DMM..ltwOUu0h΄ǠEkVtZ^6\o31@ Ej O)N.bTʖ&͛,?ݷl!|С<F s?:?U(? ]ۜ ePzyT=J A8HZ*\L:lF44(ZSpR {'њ{}$> 5jo{ 'u_tD~z.`KsM8=)J]%ɪ(? ]ۜ;P6C cCCL4Ԟ&Jث-(?!\96q֭b#"< { 'u_tz _JJE] =zܹM[I1>YIt~9!voE{%44gn[JH+և-_Sub2"/]Du`DFFlb8ڂhMx?}@%r :!< { 'u_tz _u눽|955 LSV?"| 'c˛WVB\{",Aa+P鰧"""4Fb=LDmBPjO&Jث-(?!.fp2 yop""< { 'Uz'>P?1^cdOB7JaL!5{F(?>)999>>>Fc6þb3ס%P( jlb8ڂByA8짖lslW6ٻ&-ChEr-Aa9ʱR@YeAu!UڂEQHi' Z Gt="ZhIg7fgҷiL~'g$3oi ;ٱPK|-ObjV Ws4%}i?kzUˎNoNy͌{ "~ys+)޳PsEGG \*| 2(l6`mR"ھsa@"zPzhz4P{c&ŎJLtt@}l `YJPdǎ@-3tY֋(BsLtONgDyuTԱ c/$4mO ԯ^RSSM&SQQQRRbيiVk) O$'@O:&l^T(?Ȕ": */*3cp%vcG(||!͝vtJ{ϴP~:u2;((U]ěSKo )m~= yyגpIs8t Fb~]f q"1bĆ K,?>ݠ=ӽ{(>;t) N4P/ăbGB @gړePJHL:Ydǎ@1zf>zJgl'<׸@;w|;lZ rƍ7mڴ~xڵg}ּy[+:u^roٲ… Bs9RN/vT*j$Gy05Nv(ԪCӗg1$is]Uz*>*P'5Zz_6A'fs PK NKZaF+ ĊCl7~z8l/ סsObbbvFFd*((4e"JKKkԨQӦM\Bk~?YQQq֭_>y$.\;r^T(?^z4v<4{[P4W w&\`&;vj[^+%ly\\E׫rv?V`c2D6%trc _?2s}kcois7'rqhDLDnСԩSK۷o߶m[^7tԩ[n;vp VK9QP~U*/y*qG aiGK8{JɎv-n"Na JY 3u*7mըo͕NicolrTfsVVA>ewM6ݤ4fCl+>|K` P{.fkٲe^^Šgϖ/:š޸j);*O27o|q8: ڏ{aȕ -M+{`ڜz48m5+MO7>|^S'vcGҥ'5jhT#.@+s(_q%&zU\ێܹzq2|jZ,zԤz]t:z]j4#Ma!A P{Ҫg8QtժU}q,;Ӌ 'P_N t rn&|onYooB?pJ; u(&3|2ҲUO= Usm򔶣֞趪'),CF/zLјj04J#i*a@h@iXipiiiii@/DT#ݠO?tҥ;;(;*O7fhe6r?O/]4PӾgNjnZ.\OD9QP~Ҙ]K7sgr/vcGn8WF#qqqq6, g٦w?LX{o>lUuո 2?We/-qmߎBYFQ0U.T^aȱ5 ʧtr᪠xҗrzR*phr}Á-" ZU@ڽc7wi>uNv(G?2:]A!.4\CUsmã)Cx`J,(6Nƃ?ip%PyK9QP~zoY Yu)YZui籓; u@)\JBWε|%`8 -2;ݦ%•@ /bGB ۰(WحVG9n; uF Usm;ema8۔^mn:đu6&W)yK9QP~3zfŵ4wާ_K R; ydUR4yjmQ; x}ϼ g]At\/bGB F);qa͝vx  s RɎzW_R4yjmQ; x-W~rat\/bGB ^\XAsL:!!h4Z,sHQZH}m;yma8;uW>@=/bGB xr? .-\r~Nڹ/iƾRSSM&SQQp/+;ٱ൐jvp@ ݼrjiz!8^{'%).|U8 ^邬^T(?/;9qaE;6n\$^^Kv4?<..jeb';vCa_Nc{^cvl>?;ڝhu:{aѩ7c5/bGB ̓>}z[*^^ǭ^Oq '%%R; ^ ɡ-;:pTMG׬o7j[bLC?!7G#W03({+8-8yyK9QP~Es4pW^VvPvaZh֭=׿+RT~?IjgOݻ{֭&M q#_}ЧOZ'FK\uΣ^ɡ-;:p*˿ ^0[ƭ/uH.۹vK;/' uҗrzRf;6 q&oߞӦ=\Wx?qm̙y_5|_uw^k.y7Qٳm>O'I^7jԐ/}m9ر/\uP~oP{Uma8M{x^-++= ~oV_0=&9 (C"-]M^RN/vT*@/?KSˋ:q;~|vU'ǝΘ,;ۮ]#T ۷Bm2 {>>& 9`txm5T)==_J>`'鲗'eZ___v}t]7haa˾U5 9eGUGcv^EŎ?Musvْ L?d2yCJ^RN/vT*@d~e_ǎƍrxƵkea=ܣ]|xtW-_~˗sSRV5pC^.$-oҤqAAzr6mZLtի{w;*PҗrzRf]΅w۸qAz 9C[hffnkڴID0>>\|'οv+ڷoCݰ̙ϋ%~ӫWK/ɓ;lۨQÀ ߉oU '6HoQe~7خk0 əFl6[V&QӋ 'P=_\6>Us粯^ƍ r6۞3Ig[y¬|qHO1ٳtWmzڥj۹/w^ɡ-;:5~q[<Q17hlKQ㲲,KQQV?%N_PN/vT*@~ ܭqv21v`5(?];} /29G-;:5:}Uz7\W5jZۋ8q_D \ Ew6QN/vT*@nRb>L S,#:_;x {߲QKA86)*^vJQ7hlG?,ȄhXFygbGB ]#_,4wާ_Z@Y]Fi9EkeGUGcRx0c8AK Wvq^!La[|xJ۲e!_~xyofQ&rNj2 &ŎJ(ݷaQz}%N[hV~`rahT((-u ^K:SUmJݶF!c,<6@o /W^HF\[W.&qǮK/gofm4HYVjrzRJw)zqxV8mU>tfOe*+P(_{u ^S uƎ6G #i 67B?g+׫︮Q]6ُJ/-rzR*phr}A-̅VU++=vo01.11::ZXp}3 ާBʟhjU0EqpQ樗ev$[npȱHNx=wO޿zxA=5~xq'w%5emGDQT={r[n5iR|6gr}s~oէ~q =ɢE޽Ѹnߺup̘?h>t#(ooNwxΏ[F~z=<Ĉn.?ۮ}ЧOZ@U5ؔӋ 'PWu8Z.qBsͥL_ݳgcF #GFƍGСMd估i+D^z/۷9KZ_M6mdٲ[ŏ(j>s̘isVKO]~rymq#m[crsWsٷrzRp-n⯧|W2fp颣UqQ 7mըoͷ=Bd۟8fC/:MQ|Z)=KQO;#\r/O㓖&//nǍB mLJ z9+ƍxk( F7JJN>mmfÇgBtM_~Ofee}5kiaQV;hNgF;xtFCM9QP~8KOj>:0=FVVz 9wi%&;Һͦsm;~rIǨ4)GSgl }LQIhlsT>y姞3:=#\r($EG/޽~FE-'O]Z|#<ЫW`M]n;n>mO.o11K(NpԖ.ٶ`y9v=TT[^-Z4۰aAUh(?);*OEEE[v0o}jɧ/o𵧘JeeeY,ڇ%%%=$\ۮ/?|hj/ĀWZjܣǽ& re7=wzܪ$y~HMχQ~ry.ҥ׫WI˾v'&bGB TdHIM|cѦSrqGp#^҇.8.!!)&&ngddF{O}smkǤv<سAQQEEEnnٳzGvxm7MKh9ES~j׮RCm;6nhР={6sO{ fϞeӧ-+ -m}:n'vglYz֏ZhVP^Q9[SmQ**BBf#F<\U3 *M<~rO4ygF\eƣWP~bSN/vT*@M+PF㸸ubERg٦w?LX{o>lUuո 2S||N?DjO%7m Brݟƶ?>)=Umdgg?<%KOp8Ii -(#n Ν˾zu`aQю|+7Ξ./?$^ARsЂ( $\|I|Ͽ\U5=vzVjliYKŎJe6 a\|مyTB#34\CN3h/sv߇GShrf)=Umt֍cN(JК0vJ(qCukG>!]'Ӌ 'PeZ-KnnhLMMP\\\l]ѽH^jvho>=CjOwm9i%5a~ʎ6G-ƏlΣ>:lذ#Gѯ{rU:`Y).sef~|6r qb>"'WӋ 'P%z{Ao2)b2(gƌTTW_韴h0Ph{iii~Azߊsm;ϦBЌAsv/;: vMQ~i <%ce4esĠrzR*譆e6M&SnnnN]پt@n_0Ph{iiU''*-<9T{m;ϦBP5)ǷŵxQBvvvn~gǒ[n}'ܹsBCC?N~;ul2N8b~7+hJk}5=SUӋ 'Rj-+tt@Om6JNΪgS!(3ˎ6G***<:ujHHH~}||'~뭷?q\.]233Uw𠳙蘵֋hJk/ Gn@*ŎJ@xgka(cˎ6Grss͛SN}zFFS 3fj3uhMEŎ?븲zrڥ@p]yn:MS#W8 l^T(?ԈgHpƾ<:EqPPСC1EqM/;:={uڵ!!!/YԩS̙3fk6++bac s%.y)]+ڕr?H16!!!::ZӥL&:l# &ŎJ@xgkax9S2䩧Ų/;:ӟuڴi<3Ɏsrr:wEAA;iОo毣#h.霌~qNNHH0tjp85AD9QP~oQ6/^r;bb؉Umu]Ο}3gNV5''t,Zʱ>,Uot:xMo)ln=8.>>w.;(;*O5-; &|ڵgvxȑ[nu/ /;:kҤIii  kNNZjek׮5j*Y;?<.4"e0arUn/ڋ8S +qS||N?DӸxC7gbGB F<{@3eG@_ҡQ_իtN8)a'^vTu49'Rmbb⫯*@jQQQ_uVΝ-Z88*Pf9++`0W_3)V\h%pa@%w5y4 44"s ygbGB F<{@3eG@äNQܬY3OUܸqSNhls%/^y;v`by؉UmZ3L¯Jرcz}BB>.EbGl6Ӌ$777Gٶ'%?T0gێx&ŎJ@xg}ˎ (޹s'* ״iSfΜ)a'^vTu49jQVV6lذѣGsAӧOׯo߾Wœn\ZvHNp|0LD9QP~oQ0'OWh|ڶݦ% ;ٱqˎ6H rzR Mvv>,Y$??ǎ,tr:n ;ٱqˎ6bmq"r*ŎJBBBuf01'uhMZ_)Ɏc'^vTu49'RGE_@D9QP~pKYYッm60&B :ȑ#·ldǎDZ/;:PK֣㜈'-(rzR`?Dk2䩧X,V(Ɏc'^vTu49"{x0G"FKhpUP;^~IKD9QP~]wUTTu޼yZ֜kժUO8`4-Kqq# dǎDZ/;:P-DD?1z;(;*OhҤIii  kNN5ke7n4j(::ZӥL&b0; Nhls@R=J~ &ŎJ@5~gϞ.?juDQ8veGUGc KJ9QHfڞE~Cn4L7QdMӋ 'j~;w.{ӧ;cǎ.]&%%8Nv(x;񲣪а|֮Nԍ[ՂygbGB +ܹ3$$+5mTp廙3g?'Ɏc'^vTu49d;b=.դǮ?L+{`)0}k z敫 ygbGB }q^1ydq|[nu^k׮-BIvcG؉Um,ZpPJ#+>,0_6cZXMӋ 'jTTT`08Yf#Fߺu+Eknܸ1008d`';v2Tڮj5oA+k%l^T(?%$$$88L9xchNj2 dǎDZ/;:aY4߇GKhn12ab>SAAf*P&ŎJ[Flل1ZgȐ!}MLL8NEGG'$$FBT\a';v/459}bؿ hkySifygbGB @Gl6geeQN?D1 J )D+j2jO2; ^ ɡhlklsԮz ;K 4+)F۠ 7;(;*O(J077rbjj`HrBB ړ dǎBr/:$jGڲe!_~xp:l׮폊ZWمO&FXDt'd*** CD9QP~fQ,((X,ssssЯBF+$;ٱ൐6@|Mq#tLry;6n\$^֮]kƣDE-11Ku7eЈؤJPygbGB QZHEc{^cvnz]~#nypw+X,C xbwwfϞܻw֭[M|F-9 ڶ޽O?=fn{A{sn5OMO&=un]+QQ 'Lr.\2ulٲ9=~HÆkѢYn_s~Boէ~q h-z--S xoJŎJ(;ٱ൐6@,?5HK[]ԴieB>h>-| >s̘LXznƍ{%+={66jԐ/`?qm̙y@߾=M{r޽Xi8O.ymGF;v,-7 ~5}]> h7g׮ϭGy`„/_}w5VضGl@+^u:thC',l=+Wv|ڂ?9?I ߔԋ 'P(vcGk!9ymհf\~ԩ^8cpnȻ.ӍSУѽ7ĹB {M/)h3ϝΏߘ1Y~w]-cC_y }'tW&S,BϞ'M {K6|6ݶZ3Éժ-=a|8{;_1k޼?1RBB&|iEM4.(HON^٦M+  M}}}GxHZx&O۫W _~LJ_[hF+׽{'ƍ=FvRxM;6j0 …/w"[գ8nTTVvo9Oj߾ UÆ~3g>W{9ʗ >sf;ݶX8Ow}8ǓtP~MIQP~b';vŎ#󪦱P=_!y.Ňfs&ZxB`_~wrNS=]^~}s^q_qKKQ JJ!˧--|Ý2^~zg)OJŎJ(;ٱUND: 9 *em:$qՊj[fh o'hcAI QP~b';vJ%!`CaW)mFSTjҲ6@nrW+m:MM蚕ӧ^T(?B; &ɛH'%57%L*em{=kjާ_ZAI QP~b';vͿ7nzH߾^` -Uvh,-kls4۰(,4oLn5ҋ 'P(vcG mn:2%}w7p%5K zoYfU~]BzR Nv(x! )-ԾұHNem6\peKlVVz ޱ\Ǹ踸8kZ/w6QN/vT*@Ɏ/tD-ٰt.i,-kls敫il {hxf~m%^ :.55d2 _ !l^T(?B; )OmO¤uLciYcb=#_~ t:]tttBBhX,ׁz;(;*OPdǎw' ~XL:^459ZrHД6A1*ZYs㝿yWPP`&ŎJ(;ٱ൒ 䰥ajҲ6@KH9ҝAS$h:d[y, /o𵧘JeeeY,@=MӋ 'P(vcGk9á+eK-lV5ߴ?EۍC5f\ip~'Vst-␪ 5+MOOn5~rړkwfZ}MD9QP~b';vVJ{+ @-XZhIIIIqqqAAdHIM|c7ŤABznKe%$$=파 \bhU7gbGB QNg?~Zڤ h8....z݆eq, sԇRq{1^:_\卋}a/3t:O|O44=&l^T(?B; ^=ZQqwQcKqTfsVV`?A2xDB*A(.4\P?1!AL7gbGB Q6ε'6-9=ժwJ$EIbԤ$jbLRJD#a!AY[D9QP~k'[mKG&gXJvg3 wz Mj3_[|-U459ZEfQX,&)''h4fdd $5OͧҢU€0`ѐ PҀj|-l^T(?_"AV6Q+y6zUU{oj>E3 TmXZh_$CfɔfW՟ GCCD:iēw6QN/vT*jh^DB$g̽c>/< @ص'O :Υ(Zfts\|0~44.99w6QN/vT*jij${ܩ=ɫ@^J~Xi,-kls@4@ *;*O5܇=ܯ=I@]y0w)mF#H5B"y^T(?ԈgHp<7Z{UzwקOn6EemDbGB F<{@ ; o0-Om3:->$Ҳ6H rzRP#= }ީ&'G^1aۇrLφVA")h`f:]YDbGB F<{@PaZ.xR\T G%'4aS'e!iATN/vT*jij${;٧6!sɣ^I}8p_7IahT6 v6s/k LpzDQ9QP~>yU ToQ?#&&㸬,ۇo4k02(׼J8@C"y^T(?ԈgHp<7WkOWu:]ttt|||FFl.**z~ sS\pDQ9QP~>y/*PqSII {|,H rzRP#= }^bd<5 s)>>>;DT ,H rzR $ RPP`2Ҟx Oqq ;$" 7ŎJd'RG}U5M:TP{ypzDQ9QP~0?ԯRڌϦBPj+P=l8@C"y^T(?ȑdHdƃ WTjĮ@5 ;$" 7ŎJ޸Ô֣E&ҍo]o0P;< @@5 ;$" 7ŎJL1 F{VϦBP/q*80敄Ԟ@e^G#Dw(;*O2_HbIA/+Al*Us@ JaCEGG'ԞNwYFGlR-bGB @kg.}'j͆uɳQڹ.Cqqq3!!'ֵS,#2u"kOZ?7TG|6qrzR _OIgS!h_:KO CRR4=;v{mYSUj͞ s77y5Z.\O,ŎJ|IgS!hCIIIANJ7lɡf'pGan>{|94c9ݦ@-?+8ɷ8*XHi P#y(;*O5d0[:Oy6fȣ/.࿬V+jOrC+EZ8|mH8rfC9,C6-j obGB FvN+eM|rf9A ܔ"s{ 0> Pkؔd=OMK+E9QP~ܱ!t@yĮkMHPNco _uobqKlo{đ6&W)yoK@%yY;]< @3`q)/)D!,v?>hǖqvAے:uh[|'>P~+P)@ JitmIq G%'4aIBPg /)|O j9'ێHI Im=M-wtO,gjͣ{H\Wn5MRGZMMF;޵cͳkRhe'&zch4Vf OMj׵S,#{Q18Z(?T&ߢGLL qYYYyYh0`eP*yQ}^ /$f^'~gVPjO&X,EEE(ygZ%^u\FeC z*P|N0͔j_WϾOJev"wMyCg0rss P&ŎJ[:_RQ{8N\~rT|9) x}x-Ԩz ;KoG?z݆hXC9&ŎJ[(?f<5 s)>>>ユl'R~ S> d@~9~\np:5on'htb"VtTgp5GD9QP~pKAAȴ'ި%=9>mz'RoE_=} uxtNE#`E7jpΜWN}6,$aN==>x1&-[R3 !>dynC*3Btş+D(ADn;,̓ !i{Í e0~ڿROgݽ}V|!$w^3fLqOлwZLl) 9s1<n6ʕ |2duنƪ'ӑNP~BaT\W5]J Hs1fh1AJ짛7xx͞=4-*K;;[۷oy^;s&g }Fz7bl [N'?sR\e bpVo:{,(pjѢx3}UUUy10 wIKK9jZA#";A 7 WR_TQQrUIfywqqJmm6] uѝ;ݻ׮`([X)d0`;cMe!4mҵh?5ts4L߫$??'A,jÝQ|O, Ny45Qo  Fn~7!CٲtI˨ ,z'dfD:QBA A e8`Y3_pqo 5wխ_C{Tu%taFWCމSH'?W(h?!Hw@"z5UbTrjΒ$AJ(x}GfkǾC{fۏ FAş+Q; ALxOҷw(-8UЁ^pwgBT3^־vc)H'?W(h?!Hw@"zO(3Dc^rpx'SNP~BF!DiPf8P@3fǎdyժU/JգGKz{{{xx&;\_n?4'Q͢S& LKKgwaD:QBA AAPjժGy 3==222~g''ҟ~̙3V^rVL }&UAuj֫T B "vY]Mş+Q; Ax@ %9PVU*> Ĵnzf=0`@~vvZx}GMim=A%u!@w/W "((HRi4{0jRUU(V:(Q BA AA=D1jsٲe ܹ%!!AS˗!lnnn~~~wid+b}6`zDY@5է}s7aaJ;Yh &^M*/_YQ eUyQ,(Cs4 q$ '-{t)n9t1qLl^}?S RSTAr:P111eee Y{nNw|||X'5wiO4 r路$gBT~v~7q'CㇿQIII999Ar ӯ&2?PˊtEBA AAFSmm6Ng)%eGtc,ϙxFY"g΄3 t%zev߿ߥK3g2|`pwwܹgxxxnnw~ Sk)//?q"vފ}È<./lW /lXBgISа6o̘vAlZ&؝VW\M c^aԉ7`re 'iHA@nn.Ǐ7,P3ܼ(# O( ',YԺ̙SA.{nHNݵmM][UurP2{tvnMsP7nNLܺ` ;9&}t #tD($m֭$i hkj0ە=>@tOOw8%KT?9+KO/!+=r&b?o?=5#fCO+c/Ŧd¥PP84R Vqsrr4MtttDD%IX&"Œ5  M M ] 7N$婙zpb$Euo͋"!P~BF!DaaŲvX%1q+@׽{gi?" iVsssM6Oty}1l۶ʕ$ngy:T:v Ʋ/:ujOʟ;ekk[T kgg >}¾}xbK/޵39+KMsrrxiii$ɁMa":i;C@cBB24<'ЫUnW{L1E?W(h?!Hw@"pbX uӹv]ֿroˬY/1…o:::ta޼׈DǞ9ݥ_II_2aSsKe⾈ 4zuurr:uT߾'MzXy:j҇J0bfل/"W g׮MbZv,/׿sjƏy:ʲW[ #{FcxOj'A䆡'I2!a1r2Č *pa&>ܨ?W(h?!Hw@"pG<좢d& ۷WUd=u^vUhEEd_u_"֌,ѻN$uީ+WRtNчm?:s&֭cwffq3T*b;CV_A$NDVF37# O";%2= sAD!GM/ȁv!Ot٢'OFOo.مAĆvP8NP#""Ap"DD '1߮~_QQcXC*fvղ%kCB}\`ZVXDgWK  QTB:o@  0ş+|xO/GĒ:P&""BTŒ0-+ag8=$SNS7GnEDDd׉҉ Ob>خXƁj=˿i9bŗ $p #{TȭH:Q:QBA AED0 {@iz?jڴC0-ش!{N8azOub߀JA `^'J'?W(h?ɚg|B5FI˟Y("D4KJJdO"Xzݻ/_o߾^^^۷o'۶mի?W$p #{TȭHZ"^5\DVΨi(\$_N/ؤj& >GAúOt666<@VVСC!c׮]eeesΝ8q6p! [} 8D*/_YҝPY儎2 O2xO?F5F!| ,S(AD~̄nݺwwZX͵q6Ľ>r/ 'JV'B:Uu}SD '9B |u(D'$:P4N)77ީS'R󶶶nFP#""Ap"jƦ3k ;ě~ )s@ %Yjy A`TYY⒒R[[lٲQFlFP#""0DdH]Mmdāb:1Em2(7 O'E?tƌcjժŋÂJѣGIҥK=<<"""XAdrbbbI֭[ڷoߵk״4&17GnED"< YY]~c?EVl'(]"?W(h?P-B<ԪUy䑺LOO77>IOQ̙3 AI߿zALoX@nED"T,0L>te u6È n7̻5EP~ =Z9PVU*> Ĵnzf=0`@~6^Q @7u#""h300R ]0)OԻN)xS^:N0QBAIjq2@M>>[l՝;w$$$h|2$^p b @P#""[=nŀ02@:("a~I#Ɠ{6AMş+=]|:ݘ8s&t[`Ľ>r/ "kbOdQ0_Xv!D_@9(_:N0QBAʑT[ YJIѾ};:ݘ8s&t[ OVâ;>c_j"AصE5&҉ OVN8x1~׮ ޽So/(@i/< wd:99&'sBwb?ѱee7g0م=k8sц %WA7sLl):ujѣ*//,"3.CO!!۵k!-HRˈxOIs EPPRjh?!‰Sx|TA\6ʭxeł 4&҉ OnLB񹻻k<=j;f̚Y,\C.{Ot3n]둔5)?65gT&@ӫWW''ǩSG}Ҥ&}tX2`2F**26jFl^{Rh?!b  {*;ircV_&_^PPxO? CNYM~;PnJKKY7o<YļtsG<좢䚚,&UU'YOl`7{!Vʓ}B7n 6S3%z)M$S.ӠTzOJ2|AZUշ.F}2J3 c/JgYFP{Fi\+"uPSS/zzzvرSNr?u+Wv3X6ʭbT*//wtgOEEE]v Ċ HyZ>ig]/!_鷌,Kxx톃歿Wvd-ͱr/Xmڴ14 %;tO>$ SJ۵kW&YyODGg'oBz!=55I{/n?Qh?)s դ2{{rDDέ 1/?}"S|ϠqbL,:nXAW_}>Ӓx'ǍW[[K XBѩS 6ms{"Jwh* ٙ|ɐ͛7@0)[nutt!L[bE⷟L{֬Y$1j"(\$ ЁB5{{rDAje݉n8K9q1ޓ"\Vsyy9<&e6ʭbM d\QQA,'v%??`]v>}={ l'"NjӦM...S۶mה, }oDGO{ݷomv̻H'?W(h?tPW#:'GA$ܟ(C)>?gLm[}ʀ!""Xϟ 3pephd=TBB4ZLL CyGaR^xᅇzOc+j„ Cuss3~bEݺu 5 )b \f/QBAIF~4ԊU|af|ɈuNFzOubOsn]k[U_un߀O7uNrcV_~_~~WdbI Ç6lXee)S㽧O>gqqB ˁ}A1^ u5BQ]]][[cKKdZnn?A׸ շop%!?rSSD 'yA;P(T8NAۏ81c>}2`b[!hfrcV_֮]kkk۫W]~u|ׯ_۷)SjOjkag΄3 tANyQ@={gvss?- VPP puuݰaNcz֭?~#00ЦNde?UVVgԑ#G{w7ހs/76/]Mş+dnjzŒ!AkPBr": %@K  QTB:'GAxH0㹪|ߓ0<ظh+"pΝ .԰3Lޓt:̋2QSxM}M(CNqqqEE;Ubw5NP~#FP*!pK€T,Y@'!pBiig3q'GAQu{S>XV'6ʭbT_ܬ.J8l{U6Çwڵ޽ի!eڴ1={zonOm0g6N>>ss# ,WEEɰG KӧO`ݻʕAʞ=kzܿΝXQemn;hPoNd̾8PqVŤ&g ȯGړ ̺H'?W(h?ƁCEFFF _% Ȉ_3I"pZ Ğ1Fm>eST͎8vȷjuRRRNNNQQ+"pqZMAMfWu}vU^~(޽S==kjh꤇[`_iiܹN8n</eG䘚!NvFxwf:S<FqnNCkJ+$ń*oK&҉ O8P?#|OKK;0 l@'!pBPN#IoIQ(υFFF¤R%9VnE+(6`䚭{{47'&n]`͝;ݵ& *l0Orvnzl'+KEV7n8sT޽ 87Y=q"غud;Ne}jXS3bl==2RljQJ&݌t1@o'cwbD:QBAI֐Gvs~QQ|C_dŋݻw_|y߾}oNҷm֫W/ggjAWeD 'ilb} ++wСX]]k׮sN8o@DBANn_ Ze lR(wý;%XA-unsN^zN uީUYƍôTQ✜MMMֲe5 :99B ]sϞ5;eG/88~o?Dqn1ԓŋjVSWeD 'iSff&,w֍,rnn7 # =J_ݷ>yRA7  Yy.??ʒDns V6ub*8xU۶m\]]<g+ CP|ڹsOOw VY W޽3eIV$,(\ ~ͅ億b?wԉ8mqq@DCAN@oO[:vtz 0W{c$+" 90cuީ+WR7yT^~.Etg%F**2- ( T*LǼ*K'?W(h?!H0~tqqIII]l٨Q 6# h? <`6{zK '+" 9##CE{OWqM*7cTWyr 24 Nᅦݻ[:99o߾k׮iiiL8""# b4|#6wǞ~䨟|6m`THv{߸qx^$O<1(66sn3gN5g/S?5ꉶmo OA-zGonƌwfD@iƐXNR[} wؖ܁{ZU*/_Y`,\(ì 'i4^5'FL@lL:vX}srD裏߰a!\oHg5;8S ŋ1iiߒbB " S}"0>y8s!rBA Asɦa.bI@DtLYd6661133CgΜ:y3$wn_},_'^^[|d+pvuFFI8׌[a |>+WUU'=<^{m<X~j)ȭb>le@5T}$}̫rFƦןz07 LI 90 WQĂS' cļP|o##Ooe2\ӧ*f>}OwV\~ řOmرLgB "H +,CJt響HU6F?71^dlɐk- D07ˣ:'Cp_H\vbA1~hdeXez Yݻw,vrr~ ?2=dW^y[ZjE{uIƄSKAnE+L;P?*jQ:1jQ?R&okg΄ ~y󈇇JKӢ5ܠaABz֎yymСG5޺u,556XP5h?V_iX´e{TI&UyØ#nDp_:N@ A!wvua1oK^gm]OaE,ԉ =J ԩsDhޭZrtt1QVڶm[iz͚ Q7bڧ_huvmooc胄H)) ]]m0VAX 4`h?IAZ.sԭl70,Cw4ÕJehhhA,Gļ~:B'yTc^:N@ A3 L}n.b] ƦNADDovvZXA a ޾3'i#"H&kbOdQmG2 @iZ"###T *lDXI &Rs}|ԍ>/' fB@a6]8ƦNA%I)|ph?IAZ4օF؏paː.$āZmZZ!*U B5E,G¼DkK h?!Dw1]&KǛA] nˎ fGu~v&OGnEM#mh=I+\UUU^^^RRRTTgu@jPA(SH:w5Hԟ/ O|8'1 ɛ}%XAZ:s"__ai">2j"W}f=紵*4^βA{T\:s#N̘O;,(c* 9J/_JP(322+I6ʭbDu,#{mU5Q$%{}$fD:QBA iB=jZUrjβAJAIyiQ?iJɻUͱr/4[ybyG *\Y$1k#]Mş+xOG,A'w(q'GAfʇ0N̘_uŭҜgJep=j:)))''Hjj6ʭbne[n7tx̳k~ we]&mG@'/,?n2 Ќf>Bw5NP~BLCO_WRR"KN4Hyyy~~~|}FEuqz9qkyNB뵔>o[} Jtn?_iP!\/kdw5NP~BD'Ӂ"Sff&,w֍,rnn7~ֈ# C4FRJB %ka*P,Yΰ P_5ZZZ2hm"6ʭb:y[0: cD1{}nx,K&҉ O84=}?Sƍ_|;L9v,mqt)n9t1qLQE\8ީS'r?ֶq'GA@yyyQQL 999p3w!Fa)— SAĊ/ C @;@k@@X5MXAL^\IߦQ<*9ٰ!7hYG̻H'?W(h?!"РT[mccsL8xkܸaj*:W:F,h߾nL9ˁ{.m?UVV.[lԨQ3G\ĝ1bBPZYvTp`.vBmr'XALrWR$}-M]4]{S&҉ OHci{54|#|÷w*9,ϙ( ׯC'&&vޝ[nurrj߾}׮]NwrDZQpKg)2aAa9"9VnE|giLq:bxÈ=ꤤRX)yWD 'Q=n?ٳG{ܹN6gOm_t,o.n_:hѢ;th7~pXݷo=۴qAvݻw+޽sX:CbӻP*!.X0cȐRVޫWעdX# gOҥ8339:xp'nG̻H'?W(h?!vX%1q+@m۶Y򽠠[iVsss9V(Í3ZENIsQEE̮Yw׽{gi?"3bD7gy:T:S{ROz788iq-cXf/H 5 p==ŕamaKTǎ}No:]IQA.]<핟nei^{衾k~H'&҉ O4@1&ŋ1v})} ̍u`X 7O..d-[Q0ܵC". 4P %eGii/&nΝ;NN ܳg gy}1ljΜWED7tbդNE}Sj84rkXAL#cG6mȑQ?Cm8멐F7؋yz'@_|QgΜJ^)]D۶mg>H]=h1cݻ3t7҃Ӧ!)dƔ^3 j'ĉ#A $B#tޙcume=bD:QBA W8P.*Jf EڵCQtީ+WRtSDUU'YݼyR:Ԯ"Xy/VSn",ѻNad=)z;A$NMVnES0S{`Ιʉ>Æ_OT}mLfP|No.\&yû8wjX̞=ƍvwWoA$MJtŘoI1XcAcJg}W!2d >YYMOt1j"(\ Ԍ?0сjY$['84())A Af&Bn +")bdt"˗/ﷱٜ:sɓ!{wd٘lΟ:YܲS&Xî32BHf̘HG} cItnY=]xxx_,`vИkzaDyի[jż ~ΰt}mJ[Yew5NP~BġĊ(~Uqxr 8cydƔ^k/<Ut)؋OZkS*{ļts"@Y=Չ=m2{ADTDȭaV_1S CݺuDҟވj>=_@ֹsص݇Nl6665%lZAo6# 4g/t5tݘ1OmyI>~l11j"(\ @e0.¦!tuSӖ1,A$NMVnES0c<\Iuaݻ,sw ¤>H)^aWWkTqٱtmә4& HH"KKpa_2gIKG̻H'?W(h?!C;PFzOubO[ư^884rkXAL|>@c<} 0d3;h5#V) yWD 'I0tbmW-[X6d?(w5I1c HEthZ:0/T*SӖ1,A$NMVnESWv Ņ(Slհ]6Acv%&n|=bD:QBA i* (FYTX%ka@*b+zOubO[ư^884rkXAL$ۄQ(q u !MfvpfL&҉ OH8PZs\\\ddduK+dgXq}ЉЕB:-cXf/H 5 &2{xԝ+GԶԯ~Ԥf'kLw5NP~B@Z6--uq`n6bgXq}ЉЕB:-cXf/H;%2="fC7u#"'0?U Ѥf'kLw5NP~B򒒒<"[ 2a]@AA'BW Ğa "A~f>k*(b.rcV_1.y[ }'T?ϣ4'Nm7ţ<"> >畗gEc隣G̻H'?W(h?!Hɓ̙eXݹsKBB˗/CbYYի@+n2{ADDQzj30yuv!\6ʭ"~;83& }'~S{?/g]Iޓ:mh~S_$NGp=STTd;U{zļts4 z@8888;;j^^޽[ݿ/((,???&Њ[)^,?ڤ7Z'Pڄ/مs+"PӶFzKs߿ycrTu0.¦!teu1c:6i_0`jzѦA,GR&҉ O(y.]̜9IݽsΞṹKNNfY1t+5 fyp=1_:."rcV_JUUUIII^^^FFFg&i,e}c hUZVTB-'mDּ=bD:QBA A= 233 JtZ&VJMe Iyr&<|鸸m[} F]BQ.]_W1|?}a؆\N;¿ %RT(5BԤN)4sY#]Mş+D0wAz@n^UA)ya0y/ͱr/ehڤHՒ!@ްԄaΰ,vր6VjRDך +e1j"(\ 90 oV޼yJMe ٻ&-(*ȥ(P(xˮ[,*"- Q iNJCii# `,jx (DZiigIsL's>$pi8Jݬ @ˈcE/fr-na 7 Z'+||i4w:th1#CLJRT+kWU^}EMӋU 'jmIE ,غpPp72-#Zm-A~xOzYQQAӟ6-[:?ye//͚NG :&tdQjCVyZ5jWD8QP~ТK0 Y;^K4KߴQGZZUW]5 Jz$}B)ꉖcE/@@Erџ+ؒZ[2_VҬiem8j+lb^Z(?h.u1i5ytvFR#7mg0+rСK,ٹs羏Vʡ[O[h;Eie7PI+|\z.vh-Rr.pë*Q"&ŎF[exZ/~]ʝi+>qե&''l6e,EiړW bX `);OtZkR{?]#)w&pQWW7mڴWܣ}&M4nܸ*>zh{D˱ 5>_#mS6N/vT-4:XxPE7mg0ԔD{N8oM7mh9VX'kmŎvw-C~3蛶3 AyyyrrѣG[Ι3KOO'yKSh}\ry<TT-NJ6_6|M8QP~N9Spq}V8|FV0lذ=}WRSSGt=%I0`@iiDJrhhlOH۔ӋU '4~7+DߴQ9!C_#mS6N/vT-G,V6|-<jѓ+Wp.--mҥ/bjjjhIFi7oɓ%I*++s>oh9VX'kmŎ37Sj<] 7mghu#F <={Õe wuWAAA`7x_~9996px<*X `);OkO<~xa;9mhFO.`JߴQ>ݺuS֜\p?|`'%%L&bnwuuu ሖcE/@ek|Fڦl^Z(?\{3wACÞ6B i+>@ԩSmmm… 5 ]v ޱc:t`2fsaa .fA8X `);O՞&W^{ ߻o (~?>Ocƌ ~%^Oy@-NJ6_6|M8QP~]{\*f+P _nݥ^znܸQN)]|gyW_w12}V8|FVҥK~ZlYJJJ _QQAy8pD˱ 5>_#mS6N/vT-@tR{@2]r%vZ`Ekkk^~n)))LM[Z裏.l|Gsڴi(?%Zmm iqzjBkړU7oӧ|'11۷ofc\pp 2$x˫zVK/6EĉKKK/':wܽ{޽{ӟL5+7mg0ɓ')a?~|ĈSLZd2B=zh9VĨCmpZQ#a^Z(?p=Qڵog;~2t[{?z'O>|XM[ n7xWB|#F 6,//O$l2,nwݔr h9VĨ֩b+_#mgbGB y[^ݻw[Qy˛oL .87ָ?'߿OUƉGZ?佋?Hw^}u7hPߗ^Z:Pp+Pm.lzJZk}bWpQUUuw^tE6lP~V__ /kʔ)r)777w'cE/@=x R ^tK) i;;O x -ڊWXWս{{ޥ-;wܺu}MNwgMO뮢ǎxݖ'*7I}fȑB n%ˆ φh~=}|1gOn'E[}rֽ]#pKw(M[{^UZZ>ywޝ28pڴiR"N=vH֧cE/@ڱGƤ{IMJi mW DH8QP~DRz=Ygx&bd=ߩS[h_wU}Oÿ}a.gl޼jC\{ʽ[^lg >qPzx&43 \znpB͖F ړZX j;^tSm1GH8QP~AџJ+PMsx߿]O:T~]|vkOo6a3Q(O~J2t8A!m@Kmh9Vĺn(m}e @H8QP~A )1 @qh[8\Dlg >aE(J>zCH!4-NJ6_XchZt+cqzj \+P[{:w6a3Q$Zmm-zZW0|MӋU 'T%JkO& |3 D˱ yӴl]> >;@1%^#GBCjt$kjX4MӋU 'W+VP{:w6a3Q$Zm1p]uUz e;G2ݧ-m/>x;@ӵ /~Jyv61N/vT-@hR2Hل@|Fh9VĢ+rСK,ٹs羏Vʡ[O[h;Eie*k(jo^=yol~yt2T#g)wۦ>*_h;;O 9p@qֿ3<ғtJlg >I+|bK]]]jjjrrfSƂP=ie,}j)}BMVUϮژ4+/CMӋU 'f*Pmƨ=;0(b-NJ6_RWW7mڴ߯}&L0vت*6TKL]Z1Aӽ|}]}ڟ~H&Ŏ@ Ԧ/Z2?"{2j9 3yzroON$;;bVل@|Fh9VĐԔD{?ow^/m]oVs`v#nqzj(ݞd~b-H˕&zZzrj)  &rhG ޸t%x /\rj_uQ_#fn<Ek2:5ݻ0H&Ŏ/(J(6-ɚ<-&zZzr" f>@LX  |+Vs=={[O[h;E{o (--mm9 VJMB+#h|ڶ;F?$mgbGB (\.Q% d 0ғo3& |3 D˱ &ЯC RRRhѢ;7B)%\np%ٌlmf>@LX Җ.]x~zZ{v oѢE'O$vӯ{(/*DxYI/D޲52MӋU 'Q,%K\~姚jX=zضmڵ:w~tF|;HUM g1cE/@L1bDeeej;vO`;hРp8<O*V=On:obCЂVRѯNm6Ӗ2MӋU 'Q,/2!!r^veeӦM:u.((⋓빸70(b-NJ6_лwo;?pٲeݺuS֜P O}{a2,nwՁm:ʙVC;HJ&Ŏ@.ȩS>W_}ڵkK/ԥK{ }ҥKvSO:G  &rh :u m)S8QP~hiӦ:q}=x<GCCɓ'ǣG۷BO=W__%|3 D˱ &(xӾ O}{i2oZz-oWJ/G3kO[v61N/vT-Z$tA\Ys BQ'?5|AG  &rh o}Q7Q O}o-?IV)"NկX={&JF5z!O ߒqzj|7tANzȐ!FG  &rh Ck_י3gVh mhΗ_~yFF!C.^nAh(?E;O}0 |:ȑ#m`f>@LX  f[W^=wÇ'&&-ݧ-=K/$ F<3k:ihzvKW]3Lz+mӟn>GA~͛ |mKք> =\ou֬vZOr1yɡfFr]h .8]~jf\qŠݻMrD15A @# /h;EJlg >I+|bEyyyrrѣG7ڵk…OOO[O[wh"Ib㑔zLj>aٲ9*=6l;9W\1g-|RܫWyfiwmڴZxty&⸸7|YB:qG5kۻwO9l+Y1~BB ;ovܱp6;ÿ?pپo2mbfa' (?5D0: Om -7FK޹w& |3 D˱ |>۝_jСK,ٹs_~I!S駟Zr) /++$555ʧ6*~xOsNO{'PX7no~3I<Нg!qqq-/?4t#G*(xwlݺ>!ݾ}f?ᖏ?ޘ:2O>mԩSDÝsN7z茆6(?h; O-~Llg >I+|bz.tŊӧO2dH޽vС3g\rbȟkOv:Rzzҡ7BK!v݃+G33hP;}QIL<‚3|/6. }fw7h@{ѡCO=ues51cʥoB9d{|pP TA @MMOE}Eef>@LX [jjj~i%I1L~,vkOԅ:RzWy))[Z aCʿ~G'*Bo?~?WVұ@cֺN[b7F }%,-ߏpf畿ˑZTA @ 7 {[M g1cE/@ T\.WYYf唓F 'RRkR)~d O՝ڍY|MX,70(b-NJ6_X$W^v8vfBF;bI+g>Z A^?kRVVO1hV|-<h?)Jd50(b-NJ6_USS}>vNBi#hڍvMMlchj{a9(?ĪowUŒJ J/Ì|@LX "Tuuz 6R( OyeKТ>|f%AI- ۦ>B Վ?||;Ujv`ṣy?Z&U?}򐅡o6a3Q$Zm`|?_8u넙4nzca$Ifs8^W5v61N/vT- |2~ƤU>SۡAv[{>Eg)RM g1cE/ VJMB@?(%fbL&\XXt:}>jlb^Z(?Ą 95lhԾuo;%~]lg >I+| &Dg9~"h,S4}$Ifd2Y,vVmgbGB bFtkQ>,4*um~f>@LX Opָ.~C-zOK#cgI;/UmgbGB b+G˖&I .eNʆ*Pf>@LX 1-7[wqhoo!:)HcgIf5$v|f:lb^Z(?A 쵶9-\;PEbӃNg ل@|Fh9V@ Wl8g05g>V\xQgScw.{)gbGB bilL_S=kc+s}Ƌ5[70(b-NJ6_!+=b_]rkn$5,Z+/~Mځg>׊_a䕿3nh:XFbEL8QP~vWznUhy[=0o6a3Q$Zm[ qX {2,4y7WffO$I?"=z61N/vT-ޟ.յ߽Z^A;M߽^33ǒ& |3 D˱bNrW}5߲oO4?"{@Y8<=RkАZ?z,;;l6[,FGy$SZ&Ŏ][S~,E*g>,%N f>@LX H@y^M|Œk6鯀l#1?Rʀ!ѣcHG'U:t8fx0mgbGB x?+B +hyikQD8f>@LX 1|>C;Ί f5yJ+DG!I:tTLGy$SZ&Ŏ|Iܽ濅Vb=};wCG[O0ky'){RҤt8f>@LX 1M.BUWWP.~w8F%Zd =:t$U':‘d&ŎZrKha%F[ϞI=JytX ^}  &rh6#E{dR"ei+9v61N/vT-JmȐ׭[s\~CGyIWlg >I+|80jqzjaiԨ33暡&ntU:믷Җ?zH47߼9xp7xI;|EEkB_7OJ:kMuuB _0n:39¿ϊ. AZZڌ+Խ{SN7Or1y[lC t!ZmZM8QP~CԳgR ˖yWaxwɹA>m^z̛7sNO?nӦrijCD~#5ŽÉ]kđ۷tikK.?-ԽCrQG5kۻwOyf6P~]cE/VmS6N/vT-"/?-..nӦՕ9f4u/O'Ιs|Wk>a˖ih3[8xpSB{JFRn'NT^vـŋ55;y Qv_EC t!ZmZM8QP~C^Ak#)?_)}-?)vX):𡹻k<&OHhaótΝ;U~**ZCCA+vjܼym|| aOԾ~57\C t!ZmZM8QP~CP~ᇷ9wk?رweq7 xC{֫Wȑw߭~ظvOܫw)*(xsNwlݺp>{}5@X U۔ӋU '0O=;9رW%ij:L /,8giicn' ~8`/c`;shC//+lȐ. }zС}RRקO~оO rh@ԪmŎZr|µCʿ~G'*Bo?~?iҷ>pfgh(K^M?Z<΂~bCxiHfdXv뵊Qm6t)~qY]cE/VmS6N/vT- f#v>|zYh-?V3Hkɻr*pHC_Hm\k¨_jO hr'N"ZmQr ?mR#a^Z(?A8o}ڟ_95:Pͷ#{7^0ܬ&$nVJ*//@ܢqqcE/@|0KP]"mgbGB bzY&%*6gj{;"l;~mx8AZV=;C_H'_m]mgbGB bIt:KO9yXda/x$In=ɇ5^\~S_{a)}#-NJ6_z[iM)~eO[hr6G8QP~@\2f1?Ċ-gr><=d_L/C.9llX񡣤=t|i7FNЕcE/@T ?ڮ+NmqzjG@y^p8v{aaa~~b5999ټXA re#/͚NG :&tdQRԞd<|D|̤ JD˱ L AirH8QP~TSS}>qNnl|^ӂx^K@G :>tBkO~!9\jʀML+|q?=~L8QP~&u(t:G/[2A rKG4_5=Puc$tN[@LX m3ti@ѭi;;OF^"Z{rh}!)V8|Fh9VDܶnQj[.:.v61N/vT-ZD/;(@LX A(ҭ2vi;;O-ҝu &rh`(ҭ2vi;;O-ҝu &rhÑvUWM1Rtv*;-&Ŏ@ 9tx%>I+|+:t%KvܹUrNQG٭ v61N/vT-ZD@|Fh9V諮.55599f)cA(JО2&h;;O-g >I+|tTWW7mڴ߯}hω'VUU|>Um|&Ŏ@ 9(b-NJ6_D&ړ1czmlb^Z(? 3n?< &rhKyyyrrѣG7ӖhZry<6Sv61N/vT-ZD@|Fh9V袡aذa{_jܹsGtv^^ziiil3(mgbGB E]q g1cE/.ǐ!C 3fرc-;_~vryH.ep&Ŏ@ 9(b-NJ6_]-]4O?7o^hIF)J7|$IeeenMӋU 'wAr3Q$Zm1bDeeeu,;(؟^r%9996pxI+|tj0aO`{geeeggWTTx<@4i;;O-g >I+|t?ӌo(ѢEӦMC8QP~P_. G  &rhЅ2dH𖒒%KtYQ{-;_r%(?;O}0 P]Mlg >I+|t0l0R___RR3}[O[h;E{ZHqzjZA " ^Ur6sEJlg >I+|R^^|/((LOO[;B_p .$ 7N/vT-(: ,w_F[*g+w50}  &rhQjj ꔁ0hϱcN2jZ,d6 Ng"mgbGB @C밠˸kIGZF}ͭ& |3 D˱@Gr9(,g̘1Æ ˓$l6L&bnwuu\WY&ŎFi)n8Z{ݥ& |3 D˱@_UUU~{>}L&2$++2e\{ c&v61N/vT-4r Ors]#)w26}  &rhЗs+Wׯ~ኊ BNtud.\jʵ&tӓ(:h;;O[Fɵo٘t} ]t\o6a3Q$Zm^rO<ݻwہN6-##CjbXɵ'N{ֳqzj]姭& |3 D˱@_555tvIrrrL?$ lu=tJ8QP~Ю#iAغLlg >I+|t@\2&)''';=h7ڹԞNi=;O-RpxZݬ }  &rhzn텅6-?=h7ڹԞNi=;O-O҂=9e M g1cE/@~xnt8A!m@mtJ8QP~PdZZUW]5 Zz$}B)`Tf>@LX Ury^OzH) O2mgbGB @+rСK,ٹs羏Vʡ[O[h;Eie7C70(b-NJ6_mgbGB Ruuu6M BQڇ170(b-NJ6_mgbGB "uuuӦMKIIXgҤIƍ2& |3 D˱A8QP~HjjjJJJoh='Nxw& |3 D˱A8QP~8G<111..n?___j0to6a3Q$Zm &Ŏi8!COf͚1cFǎmy衇zJNNNOO.Fr(n& |3 D˱A8QP~8K]6555$K,ٿ`yM/p޼yݺuS֜$dXvD[ل@|Fh9V@4h;;OѩSÅ &$$(kNAuacǎud2B\j]f>@LX Ѡlb^Z(?O ,`iܸq~ڻw^o ں& |3 D˱A8QP~8 ŵ-[ƾʕ+ydeeeggWTTx<@uM g1cE/DqzjpoO{p|7tիWٳM &rhhv61N/vT-Np 2$x$Ig@n/rIII`ϓ'O/##'@LX Ѡlb^Z(?FCCðal6[`K}}}QQŋos9'!!a̘1˗/_zuII E{ҖK &rhhv61N/vT-N<99ѣ_PPNt?3w… %I¥9 g1cE/Dqzjɓ')aÇ2ejX,&l6:k]f>@LX Ѡlb^Z(?Dnܸq7pWB|Ç6lX^^$Ifd2Y,vZل@|Fh9V@4h;;O;훟ɓk֬9L"מrss?yx")`o6a3Q$Zm &Ŏ@|>^r 裏[_|ECC޽{KKKxo…VU=e5eeeԝFԭDlg >I+| MӋU 'H~rJKK'OܿݻM!5X,gړn8o}:w6a3Q$Zm &Ŏ@jjj=$~&IH;ȵ'BqtJlg >I+| MӋU '(UVVfw9dBF;tJlg >I+| MӋU 'u unl 6Rvhg֞NM g1cE/DqzjZMM|v;NQF l)  &rhhv61N/vT-4P>zCH!df>@LX Ѡlb^Z(?g IDATKlg >I+| MӋU 'qM g1cE/Dqzj .}  &rhhv61N/vT-ĥo6a3Q$Zm &Ŏ& |3 D˱A8QP~ل@|Fh9V@4h;;O70(b-NJ6_mgbGB @\f>@LX Ѡlb^Z(?Klg >I+|9Ւd- 444MP.-4ыU 'qM g1cE/@8hhh-iyʥŤ^Z(?Klg >I+|x倆,AKI9(QP~ل@|Fh9Vu^Z(?Klg >I+|ZOۺF/vT-ĥo6a3Q$Zm`-mE;O70(b-NJ6_pӶыU 'qM g1cE/@8X i[wŎ& |3 D˱ hbGB @\f>@LX ]4zj .}  &rhk?m.QP~ل@|Fh9Vu^Z(?Klg >I+|ZOۺF/vT-ĥo6a3Q$Zm] QuhbGB @\f>@LX @w(ˮAŎ@wpjWVf5),ݡ޾5mg1cE/@# ^ tK)vF/vT-ZDtSJop &rhPxk %X 6t*wi;E;O-+!Wprl;'?ao (b-NJ6_}n]c5yﺶnI#V zvF/vT-ZDV9J|Fh9Velim`;k\~ћݬ @9(QP~h}d[(@LX Ŵ m&]#)w(vF/vT-ZDV9J|Fh9V:q?RjθC9(QP~h}d[jUpͱmhhk%מ w> @th;E;O-lt?N[x: EiC,4ǶmY o*?5~ .:sP4zj".ȶ*zGiG͔+FeWhercmwCka)DsP4zj".ȶ*zG@6z板_zG-tI216_pޚ'Z < @h;E;O-lwL+PQmhLnL mh-lQeF9(QP~h}d[գE/IMW%pmxK+w H4kLZE'mrmAŎ@ ۪cYF%@-8 HkO{>аgtTvF/vT-T;~:p?tAGAzm#)7+ڙhD/BpIn;$m:k뭮Qh;E;O}0 P]i+|+ڙhD/@ړܜxPmhbGB @# /I7m/@ߩ P:].ڙhD/@@ړܶEKz (vF/vT-:~vAq چc*g+wV/@?Fqqq"|dGQ*mhbGB @AYs5y =k69n]Aլ k9 88drCm"=i@HsP4zjKFm?{cRӵ7tti+Q(o2-'hUwf.[vF/vT-4zh)uRӵK/}I47m222-'h=U{ĿwuFw4ыU 'hqpM[E|]e;fr>mIh՞ZT.n:aCkW*&mhbGB @snkOKEM[@r < ͱm /@Ԓړ Tј{+~MNsnkB>K MUۻZttr2}Ŏv o*?5~OߴQım#|_ci;Eٵ9Dp`oz[hQIUECCӱy)-3;O5]vޤA}V8|FǁmR"+P9V~ES@{lZ BJ6_|5rccu3#444U`Z;j?Ŏ@ӂ=9e蛶3 8 ϝ$W9V=v*;!88ZRkOeeeITeKg6"5mimzވu3;O-R1ZozT p -zIjz#Î|mWڶBVkO֩fd-..v\>zϿ)WK.2K mg.v/vT-M[@(jp|GҖ6|q) sAIBO555 U̯wIj2mg.v/vT-4BO{.EAߴQD֍K񿶶Zj+O[;98hnDX =f5i]Y=OP{7ؽQP~"$7?ӖyjIWpp m_t\s 8һRO[ =D@σ߄Z3;O֞=]}V8|FM{=}q) 5 jOŎ:jOr{7-ξM[@@ K\t\s ~k=m '#vbbGB @vIn3o (jyǭ~/:.9Į@`p\^Z(?D*ړ^/Ǩo (bz},ʱW2Fb WB ؽQP~H'U3#i+>e&ʱ/3)m) TI4Kb`X\^Z(?i+PG{WncZφo (b:\rW[U(S@d `n$P{0&mg.v/vT-NC[In(Ng/xk~ k1i+>I+|rh-=*''';;n- jOŎKKjOr+PEc LuX?__1{ђnmj{t+]#-NJ6_Prjgjn`@\^Z(?u`ۖTsM7()~ W@D˱ Xm{jmh%E^ Ԟ$I -?eTw/nsnkILD˱ Xm{j۽u֣VkOYM~YY=OP{7Z&G&Zm`- m݋U 'x<=i@֞o}—FcE/@8X BjebGB "ՑTP{jE&G&Zm`- m݋U 'ԜS79@0rhk Vh[^Z(?D]B h9Vٻ&r_(].AZ*. *(PѵGjnR 0pU?hSƍLN&v>+'L%0Bh +`?A)"{Gjz,k9@dRV- 6XO@p ԪߔJII!{:-9X/ Պlߋ_1MD }3W^ \[`WUogUn b蛹 6XO@H_!{:--9X/VqЛI̶pq 雹 6XO@Hjꇤ)(N:D8}3W^ 1[5~(lt*~Iǁo +`?!1v@"<>ff_&톋wޟ+D}3W^ 1[55x\1N^\ 蛹 6XO@Hjɺca*W0f c$Bsck6e1r%nD@$7sxm;  X/&eOۦΨڶmS$P@7sxm;  X/ Ϟu d̈́n񔗗+8\{^,' $Hl5l ȩ'y]KKKIHo +`?!1v@"<>ff_I@ i\{^,' $Hl5l HgO$P@$7sxm;  X/I_UqՊc<o +`?!1v@"<>ff_@{TYx촬M@雹 6XO@Hj&lTkUqc*P\{^,' $Hl5l 3ӗ=IM@yohfCmуӚɊq }3W^ 1[5´Bɞ&%PW>3b5"tHfaRhhuv-/rm.`?!1v@"<>ff_ޜb:[աRP-QRDN{+}%U9 <^,' hPH~tQ?ƚmaZۦ.-U)cvp[ҿӚ h3LKX#Vm_1~}@9 <^,' h_@ŀ6E28VcͶ0P(){Jo VnꑓҚ_yO8"Y.`?A8||@bX:alqșƚmar(){bONNs:eeeG}.cOݕ % b ǚkh=LC߀ML%QnblqșƚmT˞V:~*//nֳ~-Hx 6XO۟|h`wťZO-0[߭s)7E]18VcͶʹeJ=%'''"}N܎<~T~"(|xm=~5<s-Lo>4P-9X/PZZp82o|J=>$nA܎QO"V.`?:}8A1?z-mʍP-9X/x$PdO@x 6XONPvp3[5&PdO@x 6XO~FISN1t38VcͶ/%PdO@x 6XO~' ??U'?݌-9X/ )"{]"~;ub@u\:glqșƚmuX7RRRȞ&|xm%HnrOWuS'58VcͶ<:A3>>^ Ȟ$|xmmSXe'ۦjj⥢| gk| ԚeIb,Z(= F.`?A+){:k4_ۻ2qUmErZ(c#@l5l I VK +_|#333--MdO@x 6XODʞM}F4&^*u@[rff_Ҝ5b,[^qqqQQt:ɞ#|xmP;'<]l޿kzA18VcͶ@M.cSk~yk jAD6=R@D$PGj42~S !{$P#FX|ty޼yӧORRR;^zXBZOw5666--/c#@l5l Ԅ4lx"~BȞhM<77o_NVcǎ֭ڵkp8-Z:t/ھ}ѣG^^*Gj chkEȞh6 7|t:/"$''yNi۶mFFFEEE޽{왚|[rff_&a < _͙3Qw{ Oo{+_0cgkz~cN8qȐ![onzժUS#>|k׮ݬY/Tc#@l5l Ԅ4lx"~RfO[ES/W۟^^S߅7>$M*p:nn]Po:iڴi˖->,^{b;TUU??{nj֬YC QX3[55a, [1xm;=֮]?NP/W`]hH۶-ر!OU꧟~ܹ |䘘N:uf}']vٳիe/U?-9X/PXgF5kU}o Senݺ<=SjBw^^LibOS>b.rvѣK[>]vmg(>-QQQ;wnﻯoU)2˗.g ]"5vȒ.ڌ}[|YY22}~V- '}x͚;̹ 7&Z;x <*^|qOiޣ߅OUM?Ւ=e:FX{։Rxw)_[rff_&?Mi4Zn?Ӯ!~7X>6u^hR0x%}yKO'}-/E={-/yb3O~X/pUk֬7}[nn׻>-mQǎ1'On߅(/E 6o~oƌ^~E:~66݊*~ƌ*zs S-.9PlVT6mZ۷:#&^&}{~jD>V]_Gjѯc7@^[qՌ mS_Vi:/7)bز%Eyf&m/^fLLSgk}o[^w{ /emW\u+Ƃr0cg'w Sf.`fV'h"Ov{ZZZrrrRRRYzA HS +LO<ċDT &]NGFX[/r:EEEyyyigis_V0=/R/`_tU:]Qcsbffl ~Bv?q:EwĀ`'^E"^*lJ @#j,`N}̌ONyyx`fV' $~㧒qyʕ]t.TVV˟|I&M{mNGFX[>qyժURd:,i;v4nx߾}Ј c03c+-ѣG[n]XXXYY9saÆnt:4 @o$\:4 @>z@B7.9133?AjEo* )QtthD̉ ZAofHi쏲:k<NGFX[= .'bӚ -wrS袣Ј c03c+Rìvť ~M[A}Υt:4 @vEx`޹ݡzt:4 @kV!vYA/NGFX[֨:x-հt@:*.9133?_x?9鸱tT:]Qcsbffl ~N@.Z97pqct:4qlwY=k4l  ' $i͇+NGFX<(߷W:]Qc8͌)w?PP~$g;ٟ Reʼn=Kz멯v}+TЈ ʚkNoq$v ??w81 r:,ﱝqweիꦉ}߹tə11)p߁MʣVt:4rhg*.z)9P[ぜ0cg''?~w_v[3߫Fʟ\O|2JWtT:]Qc0=(01Ӛ -?+7Р;?/vó:؛_hR;YF|p#{+e}Ј ͧ3Qol=uUogUnA3v'~Bphˎv?䖟.Rg.4E۱t(v=O}LtT:]Qc8Mkk$Zfk88`6ON>y̪cUG-4dbg%JG5088`BO>d!W:dNVl9Lz\(NGFXmE:? ;?!ٵGL{ۦP',mySKGKtT:]QcslWyI ;?!{ [c?R+Ė3xn;N֛ot:4;qȑүѨpUa8tDy|[Cg'D:{^{:[iiVr՗@t:4Fٗos4CZNK'uue A?"F?@д~ϲ(NGFX t 7 O ̘ᎻFrۻ2qUar:VUp\ki'~V>dO߈;MuBv0ar<(G$NGFX Ux] U=$VUfHۿ5z^3 Oh}azjm֬7m0UħX,lQVV<IG5Эr-ȓ'+6߰ o{s2OosVKxz (bO&'z<3w窗CW?M$}v+rDQtthDtd21|v-VUuZ_]+ׯ߀"?"S~-^^OI3$&&*rDQtthD9PsOߧݎ=LZkK>I`;?!i Ò%:ѣ.+Uw߭K{&M;vի߯?|K;`@8+V̺kbcM0-;Uo6MV={(T'^۟qc/Z,[ly-#%~mOCPc=V_~j\yAYq8$P@4OgۤI7o~O\߿ĉnܘt%^ziXygw&ˉ,ĴXW߈tuڴ?v~T%ȑׯ uu/)~R?Hf~ ѽi&R(&ncǘ'v).߾~X@}+:mzIk9oƓ@2v'~BD?=={ޏYZR4acwWsMF'!]>쎯oUM|Dia>}uwߝ[Ӄ%]g..omw/hOCPc.ȊCldm<0vt^WTٟ M{;Y^JmKZ'YWw7~Rlb,q׾/uW^+z -*ۭuR$ʕ^mڴZ|fU;? AtrIwh:n;Vr(@ cg'D4ݾ,**j˖6ݺu{B8=%qYۢE38qן~&->z^~ʕ4jH]}|hGlx;ڽ{ ? Au1jv.N &+^%h~@Z;?!鈟~.];/;zt/6lwNޣǹ&ݾ}mȐ~QQQFO~o9t8+ ͛fd,)S mGlXfݻ~ԈpQ;u;M\$b].Q>aT4Og^lܘԫWF5ktKV11m۴iZhٲ7"ڽo_ ݾS⮛4~챻Կ~;**on

5  _rxɷIǎz&c[eֽ{ 7WAN1o_)xZx#͵OPPc`ioTRi&lIIIn bO߈;_vk_XJ5VONgYOKׯe3ԗmn~!8N6Y\:~zz7t:4? 7|f̘11m~/\9t;̝s%ڷ?H?aC|^]e?o!) MedT?|K<`@8-M{&M=-^qjb}FZNl k+ڶm-6:Eܲ|~-r7'@cg'D:{^{zii<~DBBBkuT:]Qc`i<3Y=ȫNܹC\sLlڴg?TP%RN$ԩS>|iݺu'tSҗjMMĴO^~T_|w{7mܙ|LxƍI\K<`j;A˖."6Rlck׮闣4'@cg'DYatlGꉄUؒq0#~ Kc4gDq|STT?%.m.^x!ZjQP|S۬)~ K[~wyϞ-+)I0aرWIzwbK)m,:6@qˢcllo;qjFcOHwd[mS7mYیOQc`i矷DEE&.uud#0MKֺuK_#ZB<у7Um?%o~)>Yq zuСJzw D{:vQʕڴi|LiIM ٟ Gٚ :S%Ɋ9`'kjj|||NGFX XO+WҨQ#eg ۬Yӭ[gm͂jzTݺu{Bqn_&naXzw"ڑ#}Nx-P܅?;?8qHNWvhZtB6=DGf.JIIX,Ly|#J @#j,'yf;t8cʔ^>xWT8njڻ?ߦܣǹ&ݾ}mȐ~baSMjذw9-urرW}Eѣ}9Tw'6k^'w^7xZq/~$#֬YxnA*?C[v?קeKql7NZ%>>>%%n\.ǣ<WV:6 ~ A%~:F=o@\v^~z}v{/ͭZj6JKsŖ|عsJ~SoS\~ZhٲĴMN}Tb57&յQF͚5<Fj3ό):q[쳌شivf=(FcOzc%7ՙMPO*4|FXkjjrrwbz#z7fWZ*Nz3?tӣRpj-((p\+lX7vڶKˊJ'mꏱ Gfw 6ZM~Лx,E?A1v'~Bzn&ƜM?k9-ﱝq7/a{W&WMs|$ݰ=um"*eO9Nq"O.8/tb,*; Gu1j4uB翶g_`m40?A1v'~BQ^^xTp8Sz}iWg.##XhZSRR=%$$yyy∉&^$I:YeTtR$glqQcrc: qʿX\/?A1v'~B}"OvjMJJ_$IqK$췤@x4wةU\ Gtp> +?G;]ZoBٟ /r:҇}\".Gu ikhWCA qLđGz=rARkbIK-9j,þq[Tg":kӭ'?A2v'~B#%Pnrl1>XW,O q=].8hc"8>(գIn촴W'q֓K-9j,GU_cɊXV+=6!/=h&YdP1v'~BT^^zJKK].(**yyybRI+_cܗ+_bA qLđG=['[rX@ȓdvlmc1m&[,Y[Ocg'cRx|9IqqqQ]`;b@~m:$Ww_$I} $ Ӛ 6-'[rX@O/gg?WdkuWVA\Q3 j, v%Q 39zdX#KعdޕUar:]+oڙ#aKIeO9ND'@#cg' $H|6?^+WԌ‡ BRÑ={IaoBliG؆6e՚")!!A\_<Wٟ >5!'PvdNX8}~ҌO/|Sf.^lrgo0]<5n$)rJNNX,'=–=:;?!1v@Gǁ5/r:'tD"(B$X ~ҌG~ !V^qš ٟ >:.!v\bݞNRRRb$<(WDqQLqHŁWdq'{t0v'~Bb쀄 @#j,`r[VVVZZrGQQnL$_mcܗ+"8t(8+8–=:;?!1v@Gǁ50By<_t:GqqqQ$`;bm#80R'qx P;?!1v@Gǁ5yvK#VKےs+"8tDN; Ј c03c+c<)_ V:4 @پXۦKG50'>`fV' hYGJP> 0JUv@tT:]Qcsbffl ~(oF8 ȲfCKt:4 @QZ:\+.U kk2xw\MJ @#j,`N}̌ONi͇vu1 Ӣey;A/NGFX[6yE[}ʍJ @#j,`N}̌ON?~ڨ:xS!Y1#8鸁tT:]Qcsbffl ~[&)~ʿvN:n,NGFX[O@~*~<KG50cfOmsqZTUU_~+V< ǏpUV-znݺ;t:4&T}ό&dOԢ_~%K.{5k{ZZhѪUwmΜ9vtn-/*.9e:x+?P]ݸqԩSٓ]v/ oҤI_j-((p\eee|J NGFXt,b f$17zU4'@ٟ _^RR:}(c'iӦedd.R>J @#j,PmɌWߓ_}H Zs9eeeO<g̜d._~ٷ_~b].EMtT:]Qc:^7vqi͆,7weիꦉ}l#EmˤE'Q2aOԢy3gΌVfN2ZYM6X,Cf&:*.@|akvbᏫѤvlGۦΰ5C[v(00v'~j짟z)I~}v֢&:*.@UV(oh\zuB+oeZ_Ւ+ޢ{^N ?P ŹMO=OݻwOHHHLLLKK+***--EMtT:]Qcp+w@: s5 &O@-'_2k֬{G@%g^j|=z̝;)X:*.@x?wۚ xduBME)s( \ZTUU?33ӷrҷbbb!C<쳫Vk}[&&&vj?KG5uϊkkߛid^Vo@`OԮgϞ?|᧟~/Ξ={Μ9={'Ν ҥ̙3V+J @#j,&kEx/L.ѹvt^Wy|ٟ dʔ)cƌ9yrE ĖW]u 7`RRR-KvvG5QtthDҚ 8:^*t>|j-((p\m6 dOhr뮻n̘1ZSEl3lذbǧv1/z<et:4aP+4mG;}ɠT{u;?Zرc„ ={J-==λ)99Y;&EtT:]Qcp8yhg/VhZθŢ4/[`OhUVV&%K\tE7oަMJΝ+Vu}̙6MʞN#QtthD/#myS6kփ6Yv0aNs?uN3??g.N:EEE}7nܹsH߹'N,NGFX <뷋0Ev晱;Wٟ Ъ񔖖:nZ<_ҧ$b){`Ј 4`[~-^^ǭ'qL 111--HVez;?A%PN 33SSRRR*Ubؘ)X:*.@8h Ò%:ѣ.+Uw߭K{&M;vի߯?|K;`@8+V̺kbcM0-;Uo6MV={(T'^۟qc/Z,[ly-#%~mO@X;?(rv{vvvfff*Ubؘ)X:*.@8h<3I'oڿoݸ1Kz4/>?qM'6GG7Y*ik<i6**jڷީ|K?#_s="^R~͞PA{ӦMPLfǎ1qqOڕS\/i3}6' {޲21\.Q\\\$#b@l&6&{ J @#j,gy@gQQQ99KKJ&L?vUnݺtrMF'!]>쎯oUM|Dia>}uwߝ[Ӄ%]g..omw/hO@X;?:I!)++sݥ2X(V<馣Ј wB%HzuСJ˵O2ɯvo`ŊY}_민{WA.Z4Ut۷[-I+_ڴi|̪wo#~ٟ @$QtthDAGd/ڲEcyݺu{B8=%qYۢE38qן~&->z^~ʕ4jH]}|hGlx;ڽ{ ?aeO t:4#~Ǐt9kث"эfذw9Z:AxNt!CEEE?SSۿȑ %Ǐ|mt]د'i2UVnݻ|z.]Qᬵ0:~z' D"NGFX ~1Io;u8/,%~ٟ @$QtthD kAдKWOoE?HЈ k:Xii;&u6!! cg'HG5YatlGlUؒ{m߈01v'~tT:]Qc09k-zжOZ涼)mB' LD"NGFX |>d!W:aNVl^}lZbۭ<2v'~tT:]Qc9qHN1+pl:gm['?YѧRRR-Kvv(++S_z;?D:*.@Xڲ#柾.RG-4EWw'[Vb].Q\z;?D:*.@m䦵: &>Du4/d7JKK^ٟ @$QtthD­lՙ,ښ_:|1sy;͕VgO7=*eO Xւ%ayyٟ @$QtthDM9~H1r:,ﱝq7/a{W&WMsb]d\S(=cO t:4u\.QTTd򲳳333Jߢ6e:$WwqqGCqdG cg'HG5{Rx|9t8Eu%+_b}8&O@0v'~tT:]QcKEҺՒ+\:$ 8-D"NGFX[D"NGFX[ݽoRk٪ bd l⨊J/[C#@ c03c+)%<538䨱9133?!)[AJIĘoGGfG50'>`fV' $H‡ c03c+c$BsC̉  @P\@Pcsbffl ~BbD(x. |9133?!1v@"<>X[]VHZ%s0 50'>`fV' h_@ŀ6E28䨱y_$  ' hKP> wťfu%*;N[rXm@[ֹ[rXT>5Y__}d ?S#*1 E+Ew(7B28䨱|WZ[?s߶E5bOmSIя튳*7B28䨱dw^W[]e{r# ?SA-z=u{-߄?-9j,`6=ョh_;?~i>.z"߄?-9j,`6i͆?5n8 ?_  y𧝱 GLQ)X0!cg' $m+P-9j,`Bo5TvVlu['/V@38䨱9e^[I+W0cg' $?&T@38䨱99xjLٟ ob43J6ur5ꖱ G̣+Fw%=eD,k4PONRmꌪm/ iglqQc(,,.ymڴiv,JeD,k6n"cg'@y$5)Z3ax<>3c#@ 4x'O2eJϞ=333dZRl\a1v'~Ξ wM/--%{G5hN<9nܸ1cx^:ѣ cǎ2q@dOI@ T38䨱@6eʔ1ch@rԨQ].*cg' ' XaaaϞ=O>g̙3k֬_}Ϟ= … N'{ٟ JK/8jE}  UUUU{\xi %3fHOOk}[fdd#??ph| 1v'~4ў=iO~>5'eueԐ63c#@ 4T/Yx=ӬYK~l^x9svtZN0v'~jlTkuxlk2h`FaFg38䨱@C?s.kڴiI"Ϟ=[-3g^V@-++߮#cg'')?|~|9cQ9kQG5h.~I檫JKKm/v=)))33XP@aOJ$5)ʹ⾼Fb*4͐kU}" d@8s|Wy38C9Ɉ .m/o>>>>%%n\. ?P9b}pP)qֵԉVJ7"G+**|WgϞ̜dg}ַ۴ixŒp8azٟ d%>}S-3sKowMFaAI?fT1i}<o@Dŧf͚O111-mwn۷@fO"iouKHHx7}-Owx[̶5c߀s?꫁t뭷fddw8=zuҊJKK}kkO@%Pk&<^)X,yyyNȾv-M}W5c߀_۽{k͛ _{'ǍG4HO&5=ʗ=YVu=ZaQCp0 (Q\\ܯ_?6qDuռyyZJq^ΝK4HO&.+)(yp;q;IRqՊc@2 (QU |pB;jr(ТUTV@KDQ@EnUےlBBn$B@CU *@%Ls&0_W^33gfg;s.k6%%EmFFFFDD[z.)nt/rd՟ hqO7JΞԷ>D? 3[.<!&w؍W'sX;ܫ?`OHvc՟ 0'$1Obkzݘ{'~ 1 5qn̽?`7^C}B`M\sO!>!&w؍W'sX;ܫ?`OHvc՟ 0'$1Obkzݘ{'~ 1 5qn̽?`7^C}B`M\sO!>!&w۟mJ̽?ޙkx:_;0Obkzg"%{Zs]$P+3Obkzg4)~@,ܫ?`OH)<'jEVTTTUUi1Obkzg=y&P9O/))!XW'sX;j˞<5=MsO!>!&wFԝ=@ܫ?`OHDyŗR3jѐɻJ?ܫ?`OH'qVu5fkTóF%Perz g;^C}B`M\4ġHp8&+;ܴ=fO&PG\;&.c倩R'IqqqQQQ###Cb[WAl7MIh7 U??`7⺿4ow}Ǟc&R')OUUUJbbb4O!Oʉ/IV\\:~J(pD|N?)>=5??ڔ'\o%gO[D M@3BBjSQQѐ C@mMȞv@BPwE ''P('}??z TzgǓ=`hjeaDEE)??R7bCB䫱111g||<??Stq?RRRl'#~ !~RUUU+6"//ogaa!??ڟS >rf LjCaZ ~(@cO!Ojsaԅl?p@=.(2vC#ُ{v?Ihp'nhxfOJ) Tja vC#Or@tcug`TeO T&nC};{"?p_.K>ΨEC'(bOhghθ `딐[r=XvO5<{jT2WuWp'n?&+xJEyMP͞Ms0gWbPrxy!~ oٽ*ddbl5duxʻI)9{bhO>:5v^[nC27ǽ>(;O%A÷iܓ^gOJ) TzăY'$? 9vUbPwS<˳ho,SO!#EQɡR*mrE٤J$ho,SO!ǿ> ˳XDQ_iK ne=i=dMyOȡR+ӢkICoI>Z&ػ! EQQA?~ Z-3#?b.uΩ x˞?t:׮]ܻ%w=+;ܸ m"~@WkD$}.J&R'IqqqQQQ###Cb[Nl98- :m,Zum;zJtSUUҭخغ؇CXh戟:Q^Tݷ;ɊSOq D gp8S>|'bĞl' )))O7JΞԷ>D? ORQQѐ E!UUU&PdO3';"{?`IdO;'!'PNJw<O"~4 ʈV\OTT<=0@ jlllLLO"~dJU ?RRRl'櫪*Iu ׋dO!'|bN3`Gά=J6<= %~:H;'| P?`?y |,U͛4s(`O+붝iT^. ~L[OKl5mjf$ZYE{ ~L6;[ Jj{gӖ:[Il9Xk 3`PR3䠛ޭ] 'L7:)ػة] 'LV}ĵ2pSG3'̷e4g!΀A͗戟0ߡM+.q>z(gKig6Cͻn(2v3`ЖYK1FSE]3S_>dOEu^3cO)2XbIЌ?lJɞvNvlv%v u@`zIIIEEEUUA@?ȞR4M7r7اOUV)))) СCDDG} O{I:(pرcƌ]wh ]|yeeIFCF,{RJ tfff^1{֭[>|#~Ec~|]ۛt(%~ڽ{^z?%&&vU9 {iٲeyyAGfO55Ev%*w'VbQ7yaJNtǏ[f̙Ç(tOh΄' ej?li-%P'OU\<++wXti```.]z왗#50{ڰa=ܹcOڳgXfŊ}t첾3}>T.1p`ў@4>ȝb/;='M's%ҥESZ}}'&k(8жAg ̞ tS\rNڮ8y@y[_d=rd2m߾bƌJ+177wYU6::s&%r̎? ;bݵj+%zGKrPDQfUY潋ǿ&@[\{9Tz֭[=SjvN0NTNA+WWfm+XҫWWe;1 YYKNO,sDAUն. ?OY]w:ju(Uɒ vO㻟3JIYЮ]ߢo j$+k}=?R'?ɝƎ;cQQ/(XѼٳ|D*ٹ{S .fNGe m,(wSgm>jGsJUx];y $OvPrwoZ6/N:>-~#lŊO^qE?1=[,sMYbG""ZCPJɞ?t:׮][\\\VVVۗ@upC;H;vO$P[}ZkԊݱcNBCKJҕh$uB,RHHn. q:_z{wS3ggĻd߾~z7n3fخ]bg(=h֒c3)...**pddd?DO O[xb̼ruo]'{VMMQYYΙ3;Yrm>v,_nWJӉ bĉxҔ=9N'SO9@Un{yfO#<>|_ ~^ ~BIII~~~baJI}xWTT?,O O[ M x ` UUU@=?/?('\O O  ^t<4gD1,Ȟp?/?lGN_=!~^ ~ؑg3_{f^9bg5{J=%2ZSR'!&&&>> M x `S TvvvRRል1I3(=&*vXy'4''} Taa322RRRL8k'=@;$bW;O&C@5%*-- ֛dw(}&*vXy'4''UUU\ťQNG!CfϞLO2G .ѣG~^z%e֤Izٹs%K(-*vXy'4''|B3J={UW]u|i&1_=z/صk].O x ЌҿVZ:+Rkny:uJNN0`@xxxBBO, x Gcǎ8qbDDĒ%KįNPPիꫯDcee/X P/'|BiӦ}גvO>}رn߾}bVdddDD"`6@ y~ݻO0Amq8!!!ݺu KLLܽ{whhh߾}srrEOȣt\PPY]^^^SS:tc>`-6@0͉ uZ/W_ 4G6@0͗Raf.&>Z8r!''LS}ĵ˭s~0whs+ fp+osu~0w{R/R( ?/?`&epy bb7:[ >X( ?/?`ԋoOj=4p1J_gA.4OO O_-Kl1(P1J}IDATNB@D@ɪITrM|8˽y=p_:B@=չ7@tß?/?)G^c3 x WyQnm5Uҷ&Ϣ(ke?#~/휺Pni(Jh_\ x dOEuRnό><*<%PO OhJs {'EQ9@?/?@=QenM!^U޿wzIIIEEEUUuJȞ(Ex\v78p`>}VZ 0C}?/?@C=Q廪-r_~;f̘qu׉˗WVVN4i?y%''h'|] ?^z)!!!555bzݭ[>|O_| x GDQTӔ@)ݻkի)11k׮ Ԟ={ZlY^^;O OPdO1kD]^RGwyFkO?P^Nn{u-MuI9~:~xPPкujjjfΜ9|p ?/?@] {)%kjݺt,V6}q$ `׮Deb$yJ>jo>-ݣhzy&P_r񬬬޽{+NK. ҥKϞ=~KO OP+?ȞD}aܮumܙpDLm%OOn=PжkH$FZt閒@iON~mK:tZe^xwn /+u/;Tq8戹?٥S_^!Z*+7׳,GL.5j>ԷoWįÔRK^]>ںMp *cģEq%is n(2Xe.r h<'б?-O\s< ,eT|o_1cƃ]wh&Ɩ˖E=7iFA^e_|I[O,Pm(V)(&o]tEX뫯qq/bB^3bcnXP]&ݖg/v6'%ru}-jR&ԅݴ_?ڳ}wVASO/KYYΩS='eBCݧTt 凩Z9yXZdz,Iо5kໟ`rX1WDHH'1bܺu UvW~MM]خ] Ɖە+%V97|ty]1H0>-oN(< YYKNOlĉu}-jR&ԪwӺ;l#,*/kּYzg)XG50R'=}=ܛ܉[:]K-{S .fNGe m,.v>˳h_ x jUotfN+]vQU-[T>%"g}-ݻ2W7#Ѽٳ|DDnK-;vxǢ^PF60z*({H5PM͏]wYoLJIYЮ]ߢ񓼇Ow܉| ÔR{W=ںM)ٓNڵktጇG{6Du;?;/F99o9ċUmڻ7M˗?'OQX@ q0u@Wۊ>?,oJ8"[Y4K#ۈMb\uO^qE?dtltmaʻ*{H?ɛaaQ۷ؼ=*)IwR'OtzIUUS=uBCχ?S::0Ԟun9fR\\\TT(,,$~{ז@)O困W~]}O l+ƨ={^q㻵T\KbޭEaa!N+CNzj`m6ݻ_w+ሼ =<ڵP3gjscm)%dܸ^{̘a-/70][&m~ Vt;=()IWws?5{Xx*UG'1}yQJ^ݭwu}w t?@(..Nsjm Tmuo]'ǖ(m?|XUMq-ۚ]e\ǎ׶-*‡' D'u,ۨ)y-j5jJ4Gj P<\Nt0k۴G[QSV=[<3{r8;?@=JJJS~&PKPGTٓ'*$^***@sD @>ާY3#="~FLGlll !yb\ īx $ħY3#="~o Taa322RRRQz 35DfOgl $P4{쫮2,,lӦMb"888??/ }رc}ݾ}ĬȈuE,ec>!O>ݽ{ &-#$$[naaaw ۷oxxxNN`ϲl G.+88cnFm9t|Lec>!,XpW{,45ϲl Ӝ(PQz'%GQgX{6Di\5t1!wQB0>Jq`owΪU]ns+Mxxd`COi˝[ =@VJb]hr#="~L6;[ Jj{gbEC[E&g<<2!'LuԹw?]2N;pG{6DL]b Gkm41J2~<Oal .S*_MMQTSif<<2!'hN_kf?Yx 7xxd`COԔinNu'rJG{6DM쉢*w*JG{6DM쉢?MMnwx㍁gժUJ{JJʀ:tGy;#="~&BDQ~\u$Pn; /߱cnj3:X]]|I&=/d<<2!'h dOU[Ob:33W^DHHHMM޽{w֭>+#="~;{zͨ>D*{Gt ϓ#ww{P;,%1Ak%.[Fh[DnQJ=Pų4;?nР.;UUm&KM](`59w_ϟ<\3Xw(:}d<<2!'dOb}3+.椤WBΜbbc}Ya_|I[O,Ptm(+/d֬#G^/!!UİSv<^<"E-e"͛4wG oB]PSO/,ԩr&"I]˔|D::tR"\svX>ԺLzbtءt{ر#^55uavm'L'JW/+/^ݪUK3**99ot9Fl<uxDrZ⨊M9CLڕغu MX,ph"g"v۷oWۇNZjYk|X|uϝ"g3jPyuի_ݢnu_ӫdIEG{6D>㻟f#Rffg.~ر /<22G͞xppPd# g]}ueȑϚ5q_,[hݻ<^<"WvL骖-[ȏWڵ-j{uOr'sKJ^KY^]6{#aQJayEswVZM{ɯW˗?'}>M leKuk)0辴K7ӑyznٺC-W3@m}y\}'ߩSۣgoTZ%f~Dյ j޴6mZѸh=Ç2X:&Ν;XE?~fl/?uxDrZǎy̙3g><|MudAHH矟TUС\LDݓ(G+z_G'ڳm4G̭?;,{D vM۶mWn]m߾]Eyuf?-XxuX۟ZJhӹvⲲھG?V@m~iyt2Uxו7kas~=ێ7K{3LrCCgփRqq/t"1Ctu5ȝ!wNA=ںub=[^~-/7;S#[<ק=yƍnB;v#:,)IW\DQsLLxyGLAgA)yuޱm{<R(;ۮ][wNp56DwzVݭ\C˯-ʍA,%{rp8222 m?ot1T_1j%o'N(lQ-zYYm5+/SoɫÇ_YX~6jJ޴NvjxǎJi:=bmӺ[FM[DniT\ 9jɻ]ۃU]~ɓ>ꨖuuK͞N' |8u1Z{q EQ+5~g)pDLJJJJM>@=%W) o_vuuuٓ'" ^ ***=?o' E}5At֌ ="~N(jяz6D>GEQ~\M=5#B)@Q_VSfOg͈~!'h"rr?3FEG̞ΚX`COt]M=5#BIy&Pi/?ոgL#*v\ʟ*(qS,OyJ$4{:kFc ?@SLGlll K3(=/+,>͞ΚX`COp Taa322RRR_g-hg'x 'x:4{:kFc ?$P孇Y]1J_pvx򊧰x"OfD?V\0brxL \K_(}#]uȐ!gVL裏 ѣ_~/2kҤI={ܹ%KXx򊧰x"OfD?V>Ϟ=:{>v ۴i/=_ڵ.'!'|B3JoZjUXXp8Jђ֮] u)99z ? 8xc ?(}ر'NXdw Zzuy_}h|###GXV>!ڴiӾ}JkIIwuݧO>vw}o>1+222""B]PTe ?(ݻw0ap8BBBu{о}䨋 !'|B\κСC ="~4'+iyhKW_}+XUcY`COe\3J-r9rKdrv-яz6DjO~1ʻco?M|KjCS9h8B%_[fQ/{ƛGwўQ ="~\*0O̙:glWS|KOIm~Nu)՞] ="~4;u#Sg(v駱]UW~g+BoNl=ӗa>>;;Bo~!'Ϲ꾄erZa^~y#{Wl'7e?uٲ?9s&s-^qosʒ2ewhS~AVy۽/}vsk.uleٲ>}EG?k]ܪ{U߮:yJKK ="~2^= Uotxcܮ[M?:\/++[iT|.~17&&&))i%%%?xxc ?|!WS?C_ LO_,& קq}NAGpP@qS,#sd驧/.ҥ}>y@<Ľ[ 첾o+^:t5jڛgr^bI3zx\ KL,["V ;駫Çc}z +Yt4w_|)fjmaQivR~Dj? ="~\CVZ-RCB:Ž>o嗧tV/㎈;.[۶m]7צMk%=?nwkbĻn/.?Iy7IwI' B/~z޴*鮫1Ѻu'QQ/lذ4kذk~=Kzz`E֜9o_*X@%L5kM9,RWhBpw'O\ycʯzb⫯[)({ZJ)ALKz_U-44ouܺ܊21y=–,&&oz;Ě5oc}Vm֬;#gtii޹߷UaQzS䝬|xc ?|H~ӼyOy-ZP??aî8PP{9gQ޼se}kԭl%u\"رolV<'DO˖EGzwkbbϥ`JrԐͩ;Y[? ="~\㧕+狉 mǍ?BLlذ< 3+OKZh|kY/~ZLdg!Vܱ#sRdm-=bkuTŢ>JR/[4ky?նaQ{h戟.,яz6DO^޵+1"ꀀ5~onܴsgB>g?b)S +{=߮]ے.<|gԙ3;~^{g?U7!R%u?rd.kժ}ٺns­?CCyΣGVZ U7~maQ{h戟.,яz6DOK!}vs%$1}>:E#G^|9s&< _\W_= @y'=e~\Ϋ ]׻[ٿmU6yVKw5E)v]yO`S*ƫiEݥe*}KW? ="~ܩxEiEݕޗ)'s:[ ^O'_0X`COkL *&~яz6Dh ]zsͩs_eMkv`? ="~4Ő4-9v<1rpⷉ|xc ?۝s/rlAA-p8QWZZcG?VXMZwy\Pͽm72"2e;J}^vmqqqYYYUU2X`CO&rJKKވ]yo 6{٢JouSψlk3cYͷilkz"-炧ou>8#1>A͞ԏ[eH#C`@OCɞvthyܶg_v}aPZYRHя ?@=bv Bz:"~|N#C`@OFT.R⧂|+b{׮]AAA ^~d Ȟ\.(%~ڵk^b?%''jJ1 Nz?0 'O[k>p`~|g/z˻SJ@BCC׬YSQQ1yA&I#C`@OS쩢b7$;\=를]Rj(9*:}x ų:t蠌y7nٮ] /я ?|Lݲjr%m@4jŧNhPڻS3PٳRE>{Ztf7mڸ^ʇsĩ:.>{ ӢE:vlݭ[/.dW=odNm""’?RhrP^߻vi5'PsNl8$2Ev_bgȧLy:,,y'eAxx֭[FELewN9rP.oIԩM׮_n^\u(=}vY+}ѸV"z!_޶Q 8*gew(Gd2|ᕡ0 'O*,,L5̙mWĉvV~UUT(.^_׉S l5.ܚ! JK7>}}-mϚ9{JLL/O'Y,#֜@b^fO>![RRB$'+C`@OTRRҀrvhTE6aW>n7쩊l ?| %U=dOUdxe HjY=UMxᕡ0 'o&P_xZkfY3/1ɞ&Q1b@İPTE6aW @Yl6zɪYUR1b@İPTE6aW nl%%%V%;2*}gbo߾=nܸgyFl̚5m۶;wC=\vZh1ge"E * ?2tDGV5\Su!ڰa 7n|ر~~~|MYYfv>l£ ?U@ٜ^IOOoԨј 7otݻwuwMxᕡ0 'ۿ9s戇_~ehh+/طoy?,,lʔ) }FW*====88I&Ǐ-xٳ'O"qhʔ)w\Cw 7?2tDGhWgϞmӦ͘1c{[nk׮N:uuՎ|FW*f8UUYYy Ǟ#G81ڻ^:"~#O>W^N4@ڻ^:"~mN(qlkWŻ-Gu>"(+C`@OٞO-i|X+&I لG^:"~ʏ?I lWMxᕡ0 'oӽUzFqلG^:"~l(L; ?2tDG]vXg^>R}A#(+C`@Oٕ+-"UyM7z]{H}xzj0ل'^:"~Ȩ̾ū f!Ggen~YsS# ?2tDKřy)G%nz!'z5CkMUm{W{Hg/*f!G٪jMxᕡ0 'DNM\r'Ū8?EQIUVT'Ll£ ?d!a.kyXmByΕo߱fvl£ ?plaW]{_r ={oY֒GU!ytMAK"C`@O;s4W(+^ >(IjM~>X,l юf8zhRvqrPcȃΕo[eIJ7ovN6xyYT _uL4tJKsڼ M~ޚHgl;V$wSP7tΝ.7=u6= /JMM˳X,6l}I>{~d M'/Uxi KJJ.yjӺ_P7J6~#$ic5e[&hh}ḯRo׋ŔŽ1-!!!---??XLɁ?:v:"~x=֤9eE,\vRZEpsU.ӧ_md&NWgZT*?@%{G=A#C`@Oobd6'>J?OkX/(?: :"~xS-㧾}5:xXXh櫚6m,9V9',,4**|ڴ+r n-ydKT6Û?rD;<}%ڧ޴)[Nٟg܉J[1x͛){w[k\vpy ]):H{˪!~:~}<97?: :"~xS-. :umnofxY)+ҩS/OMlNܾI9rz&'edD;z*GFHH0wD??6mZFGϊߠz%ڧnСŞ+hdҶIF~8N o.ku= /ng֬Wŋ9~rA{ODE{ܻ7=/i.uY5OšOoxi6:"~xS㧿Ie{߾~~~ nj9b@sĩvtEuTlbn*|bj;w.vnE8wܿM|?{ʶ8yР:gkrwqa>xkjxiq1cGT$ļ׭[' N\`YT?-yEhhqv$+k*~rJƾ')_S5}KQQo?PYͽ/_>G\լY/\ݳWW5OrnsdGT);S???uʚ'v~j9ʷnSW^S׮ogHH>xbQU/zS}%vSG5O.;TwJK79.j&OÇrq{n=~|hұc?筮ݾ54SOeeSC䲃{?ztxXZiEE.HJ6~#Ԕwj*ґԈڲ%[N!!]ܸqaǎ'LxqͶWbϞW<佁鳝{:6Iv*]XŴObf͚Z,~MY*/BΩMofǯk΢Np%^Gem6e5<:ӛӉᕡ0 '7_b)kM9.Z)-ݤY\nde5;'\R|jU)J:u\JĉǎiϬ\޻xU^ ĔMMT ?)ɿӵ1wR퉞iٗOO˲ݟwSژ++k޾}+} {qiOOvM4%?M^;[Y6I'?2tDcg~N:($1Y#~ +C`@Ope%y%Uw$/J"~ +C`@O`M?z6&ަ'&LԼ qH,<)&!"(+C`@Oӑ &KaobJჅɁS" ?2tDKϗ4EM?Oj& + ?2tD.Ϟ.): -÷dxe TQSo',i[Mxᕡ0 'nٓRંսH'/m6(-r/&k2rrr.ڒeエ@VPuWJɞLw=i6K;{ `g:"~@gIOٓdOv]i[GZy|8~d ] Fe=%&&^|NqGm?/H;uy' K#C`@ObX{J=9>$:>%%% p?0 't)))MEG?2tD.v B#C`@OUsEwnP]E7M2!& {&)) ~d qNX/ᘘ%x"{N:"~ O*}ߤ$'?0 'LIv$dU>OMMMIIɞGp?nI_+VX+f'?0 'X/9د>D:"~%K"&M 'N#C`@Oُ'KtVtooI}pG?2tD-i4?6[!)CT@m=Eɏ]\F?5+wpB㡶8*q< ;0EG.66xyYʶ/j XJG? R#۟Hl 4wG gZ~K__ |U7z]{H[~T=:O*.Sɏ(qT}R;Ԍw~%oʣfho[z2muRML:٨(_}j5wFFԡ5[sn|LoSož3_Zkڿ[}'~ޯ!7׹;_4'?ZddrtDkʷijF첖4ÎbfsS~>ܹ.'wEˮe4:KkF )j@3w9χZ:d9fX6Ue_qWJ- T=s;ã:dҨ\}ӟWtV 3M}B~~> Ts;~)9O-1JE`LOY,քz7'}~ Ts[sq}W}wDi"n8]}P^Sכ,llZm6z 1sƈ>Pk;"~"]= >(Ij͠Gj2rrr Tq;]\{x6Z@ g5;yki&>IMM˳X,|޸9Z@ bM[x6$)k|1GE'%%egg':?<@ G65kSK)S޺5yC5Ibߜ_\\Hx[H`@O)(kCkMR".|yC˲6v!yJ̎SYVD“9R$~իNCK?鮐=$O)Siqqq)))E=$uNx0 'j?}!CFEO3a£^-2# ?}@9M?}ݒAnl֬i׮rZKe{fdD蠠@Ѽ[N M7|UӦwZ˗A^řw嵢bb޽&=QsԷ5f:`u꺉s,r=FD3ܹ.ꔔņx(nV}d~ϞWekZ)'_G;"~HSYٖNt~yj'fsIb[o=ƍ 223U_vȐ>o%>̶m^y屭[Μ쎞7g߲%k͘rT2ēFEGGwoz^ߕ\^+N.vN:I@qڶm릜yWu_x-\׳M ߉=zNn:)5`>ռ爫5k+/|N{矿)vqGa[|mT\ye9s^wy?Bܵ+e[ċdqZ.G?2tDE㧬y~~~8,_>_ILnGuSFFoS_o?-Y2Clk8?EG=?9!tӋ/>,ߟ;)u7]y.9ZrMu?Uwf׮zNv7| ?{oEUWO^?0 '.?8mۨ޽޶mѾ}+ą i(bXtf˖-ƍ{D9S?=>"";v,oٲO]tx&^~~~J3hЍ?'v߈E#C`@OYZ؋ÀU^nVßHn;466[G?2tDEoRY(/VKE:"~bbY{p_Q2ԏ3?<}.~d 3G6[გoLE:"~H2/Elgjʋ呱ɳLԼ<ժBxG(?Ǘ|\mByvOfmz䤤b“G?2tDˉkI?fԩQUS l2bbb KJJO@:;2\~] 3U?euNEI&)11wf'?0 't 2R"o71n*?*_Ν=^ srr 4vG?2tDfZ='9׃=u?Zkڟg㴇,1M)!ƇM>wΞ_3bS@:vb)VT=Tr<}<%%%)+333ŤGG2rNM&S˜؏M?iZ.GI?653O$?O:挙/k~HO<)$ {.я ?$HfsNNNjjI8%Q(?H$I*=~T9j1b)A]:"~KIVkaaa^^^vvvZZZJJ9qJxcX'1M}@nbH,Z v1b D=yGR6bfgggffzĐ+Y p1b {.я ?|B8r(ٜ+U&Vf}> 71b`:R'1O8GQV" b31C}@nbH?0 'D:"~l(nXl< 9د>D:"~[3^dXKIn&b.j#F'N#C`@OYQ[JH%{EA8B#C`@O;_Tpя ?~G6&Tj8E#C`@OxIJ!b@]~d ` % #&UQkyĭ aя ?bY~A3E#C`@O9rR}@%{1v⡵==1KJJKKcǎ=z<{j?d?0 ',Ν;w=DEE]veZ{N:gXĶlN k6nܨ%{9ʝ vTj~=z Grիɓ'HbmڴZ ]vG^hstReIYf5k|LIl6… OګjNdɒK*G?0 'tVkpppJJI㏣F޽{III&P|bI) Ԋ+ĠFLR{?^uUUCoE 6즛n }TVw:/L#C`@OY|w?/zj%X,bo߾})++h| _,mu̙={dMnW?tkn򶑑-f~iҙݻ_޴i{ڕ"?svūvZڬCO\ҦMѣ9Q(,Z4cݺuɪ\UuPΛ7g+DO岔jHȰݿ@;ۣC=6Ew+EU&5OJhӱ&)''X!ۣ?2tDpIJe;Q(utAxxwynzS fxGgl}_i㧓'CC^=ܹ'?5hЍqsϞN8⧶m-h࠯zK{f\sdO#KHHILL4OH0=Y=*6f͚6ofdewܮq㐑#uᮻnQ]R⪹s's"#[kwSZBB۴i:⧩SNJ>{(Axx֭[FELҶUup~bZ;:;o_L&Se ,J F@Md:sfk+*v <6jwyRGv&'Ok;8x{Rګ\rΞc/w>M0=YZ,#֜@5fO>CTRRBs ?CEiII(ٲ*>M0=Yv#'PfOU2=|a0 '{H(5l%gT%wF:"~ݩ%mh3`%[$GiR~^XbCϩޠM>꡵Y;ƾ.UT%~d PwGm"(N__ߋR75vqT}A&P ʞ|sG?2tDХ౷SO[XZ܁OO*ϒ&SEOpJ$%%%y7{R-:"~rdCar@~?X(9Sn֘էzs&nPܦ׳*y{я ??$ҥƲ!ꓼ͑@ԔGܔ5q6=)|+G?2tDkoiU.Pʅaפ'I@IVo6rqS ۔!{򝷇O@M}1Wvf+)))..ZGܔ5q6eȞ|!?я ?ܠ౷3:Q0~d G6]v|!/* ?0 '{d^>R,J Vx{G@:gZ~k<ߪM&E!c$/1J`NGCú:"~Yl~jjFGJo3-^]Q~F=7 qi oNG?2tDwh֜+_3{5CkMUm{W{H'zf/"f!ݖy)gΪyVo::"~o53[nk;*+wR^7=~ YF~59xMeEŦ Ύ^?WMlU3bS#/i 6nwNl(s?0 ']?=3Keuܕ׌J CARj-))ŴB;M0:"~8fO]$STYwDVbbZQ;T#C`@O o.wPR 3M}B~~/Ӵs?0 'Y͚0_6d/M2Ii4s?0 'ӖEZqfvaLV7Y|Kfjl6ʊw9xAFz^*䬜Znd),,,..N|tNG?2tDRx3̇ji&>IMM˳X,>ai5ΩG~[%1%gY)(:)));;D=VC~d ۑ bv%5e[&v~ḯR{\^<ѳ:&ILY󋋋*jM1?0 'oZvI,a]vY…v^uU_Ҟ-ZEjԭ\$,1eq+ղZyGiԦZ7wzuI-@fsùjnKh)QEiqqq)))E=q;e]=P,洖G?2tD6@߾̚ O<,,4|UM6Y+蠠n:=:; u=32M sϭacƌUNӤI?7wDS{h\5}m洖G?2tD6XO*KO]P?f#*ܹBGر)mF͙㐨U>ϒ<ѣ# WZCw8/^\yCbT睪{qڜoP۳VJ>]_vֈ5њՖOG5Ӫ{̞SϠA7Ǽij3i-~d [mBGj#J@Pn0:em'q~xUӂSij84z[n9DUIIGуCQQNUTݽ|m5OյyEhhG|qUfMbreCZ]tTQ;1QQb1wL_mF9%я ?@}M(T9XGGl,Y2Clk8D"j㧌h,~<uG)S9ڣGgm5ONUTݽ|m7=e+ƺBTi_|Xݿ?5OG5.y≑F սsfZԷڄ'6ojĈb_Vű4Ƈx.oI߿XkCCmtرe> pMzj?,7Iζmzz۶E4T٨^\6tZX,KlٲxnM<,-ݴvq_EE.|:V;{n=~|Ν;vloV^1tڌ6sZK:"~VPUHh˖n:wd(yͲm""¦LyZi騢wweE1vǏoPNvp9=}m洖G?2tDȆBx;b풸:xpMi&=O>lUS]:WEŎsC>tXvWWQ{qY7X]i[9~5⎔mѧ|k]5I磊7VTQw|I~ucZ3]7~d GX-^]Sr5~VTQwk |tNG?2tDRx3{vILYN3U5o߾ |tNG?2tD^.)9+gࣦbcc}+V}~d eVQO7J:up)q>UTN|wNG?2tD^p kXYkƔlʼn>_\TQ;9xCeϕoӮ)yXa)ogL&SLLL|||jjj^^jUϩxiʧT#C`@OlK/]STYˇ'/KJJIHHHKK:xʍ-Ŋ Oq]UV\uU@WD*9ɱktR Z0"%o/+Rδͤty=G;$3C >OyF+-9a`@OPo>X}R:7ol$If955b3TIDATvSǸ䊁s= ޔb!_QklGT^$)333WZ'(Oq9a`@OPG^k5x:up3bz29H;Cln Rߌ|9a`@OPubEݰ'<닼KmVOrk]:/qLY.URt-=2j h\.qdJ;ǗGSяfD) ~t:^Z5Y #Ҏ:~4turuE$"(--Mls$Δ8_ k2 Ƽbj~00 'gṀn#iO1-9s+_o@ziukEuTeKJYj}/C'233f@]Svn/X=ڣ= _(p\6MFzz`wѭibQ1m?ȋ/΂8⌈rJV/Gs= @\na򲳳CH7 ĢBGXgq1G^qĹgDSTz,..|llV'Sb˯);qqG[sqgA qFyip9E5J#ΩG3"~}J\.X6;b}[bQ~ eG!8h qhHQJ#ΩG3"~ ^GJkĢtgeG!p 9WZ\wNG?z :X\Q0a`@Oȝ<m0 .S zy^hX 0.a`@Oȕ shT<-z;H;ˣx؟]1VlK9Jk~00 '@3ck= ***뮽ڤ۷bQl.znw}[o7n\ P1cǎ9rݻ}>x  Cs[wc|uZAd۲'%S&ڣ= P;'O7n\Ϳ$F;p^Gn~׷v/=9gc_G3"~BQQQn~}I4wYf%%%-[LvpBx"^{7\e?oGM:P&K5@{?jo߾{*++[nٳo2c~{{OFʂڻ-VפS 8U+&-ˈ_G3"~ԔӧOx˺u^xᅸ8-?e;))n\.[[Gņҟ/qC<2T[lì&G@i~00 '@M=3J}ΝΞd]߽{wh{$v|Z-}Yr`EGPF݋!knJd9G3"~_m۶Ӝo];ׯ_rrrhꫯȰljpmK;./+QѪrXяfD/X,CO.\(*wCž;vLMMX,v],~7t󶇞R'1q:Y= 5ռyӤ&M(30w̙b߸Tٜt:ìXUzXHCPƬ]I.[A= 5y睧̜T>^o7Vv{Z}oru A꫓?`]]3g>}Yݮbk~W\Dj~00 '@Mtm)303gNhkIKKKOO...xOZFICԪ[1PO.WTUOb%GA{?jp'eݺu?:-=;>X;g'u pt7( 7\ժU ȑeҤ6mҡC|nSϞ~֯_5kRԓ˗o'$8ӧwED= Tbzuٻ׮/O"y۶s䥗v4~?sF߮]k+ɩV߶JhJz{޾y?ԩmrb )ꪫ$I"~R$4md'?dxڷok֌޽/\h.=}?iҤկǷ5'>yٴiEm_I#{C/?ˈa[ܵ륕uu=ǎceg⚩'vܩG_l\b~(Q귭8/TT/wlyYrwԶSR۳gyXVUOPTTԭ[~!q߾}eΜ9f͚={oߵkW%In=^{d2^m'6~KKxc]UD~]aɒC]g}Q$\t_ 6ܐ!׉Jygy}ШQ +-v̜ި*\U:ujەW^1{_v0=!=9xH~_u?"~ɓGuieGq}VbF~5\5Bӽ{g VnI~W\qIY_T=I#׉>>noOb_X5j([Jg7*J'V٫j)_۫W֭[񓨵kڴit^UE P;O:tȑ#OE;v$I2ͩnn߯!6~2Lo«KKRR4ijBoj"N{TՋ'jdn{Mn n!_vT:~Ӧ={^ clS+)tՃ+j%5a׷oP$zjݿ})'`@OZ۽{wݹsUV)l+;_މ%wMӏ?~|%?䫯O 5 7aͿv'S}L&UORH~;4>=m#b{۪9޺u?/ȝ<~1X?/Cb՟|r߾]#oT:yo`xU3ho&O޷"~D@ z].WAAYƎۣG /P,;vxWq/tbs'gOv](v7W#b~,u Q}!(cֆJMKK#~A\0 >Ont8aS(1L 6Z$KM^\ X'~(5/=  @+9>zˆQt3x(X,׽wae1iԸ5e)0'Xy^ӥ8u̝/5`O$l**h~00 'ܡbžϫ# u·)[-Kjjls:>OyTA{?p.|6-h2ψ7_s`7$I2ͩnn߯\= 8׊@=3T_fk?juk$effRяfD9o7unƴ鯮|qeEYT=nY\\ll6[vlU)1ה1JAqTYq~YZqq#˞G3"~!P.t:ز~%S+1JAq N,k dڣ= ]^'Hĝk1JhENG?zsdQ0a`@ON<m3^Ԑ0֖fDB!"?CQ3ڣ= ԉңɿ;G^8яfDʶfe :Pf!jUˡ r(j@{?rdڸv{gIDϒcPԀG3"~uw[ޘ7P2%ڍnrjF{?:'EZvɔ(*/~xP3ڣ= ԡң[r$ڸw?(fG?z[?J {Pٍa`@OnM]?5h~00 'P/dJ\j= @+yrdJrsԆG3"~up<37pKě;tۢE^8яfD뮻ko߾=dG-ZDcZڣ= Dӧ'Oܭ[7ͦ #z1RW ڣ= Dӧoqe3v#F޽`P9aG?zɓ'7_h#ǎ;az~? T5G?znݺə?YW\)ZW\qr< T5G?z}𤋮lݺuɷrK||d_ӳDohثW^Nڣ= DӧOx˺u뒒LMHOOܻwٳgvzkr(a`@O yW^y%t߾}k޼"{=׿Bžw}$In(5яfD߶m[iNN΄ S~-X 4^{Wgddl6xG3"~Qp|Ӆ &$$(30wދ//رcjjbnz!a`@O 7o^ZZzԤIeFKb߸Tٜt:,ȴG?zŷ,Xpy)30^v)555;L{?(PoVfNa/Ƌ}暴bL{?(PwIIIqqq >eʔDbxzڣ= DӧOxK~~I hy-Z>X웜LT=яfDo߾6-RVVz3f\&I<駟~Eohꫯ$zڣ= DGQQQn~}g͚5sڵ+|/3g$Izzڣ= DɓG}ieGq{Vb漼<A= @Ԝ>}zذaG>1fܸqb|VV$If955bv;G3"~Ѵ{w_~y}Do׮]9{ Ie4ڣ= Dsݯjnݮꪤ۷KvN(#F%ab0SG?zCr}>OE"x!яfD0h~00 'A{?4 ڣ= aa`@O G3"~hG?z@à= яfD0h~00 'A{?4 ڣ= aa`@O G3"~hG?z@à= '[XSTTJ\Q)@Gʫk :!',R^m5=<>0 'oGrR :P𑸢N|sHy՘H ҡC/K3"~NJi, :*:>0 'tR ҡC/K3"~NJi, 9yV{z,kK 煖U ?5GG+UxWpni h4U-yh.*;ng(H C.8ŏړ%6C?GgD@U܀񀊯>Mǡ#3"~N!Vk.E?P'>0KK\H o{Vҷ?#3"~ vNY|wcģVvD{x}`@OD=2|)I#3"~ ³'J& Z6N|`08WGgD@Ԩ'vL&߿o!B=iQU$'OU$P>Gz=<>0 ' :=<>0 'IYUz4 "#3"~@gOJ[>sۏa(imth`Yk.57E>K{x}`@OD(I.u%iUˡѢ+0V:? -ٓ\rfq6ìMQZٓ%$.)/;Lj Zۿ!V믽K*ժ.#Mu.˳$KyM95I۷ľbrFX D=)r0??eGMoPn:A"@@yLΞ%O OD_ St D :nXeА??P'>0KK\2''۞-=2''og~y7Fy"*9(4hO ODA bZ${,*=zy~ePO@ФS(O?cXCW@\m&PG{^ZvQA,7O OD(I.u%iUˡѢ+0V!''"%{KN ~s;]f@"jU!{R:V{^%z#? ?PkW; |wCZUEc伉Vyd)UT HOb_1CV 9#*5 /sZ .G>,(ՁyCU#~ BZ(9{ʼiii$:WX9Z Mu &?(9{nlNMMw\>#XۡGyZ!@ ~@OUʞ$IRO`P𦒍m? 5wMa=eff!N?J^w?hX8۟:kΞB_}3y~"~b$PdO0,'4 gMȞ`dOhU}E#~ JȞ'C@vxb!{a?5 T)q RSS D@4oeHĬ 222ţb!{?erUb^#feggGN"~ gFɔyJQ\\,]. :6ɔT?^ D@8D2%z 6=rte`0O '@F@Ԝ<m^8&E$6DA@Ԕ -GNxW`ODӦOj9ȇ%ITvܠP h:TLQ{RĆgI֪VCFlv+@@?fU%SޡQw$SbΙI nړ")A@ d7QΞ*rC8 +=e7M`pOD_ StG@i7B2%e`0O((+ǯTBOIďx^eIYTy ?"w?_^Q߅nMQgu]lvV^OQOHO,6 jȇwMwmP_.ÖHv%';!;ndJz|L~֪jǪ뢼wŕ>ls:>Oy9!~ݑ X\{/ۣ[˗ϼ첎iiIGVUWu7oUՎUE,+-c^r@CC8ÛJ$S⡍:/[&~UK^ziJӢJ`U;V^#>)ꖪ &?ή&S>=FJt2ygzqr`ѷok֌޽/\h.=}? !?K*,|g˖5k*g7Nm!>%={V;UoނoW?vl~uŜWooi&s>'++*}9v;,?,4ixfJgP(SR͋VcUg=nb?y&i s}Wm[ĉ?D7ޘDKS]ygElsϘ7?)6֯[ v:=/qgƋ/>QUk:AFB7Pٚ:~t*;bR͋+VcU58nb?b2B?٢{r t[}T^ L<^nRNOD]DզMK+~Vվ/o?6;**}-e^FAVOPPaG]yRvGnb?2Lnwŀ? ͛d%w(wrIJsIhҽ֮]ҨQ#[} Og^& +WQi͚1ϳy]k?UZ6_}/?iժbԢE< PaG]yPUzڱҖJ &?.x?r߾=⚉K.?~W_85S o„OޡA6*K7oYtM|hm6-3\xa{9P%Fd2Goȶw:4k4!̙ɓUT(~*?6m2i}r"zRtUZs>EPp#HV-Zls+~ԻT󢡪VcUg=nblvKădP;wfL>X:x:b$+tB/')-uuߪJ̳a0]UR֏?~|옣\u@`ɓJϠP#%TjWͨ߿:Xd#~%O JĽYyA5th߻ժUQ XU+-}'XB8?.|}:/ծ]6ISm}Y|lϞv57Yj?m?b F\r'yEE=$]26-- ?jXrnZUWu&'?'@(X2%zSJL&5oMYJc5S^߯zOEic|$)555##f9׫?jPvɔX쀢 ߦ]n&nXRSSfs^^|  >VRGQ)~;X!IlNMMX,vv~U P eeokw£5i#[zM_y<@ ?jgnx%ɔތw~)Qg u:a iUۤ7Sbv|`0?j'x^[9=nL닼KmPOrk]-q|h'8Z\<9HLZBSgw.K\f| 6?j' ~t:^Z,Cv)jX9F~:9ɺ"Ib&ť%.0q'@nW.z銴WSҧ͘9Օ/ηX :5$SuTeKJYj}/C233f'~O]\NdO Pr m6t9Mɷ1iɒ)1c\e_!x%$.$q9='@na򲳳C_lH70 $Sde C\"`e#.q I\NdO `0|>qNnll)1ה'. qyD\*x%$.$q9='VrC9r:/dJ\xą!.qR'q<0'@4GQ^c`%5)qge P7H#(;!~ jNSx/`ODBy"~?C ~ jJr#GNxW`ODӦOj9ȇ%IԪ惝^T h:~dJm?fOJ,i;\jfr(` OD-av#$SbN!RĵV90 '%_J)F#HAa?eG&*3j7?}E gOfNWvFB@U܀q>M`pOԉΔ8e`0OXM%R?!?yu׹)ٕOsY( p?b=N˻t[oK\fѣUVC?_RR5\#6`6o,6ڵkt:-Z;v_dOO8SATUU@_zǎӦMEciiiBBŠ+?ƍ }M4q\W^yhYzu'Ѷmۜ=zt-++KdOW ?m۶Ml]K.7g}ִi6P O]?~?>p%K˖-kݺug۷O4~JvfΜ؝&N>Pm9~Z^x|`wݸqmVzf͚lO=x_~駟~ᇽ{3g80|߆=}UY+{ԃXԩSĉ[.***++1cưa6P[O]_:u4qPKfff|||ǎ;t`Z?쳄Ν;wmÆ az˞JDUT};U=8@9rD|s񂂂K/T>of-ڷo;"@Ѕ@ Ю];&D!oB$;rHX'Qڵ4u1~*W}* _:lj j{^|>:nĴtN^M}tfOee%]\2{_.cV֫e.ؽ{efc23ޫ:e]S|/ |Hӧ?ڹsvV|4L:}c+zIwկ.t{USr?O%5oئ>Թ)-ˉo@"~ZKDbSƫ3okĉNmm"m۶NOP$$^/9W_Ml|1~[ĘRڸqV=RO?wk֤[tKWWx!֮]m[?Hk֩O߆ӧwZ~`pqG~(ߧ}.V k)vQbG6f_nT+<{L;;G*Q<\}S5O*-bSOcX@#~Z`O~?* ԩS۾vWdOzX%S0VEsa1 jUn@UUdO,Nի^/(u=X?Y|'Pv=;;;333###xħ]qȞt?,8Fuڸ*1k:i3L9zJ\.Wqqq~~f6gG@' 0'=)RɿW3rz\.Ql>0`ES+2a2wqjӫڶmۨT߲@3|F/ޘBNXڡoq,_v2O"pUWm߾}ܸq_,((;Oh; fOj@M:k ӬY 6DFv7|SNG~r@>쳓'Oz|arfB~vIUCnsyP?)''G/_޺uku"22P?5k~lV w8W_}YlYڵGӰaôN:u!%%Ej\ϖa#?Tv; A @OLXBfsvQƁٓZ!CڵT2.k6i)<]ZSOw*ߧ;u V,(䷿I ΞWz1C#@QR_͑߄ L&t:T+O@"}>ӨQ͛zy:?^f>ٓ٪U~}n_~tɓ[ԙ}fV'sEיa.c$y7 r؉=xp3|Q'>uQ?job! sFczz;=vj ]D`kGqTÆ,-3Ϝvի3rj7_$:u"VxG?mn*iW/l(݊|Je׽#9cOZOvkVWuXI; 6~&`v.$꣏&WP4EFHXwn[;@Br6T;]zhф._>oR{Zef~믿VӦ=v󏟤NM5pOjkY'5{J {n6\u<Z *u`AʥHT '2B&&&YriV܉@ jժ9ĉ?5z117]v!5`zH%Kf7ky':JEGچ_zfOOLAS/ըQ}ы%szvxcAGSkի#Y.5v#]vabN.]fcǶ.R_ŋ'}QQFK~;гg {.k0~:>kvs+|32+ 3;7ݫןRSgɄ|O~wN }募+)*otIzQ\6`?|@__]R%F [h*W}eoգzEc7oVxj׎˹_3?OM΢s/Lg:}{J|ٓ(OzeNҶmN.Xr8է\/$fGn|ѣSOݷeKR׮g~A]ƍժUSn[gl  /5>~[̼΃};Ogא_GD~WpgtoZ~ ^<يs__?WR舝>#^ڽ{O[e ]A8y0Yqz|Kүĉ[=}=%'''wbeVzW~ϼ䒨^{\޷otX휜#2f{WN]y*e i[Zj6oˇ}{!wIߕe}'UR[Q3:E.6,曯7c5v#~+_zQQxඃҗ ]An_vRKx]E>{$;/_!// A,姯Gz:vl֍7vWϚM7uҥ]uSIKW5o+[ϛ4#,,L?w?zI3:pΦMoْ$_w~% #| \8˥B${%~O+W7 [3SuOO|05=ѣRIKe @gݺdU{d3;wSI[^ѹI[yyԉ(MwoWkzhPf?'T]atĤ4jCҺVeK*'>/*t_B`w)KtVEGi۶[p$6U[72={2OܢZv˕6ic S;==RUN~>/\* y㧒=ng}}vuWTҦe[ѣq#Ҭͷ€+ x~a|<~|ڵkk lIE@!~BW($U6 l(OR7~ԦM Pf6͛0[$u6,,,"V>]e׻[NH.߃lfdd IRU7}}Vn5y;\ݛ?\zu֭;JfoҬ$ΔQ_^-|Lge'2G6WkmժYZ5M% -kШ TdO!8PFy?p0_|.f?I3G8p 0W>}:5,u5'6=j/[=wSe[PM_@W7?_V_?M)^A@ ~B7}& i_ OrG|hrQJ6 /:P'Nƃ;i>w()e$x AKަ+U@'PB {%%3EI}O ' 쒺YKԾkڮ7 wMfH|eTҸʫWg++ٓ*du~L;Pg\gUI@ ~BO(MnWr ׼*alpjjUMrrrRRRbʦed75{%JIO\3ۗFݺ'&O@(DxQVf%,zwk;)_p8&33bV:٨lZv@v#X'U(6};O/q0I@0!~ByD (~QXm&Pn/p86mMʦed78{%J,s>G :[mE!iVRR㓒,”_' 5.UZC=^7//UFeӲA=`C;evwThAfLxјnvj Qٽk?(? 6]Kܲڡ?}FDPV_{F*b4M&ju:yyy' DFQ=ШP 6"ט-Kߟ#r{8{zk ]e(o 6l]zi2B~pl *9םpE!7H3J͞Q%++t OT_p^Z{o^XpF{PP;f..`bV4vڷ,{frZۊcsUwCkP _͝y)XKNWoϱ?%8Nty,EQ؝O'"8}]^8!^+?8V{1sqQZqԿDrv{teKkgYҚ z.?Z$%%/X03.i\l3?~MӄYs̓ 87xE;?xK[8fJNN6Od0 { ?˙}F5͂c:u`Y2]3Uʗ@9,Ţ *@'i\bN7]Ϭh9ra@@!~*CaG'Ŗa#ߨ:3#- zCƒ1S5QZqv;NfZSSS}7$%%%)nmjrBɕS,'ZNt92d=dʐ3|F/ޘԁ*C1SeQZqHz^rNn_fjLOOX,Aa4Ct䷴ AMNV9rD間.`DT/g&uXJ_?}Ҥ媡[7?3UJ r(am6ۚ GhP9rr}t'P_5l6RKT3Jl3CճjQ!>J+v]A*ט!iGr! %r t?kÝcF=̥!xmϘ.ʣ\}d} X?]rEk,+Uz6ɡK5j:z71fQ(E#~A @ 5o W_Au9c}O6nQ.:fJ?}͛7)ꂷ{5g_, Q@PQˉCk%n:-X0N?/Lf˥=njJ?}Gk+.xU$~ Q@PQ%}ܹNr ԫWG>9Vp͚͚Evu|=ddW"/xݷFE51⮳g7/ׯg:thpkE.;)5v#]vlҤa1O37u$=w:'+/kH}E5/#'6vT xdiis 9117FF6|zlHV2y=ozӦxV}WD74lX_VOKLN]֢)~wF).:'􈟀R(Ak >}m=:?}[$E/Ȝ={2[j÷n59-<ƲeoFF6+gJԏ/)aݺnԿ\_~y{mѴiU/^wR,^VM5u6kl.B¾ɓ[ڶm٩[GONLDDD- -4)|͞7-hСOeNvɮn3gʸ[΃3w?d'KA @#~*J٣^{\޷o\/[vNN҈w r:_[HJ|+Q?:ݪUy^5OޕMΝ6,F\ؗjW_9e325*v`Z 3V隥J?S21iSENw˴۽RwH_aMK8۷[zuNK~z[:tӰ0kK.|(EUCPQ%B^۪ueW^:`xDٴq|Mݺfvr֬e.]կ_j,_>OjРޢEJ+/k̨gs^k)H+'2)JH*MctK͘1YH~)iFXXo?\CQ ߝQ ='\@`.^Q)j׮u\Ktxx%KfD^޺:u"+~ȈM_|}T 0٥=:.쥎4jCҺVeK/1r|i_3?tѹ:thӳgZmkR} 3T?v|E)S5݋؈@#~*D r޺u!Cnk'.Coٳ>g}EryO?>*ѓO{mҷk;)k׎p24mx̘a2S)~Ų½{-B¾ƌlժYtmx߾UGogenZJFQ^*(pةM]wzYI6-{Ւ[Қ֯_'~RmٟR⧀k @#~*J͛=[$u6,,,"V>]ՙfdd yW^ukƎ}4+x{pV-ʦk =a-Iٱn: .]==r%ߪUZjFE58Iu%eK/1qGmڴ3"gsG|KzԦYR3Jqq?GT_'KV,7eo΅}AS!8JQ^NM'V @T ^;_ReޞS|asacfϯ龼I?Ʉ+@GT k1%+U?KRU9JQ. ~.mz9O̗VP=i9WTUxfҨ[d]3fQ(Ey׉Kjtd}/~JoOkTsO@:{9/\m\(cq3Cž1S*dG)ˑ N%WZ[w5/%JXWhTsO@}nfL+UR-ضNw+))IIIfn FjQ!>JQ^V+-!zi䀕.{@GT3ǎ/k'=WTrM%fL&7vho0bT Q;NYb9 'U˿'j;TO@;}גvO߮_R*O3RQ {ZN3//T9coZ[2ED #~*C1cI[6?#A)Ku`[s/_~DIIINNry!3U(w[MLs?Y_ x._PX/kC u'-\~HgzUp(YYYNSa~~/LU+F)*ȑ δF`ETvڷ,{frZcY9*n;yѡE _͝yrJWoϱ?%V9&i2O%WU&!G/n*c .F)*AFX3`AT99Y{I7WUq\p]v(E)**lcǎ5(? Cvi#U@uFT_p8,zEbbzAR&S OҸچJ8ѐc"GF%ژ`Ua]0C(Eξw>uԭ[rYIL/G@ET*5MpNfYT߽III8~b66T" GC9>r Vc={v̘1:tX,6?*}׶TCO@e+Ux\.\5klff\%Gje)~:-mC%+Z9r4ȑ#GzU3* (Ey9{wSKn_~vb 8@!//ϗ)8lk*'s?T џ@PʷcUV@Ua\0.e̘1111eIzvm_TwO+3C mC%XcUv(:tg۷/--mܹ&MeZwW\q̙3yֈux]b>>W7B\QQQ=TXXbŊ9s;}!22rСSN̔V_OYꪫZr%fT_O@"@a!lnݺYbży.oEDD5Jb?O&MZvQU HPilqcǎ6m}fϞݬY3M?~Sx=ܣ(JVVx<' t0⮿ǴgyF;ٳ |ek&))bl6 Pz!~B* !K/x' t0j׮]PP8upmGZ_u_Y6""">>h4v0 #~B* !NsӬY7n͜H}eٖ-[sݾV ]$4 68ͻ,Yr7k3'?={dٮ]&$$$&&YrZ>' t0nĉ?f̘,_YG%~HPilq6[ns,YO(#Keٳg? ]$4 6=zX,ߜBgϞA1cƌ;6??Sڵ(O"~B* СÏ??sϞ=ӧO7nܤI^z饱cǦwٳE"~B* cƌ4hٳg %111fd7vT}O@"@an{^m􉉉۔sFc||dZN3//O@F.TڵkC=tWJOZ;t0b٬(Jrrw.,@A.Tx' :5+1=vʕ+MvK/5 [޽c=O&I}N͞V,(svt_IdBZcl^~~~^^VUQ_w9|3=",έO!~BKޥMoSSD@Ҫ]P 6 _p8,zSRRR(3I:H7L/' \Z[%RKlUmWa v:6j[,T?QfJtnҙ T_O@dnP¢ӛ޾;.E1DX9G68]?$??z<t:vf[G>LiM:= EF3Z,m<@1D[3.m'<0؀R!T^^q.?QfJO@(}nbl=D5֠Woh; EhZRf `@qF68' D{'tQ{Ql@#~Bג(e-k`B6b}nUa?.7jMq[r Zl6رcoA:*A:ȴ̑Ҫ]/&MUZǽ{t.+' dgg_wuݻw:u֭[~{׬$We̗V] ̱۟e-n;O*T'n{EWT}? wƾiy25b0. ~BٳgnjӡCŢm#GzJm*1 /*AU*8y7FD9BΞ={wx^mαcn9g׮]GvB99.7+L}AU~ᑊ!z;!~ߘ1cbbb~C馛~v򸄮'Ҩ']㦧`/ޘn;| ݡC}Ν;i$WeGnz̙rq ] άoK5ֵszbx|?> fEEE=zSaa+x|I&.{fϞ-311]v+W\B;T?#A^PAPS{x?' lnݺ|#"" ըQYfX¿s&MdZ.˫Pg2ܹp}lAGmLk/' ~رcMo߾''U|M^z+t:=b_j}0ۤ-]sfYNG ?}裏d6vv%%%Y,r~.9NZb`Qa> m6 p' ]zr8yƍk3'?fe4io2V\?Zq3|> ~,mj׮]PP믇k3'?:e_YVZF1==nY0G+ *jJX? @#~ &~˲M6ݾv}0]n7e>ƅrߜG G3ͻx㍞={j3'?7p/v1!!!11155u͚5.׊ sd㟦ӊ?^3g/&N|rV~~eɓ[/O>RZ`Iuҫ3p∟`w۷oKw2nʔ)wy'S:.W1DZӊ?^\~ƌV\G˷7oRV/ئMg}ʷJߟUrq ?fuδiy}%sFꫯpWtSUv _j^Eaժw}KM>r۶Տr%ӲeSٟIsWN]L۷`0VrrdmC쿔ZjSzOoj`ٺf)?]\9_8S㧀ʲ9NTO  vu=Pzھ}{W͞+X(]%KfD^޺:u"c5thXgO:s+iyaaap=ztY^ u,}RgI:~|ӨQIYT㧌8YտvgRO%P?eDt:ϟv}ԩ[nIi#)Sf5{J8GdqYI~~v?oٳ>g} _tݷ;aǎ6mZޫE1_O ˥c jGk׎p24mX5=ĉͭ[72={2O0!R=7]8<޽ր܊DQIV'=zԶt[~ROwP?eDvʕ+zÇwK/kr[n_cccsL&̝=YVYP֧rqdS1DwHJIDAT!իSnQ  >* A}#k#wtNލݺu+s|򦬰Aza˖""jɚѼy5j {Z5UfTTTW_&%;p[-Z4Uլ>zjZ俔G5~Ed>iS E=,5ws,fWg? ?!!???//O.}vjU%)))W]N*LfO,(sSr+)iyORﻑt>qbѣ6J|?p 0W>}7k>Ǐo//}π뗕8λ셕leWƅrߜGG _p8,zSRRR(3I:H7LT W _ѧ)33^ ?$~@#~B@ni٬VkzzbI#e4I&ɞݲsҧʕ[OUZ71sA OqO@hzG.NnlkG)MAIgre-UplV_#' !T^^q.?QfJSٗ 4k,9?OP dsm̂ ָۘ!EQ[,vOp* ?VPɩ$IN29qr$ʩJBUD~~x<.t5kXtŒZ'S'm $'KN89}rT %{~!~*E |96mMuC's?6TrjiNrOPEGQnU3C mC' (^6O G2'@8u 7[7'@fq/oO2! ?*D^KV˖6()'@Et>Vok>I@"~T#JXtjč_NV џc\v()Ԛ}_sX]vԈ諹z7+hV]8E @P#~TK)q^:'@tJX/.^: '@:nw @"~TCtwi';~uբ6b4e}S,9Gk8 EɁOLh[rTղ<꾔‚3 ~P֬(hK9E*բ/Oԋ򯾩 د<:zYr3ᚯY{J TuE{W1Dh?ĝܢT?\뇟f('Ҋ 7b}U ?#aiKܒkx<^7??_{ _YDʬ]IסKܒcpyyy$P@?@u({k}O#_֧!TIϺHm5]. PFOPU䙻SG暽n'ʈ +7+hq>.bx~Rײ$P@?@x2viԭg"֩RmZzړTQ?>.z}zK)t:=7@ ~̱m]Uc^IX,6rqP '܊!zob}A]r'-fdZN3//O{  #!)ĉOnjU誈]=m јn=TE*Ck}{K>hr\Uz7o_rVE쪜9;LOJJRsSWOPU[N ϿU_uM~ﮖK&&&YriO!_?@UT/ըQf"ccGɜcڵc& 9}:G%lР^m.|Mֹ~:ݳgѣY3\Vޱc_iߪNWΠA}Y n:I{ T|+l=ncOe\a黪9|e"~~'?+,eWǰ_OBUyU5 MO\}S<~O|Rݞv|Űa1E'h׮;B߄}Vɪd#2f>5*v`pY֣GiӞ2] x KE.OP7~Zx`ػ?s֬o{.ׯ[R.{&"E?OUuVo,^OQ |}EjcXW/'w!~ʕ o'z\R' _juu?,-yyԉOqq/oVYܟJ 7ߟp\lN[o[NnT]aQ?E%e"~~'?8Ufڶ}V9}4v+#-mNӦnjO?>*ѓO{mҷk_|onA}CΞݮٺ*ه֭r='OnQÝ!ǏoZvlq^kekGJy+,eWǰ_OBUё N}}ᫍ?jӦ`Y3|Gލݺu]{{|5oD |V-ɂG?F-珫WNݺccGEF6T߈(otdj˖ʪ""jU܍{Z5UfTTTW_ֿ^|qx)SWXʮaQ_ٿfWg?eAUw[1DMY>4dWq8]Xﯖ4ߟ`ѹԩWO>F?I3G YOA˖.`[5_m\(g'yO@Y?@UTpb*n>:z^CS2S߳jJX?eAUԲrg!XMTfՠ',ytҲJ~(UplV_#2"~*j_⷏gk$uqkלYr^3!~ʈ ‚3m\>.VmDSzp,ﴄC<uߟÝ?y]w]޳gOMMSėSVnyESU%E%Pǎo=h?rȎ;+\'EUUUwyg\\5\Ӿ}{r)Fcűj?O<)Ł_jÆ +DΞ|>˫'r8~{-zٶm'N{:M6=zf͒C@y_}UĔ۷bE);t]vɍ_GyWOyiii@CDHw555۶m;?9Ύ;jZx Ѹyfj_ƌya=%'P%^r***QUUxnqzI,od}ӦM͛7\^ƍ7H/$z'*vG=Klh߹sܨv#~Pd47o@ȑ_ݿɓ'ˍc˖-155cǎa=%'Pk3OŢ}'ލš5kDu<-Ǐ/y][s̙?g? L?@({"##SRRN}޽x^K]JGWO8lOrNٿk\šK/9\0{mfҤ N3gy4?f\\LUVuTQ96…S5{uzѻw'X{뗿Lߴiu ϋ<϶mƓ'7>]&ug iϞ]lY~%'PG\Wh_nߚ2^+7=:o]s%It:EQ"##5{7~}=Cԩ2uTTnLLb8WpJ}D5k|[iA&M}QZXt,U\A>ݴI+v_gJHV%%B!ǹ 98j~N^h|IcǾ|eӦ);{Zm$ݾ}[y7ntΉrr޸`yԇhlwW~OԞ=&Y>/'%I^韵_.\,)S/ck޹p/@ BΡrItx}b~N|Ux鲘VSbN*5^9%' F K֬jkZCs6;wt|'~!n/vZ]gOr7ꉜIm6+G@ ~s 9)rrhٲyV-bc[{tشiw 4n-r1kWĘmtx/ uu~>O9Q\~yEȥӽkHۊY*}jN\YQr$N+**ZO@"~sO|E\,Sgl9psZ]]5h4fmص:*(; @9guxʟڳdXszdl.KWVGeG?; _J LyrfgK:]X,ө\+wmƌ>H~i; @9TZ.>\'?7кEҝ ;^jr޵nzN~i; @عAFbbOUvhS]n+WJ.?]^Î#~v 2 ՜9/|G64` ͛7-=`ddD\\LW.oo׻p"t)w9,6QUU|t~-[6OHRvIQ&=ܧOm<ӧFbw&볂vk >Ucyw-1Q,BNz')k; @3ȸȈ̉[,}znڤӧY/ݻ:t{ 7ΜxŊܘV:[ȧ/4Mi>_{e?y#nذ7|g,9PP\@Lq͸y&ڵl<\]m5k2cFFn5f̐[s剟+ U8?'a kb_qxO4͊sʴ>z]^?+qm"ĉ:Ľ˞|K[,rc^]zhz֢ESk%:}6mڟqE7 Jն s$7w7thsg5< \;k*vgṁE|/Rv'gyvvI1`)=_z1WW̪헜9y1w-Z4)+W+fljj>+(av]z...FY5,ws<{ZÎ#~v 23 }Fu^1k͛,GD4^t8pJ6VS?y|z\F۷'u>ݷ*(av]⠴cw.{"OgYpOŽAwfqo?~w< F}aȐDF>~OGmw=j^MD˖n׮MJCQ\S;źu 1[[%sjۅ;咟 ^9}-=]۷/"k*v~ڷolڤ޽sF#7J11Zlvox͛6kdgEc^^"ާݺ{u0޿v###{ARKLhz㿑gCS<)_D=:ْsakY ;5p5vnm?|.̜'G|X*vs|w⳺CccǾn9}С۽ԩ`|Vw0y+\ԟ:j ˮiXJ+; @row"ozhk#@݈5i O"cv՟öN__*;ܐ5_N:rbVTT(W>u#~nZ+?ʕnSmӧ+뤠Ծsf[ L-sgC6D F(דtx%M^o ] u#~***\.nX,EEEK2#%M%ucyiއ$zB999XX1nbMZP7';0L⃽V/93զN{m֧͔g2 P=*i lTI?7bB$~Ď>?Ontvb]V|/**Fc~&);ʎWk u!DXJtkG eG5'rrVvx]8Ěl] 5?@C ar]I2P@6Ѐ#0=j;?@9TZ.i/Wv dG $ ^ {\' \X2ц8 aKC!sgxGΕ!zppi TǦG|x}'pܐ9lBr(=.ßۤFIK[ 5oIڞcbh,U#T|˒&!~ ۷󞪫,X0iҤ~h4S.]I=˖-KHHXzbiA33 ڊԱܲe=l@Ѐ?@D L0!::ZDSO=|rz0LVpql^CĠ/MP,IT>h( &MozNw%ZٓLO2[xcƌ$f9|1ԁ@ dUٺN<܃qq1ݻw^x֯_yꋈӅ y֏>zGU֋}QQ>_H{+1yݷ]>F1UbXN?¤Iӽm6=42WYO '}FOKuu8MKK4hPLLF?G[oZJzFJԭ[7*&622"3s-i߾z귛6i>k e^|qͺ3gDD4^b<7&N" /_v4_싊Reqq1M޵kWܛn5f̐[sUw (.^aâH9} $!!nܻw$IYYYoe˼s':uJKK^qxO4͊sʴ>z]^?+:R\D>8~Cw}pUPE7:ӧ~nӦI &%%eUUUʎZC7n` ?y$߬Խ{gnFݟ9Jާݺ]3~|+s[li5j\+9[nw-Z4'?_"~@`?@TUU 6lԨQn[٧" :_~K,$Ieggzd\.rBHdz_lݪWy&.%\ҦM/?~E}cǾx5jo_Ϲݻ^?ʕ߰۷'~k+'ӎ;.],[LEN:7NΞyg B%OǏoߺ{wɓ<Сx`NO?}W_ Ih4?:rdebݺ1[QEfO_9UО=⢢"cc[Ods33'6>gWo31Fs7uu%ײP nZW3fLBBB\\F?w]vZptK>_H =~|ѣfŰ:J}#/.|uuYPlGn\'+' %>[,$IV|(ٓ"& 'Thඬ,uBvΛ-ir'ZFQIzQtb'5{\P .='' ONf3&h4{Qtb'WϏRFuBW\;bŨ'P߈ $TTTn)>l6b6zQtb'&i4 l(ы0AF!D\.t8v/T4m>ېNh#&++iGR޹o;=kN ~'ZFQ='77WJdSB6uIҦf*;B2Uk+VXXmdO'@ lfd2{j!FjIҽ:]z5+)SX[bj5'{@?v;Nn,ڵkM&SQQQAAh1צIʟ#k(VRXUb:kN ~ 9r\jZ,ټ6|:/&9+;BX=b%=Xa' yGQbu&˼BeGG"~*-4Iח+;?S]cu ~OޤāP^K1v9yOKʖ!zG֏-ח{⧃%eQCNW'uIZjyKwo ?pjF.'VEF%ab0p  lfd2|/T4.1@ ɞOv;Nn,l^EF%ab0p "P.t:q*E Q, IENDB`kea-2.0.2/doc/sphinx/uml/currentHost4.svg0000644000175000017500000007410614206773363015217 00000000000000currentHost DHCPv4 (Kea 1.8.0)Subnet SelectionSet subnet to the selected subnetHas client a lease for its client id?Iterate on allowed subnets for client idSet subnet to the by client id leaseHas client a matching lease?Iterate on allowed subnets for hardware addressSet subnet to the matching leaseHas an address reservation?Set subnet to address reservation subnetIs the address in an allowed pool?Iterate on allowed subnets with poolSet subnet to address pool subnetAllocate a new leaseIterate on allowed subnetsSet subnet to allocated lease subnetGet subnet host reservation modeIs subnet host reservation mode global?Get global host reservationGet subnet host reservationReturn current host reservationno client id optionmatch-client-id is false or no leasefound a leasenot found by client id, try by hardware addressno hardware addressno lease or client id mismatchfound a leasenot foundyesno or not check in the taken branchpool check is not in all branchesaddress not in an allowed pooladdress in an allowed poolno allowed poolstart from preferred (last used) subnetallocation is not in all branchesno free addressfound a free addressyesnoreturn global host reservationreturn subnet host reservationOnly the initial lookup is always performed: other occasions to change the subnet so the current host are only in some brancheskea-2.0.2/doc/sphinx/uml/tkey.png0000644000175000017500000002553114206773363013552 00000000000000PNG  IHDR3|Q)tEXtcopyleftGenerated by http://plantuml.com09-iTXtplantumlx]O@?L8bi%Pk;e)ƏxyΜWևC=oe{WT# R y1k١K*_4o1 tu7%_7-t':~r隰:՘!Ubz|iC8_ d C8Γ,Ӈbج`KJ;os0:*x xht: yֿbt=ca)xvT'-nGbşTHbn8~&'V{r]:)IDATx^] | W%Ak])>*}j+mmEJ)"*$*$Hl)R[,% ?83w$v297w9seK`P!) `De BFP!) `De BFP!) `De BFP xQbbÇOl 'P^D&PSѣGΝ;^*ʕ+p&$$)aZcccqp cǎ!ٳg|j|h:`XF'1cƄKoߎZpbŊ}]vMDvکSm۶SAKIU͜Ƚp jNRE^PSf, Y`X˗//^4PokbРA9r_72n89uT ]K ͛<܎NR͛7#믿AMhGի3r?ԩw}ooo/!u͚5,>99bŊr:y$sJ!N1: y8}t?ׯ_AС×_~I1uEL޽9r̙+WvTh3˕vphӦMܹ?'h2 E%P*c[q:j(y WdH-]4TM'駟73 рNɒ%K(q=ٳqСC/[n!c4Gڡgf)S( T:,ܹsF 2}}}qܺuknR6ХJBɓqalٲ6֩bӬY ڵ9/]Ըq.]0ֽcǎʛ7/\^1Uy"E`E]I~̙;wƊ+7YQ46.l{wq˅ F0TܼyO>oѢ[*&ٳg3O%Q<|! (U;9ejE8?0kUT ))I{LN:Mi8p b~Wݔ~zW_}ݻ1B"!"IHHhڴ)3oZX1,{Rm3y˖-ቈ|@XSۯ n0y[ 2gΜ+W؀E]hߵjB4_~ Lq4l4r x뭷(SJ2eп2Erk!~ԙOsz25ގ`ݠ~`,u-Reb6š"[9i$S Q&V=<WH xIV 1 -ڋ?VRJ׮]cN5obgĈ8r0àTu.04Ǎ& sÀE+W} cV/"<>Q&5kZlƨ 0`d^;wn+߾}bIN`ٲetuΜ9^^^dk3 U& O .dh/%JE$* ӦM?a>>>Hׯ9n ̹}fnj}->- o0 yUVEOkAAAH=[nѢ)3zޏ!L Fh&^:XP'ANdI@mR*m5*3նo,լ|&M ~̃MIvZm'ؠj .sNT \'0L@`64O: `E`g\mƂ +Vԡ2Sm_΢jHDQzB'~?h xN'vPyxvءC?/a>ЩS' ;_~rXj:u ]bbHWbҮ-ASC8̜SQC.uS,EiTmlAY|gx' 29rJc"cW_2Q LSC:XГlTN:zh,J\tҊu֓&Mw.x\ɐfES-ߚ7oh"i޽H>pCR3 HwȇfW=Auaܹk֬Xt}a9EhႊF *Q ..NcWH"ѳgе ,(2Zaڮ];Ώcy%4Ŷ{G/#}dAԩ ϟ?q jgƍhlÉ(77Pl`q kn {G@$N]Ϟ=?N=9z1c0BX8"ׯ_ܹ3M}er}D?HkԨA9\]]Qs/B-X*NCBBZTP33aô"-\8qB4VZGaEP.]4= B_|C2M N,O2˜q?<~@4S&ږ4zy,S2iMfׯӗNkժ qp![H+WLH='~AڱyF3J;NOH{3=HLʝ;עE_Ι LS[ Re޽Ӛ%y~-j^~uS&Z7^ϙ3틘?*j%/`h_@IX0} %w1KLRVN*22E䗓M6̒b}JOUjG[9cunޛ˥/sƁþB")j[IZVLLm;O...kΒoV~uS&nT4Cw7٫WK?'[K8˅02Q2OLFE _+^^iBMkeUjٳGmI. L04%}=$;ujh^.s2S2_l , ;vl3U18fKy.[)؈c'k|b== )]ثU\ZXj/ Re޸pT)_tq|Lm_:-S1R<@#GV! u낶mxٖ/_jX,\8;XRjݺUq:gP8 RZ 戈bŊth))GΝ_c!jk_,e+Jr}327T ]-Ǐ S$e ͜q  e:e8}L_~2g2gA/B3}3D ӗ_L百o˯PL_7W(y/sƁd>eC2f 3D ӗ)SXM/7Zރ{c"Y=af٪ ]3DxtW2(2Ƅea0 C.0&Dԉߌ(tLL ƨ*~(cBP͈21 Hyo}]͈2QF5Ya~`@Іhɔ}2au7#48Y.lӰ/)`*}LS4Py@i72LL#B(S@0"LL<Lþ ii0LþP`2LӰoBe@`ML )sĉ(fڶm%''#2""K#3c 8nݺW<2?^6/_>t \tRJ}ݻlٲ7o1fk׮={bپ};'%%)R?#6^,lL~U\Yaʜ3gѣGȔhzO>AVci=XVޕ~hɤqƸG+`Be:|`OwΙ3g``) tRK9r䈊N:M4 Ӭ .^ئMh խZʛ7ojꫯ6mpB7o+ftfyx>SWW իWSXC LL٣G^ziĈI&+lR,^xڵ}]IߨQ<|!b_DTWNܹڮUN,YԩS)?L28x<E2dHBB{f͚:tILL|^ge ,X d_Tv6Ӈ> a^~$o@r:꫃; LLիW"xBv^:d?@(3 P XRb5yf_%`{ٳgOPxݺuRŊ?dž H]ǎT$զXZ[oSTLS@(3 cǎ8p)6>T4-_ɓpnܸN-Z$UĉɏZ:grpt e{/Fe8d?t 2q=a */[nCKڕ///l?CJJ V.]lٲM64 bl޼93f LL˗/ߺusСTb߾}qw6 *02BE@bb"m/^ua+f͚҇I >>>… 7o^8Q'|”bŊ!UVȄɽ{*TSWW֭[]\\r}lVG[病=;ϊȎpO=zDt <~Hy&?я0҇ͽ^j}[ifP!)S@hk=]k)T(8z)Sx (T lxS ig q4[*K:}* V1^N|jbXER2Cݚ1+i_E4 BY $MZiҥ/HOZӰ/)eYnל$9}*4,7iY=n`MLA|H@@0"2*42ӰoBe湹@`2LӰ/)`*}LS4Py }*Sdܹs'>>>!ě´_ep-ǜ9s֫/9< MaFJJJʕq|sx{{Lwss;wa.ePK),P4= At鏘]t#*3)o .7WT/j>6mBq4K?Lh9O3WKMMSp8q"Ne"/[҅bKWy]ƌ3XLoePTj%_R0bUL9ƒs R2W [$//*)K)S^%U^PZ&72y(*o #Xe/^Ho I "ʕ+qK*\˗/#0JX! ԕXҮ^cYO$Ɓ g7{.6ئ[C MӦMW\ިQW^y*@1sjЅ /bq 6fS;3?~Uêdt{K۵k;w()2ɾq`Be:ģ_|`2S0{/B[nѢEteHը]6S_,h޼yÁ ?fH}m{9HPգG_}ѓ'O1JvJ.Mv1]hVd/ewǖm*޿@Ɓ|ф3|a9A9A^ )N:x`:J^ƀ}721I~V-6!]P!] SûлwolǍ?Az" >5L̴IŰMֹ4 pG+K`Gdd/6`' 8 ﻜMi"$g.K-O pC7Ґd),[!&̩L̙gZ[X6=l*Maz'F}3F4.:pP)}= qi(izernmh&B&, *SM,O2˜q?<~ 7LLJ*T,PΝ}Lc2g8*$"Y|dbx0I>yrӳX5:z4xoӗ_}<ǎ} x,&О ֬ ;MHVrYWc!۷B b)ꥫ$y L`˖I_󔛼ɩW P'˯ʼw@ŊW(%a8th"E<İ+Wk#bE+yTsְa=HL*y&إt"aiVvӧÂʔ)>~|eر*^C\Me8p7THDjݺUsʉC 2ccqv)52[lУGm4i.ùs?񩇢?Y147zÊiVy]Kx g>ꚇ-vmQ^5s5ї9aP!wF;͔c6;uj~YW7eҚͮ_߅3/֪U7B4+* +W$z&N.c3cu>fZs;vl-8Ĩܹs-Z4^˜qoH;*x.Uݻ14|_3_ݔys̱}"揊Z;/Ye ( /"fɒIIE&XS_ߡ(4 `riӆ!Ob}JOUjG[9cunޛ˥/sƁþB")j[IUzTVv]\\֮%߬L3oSRi4""-nWvK~Nzcىq ad-Fe<6ȍًN90.Vj!!PMkeUjٳGmI. L04)}CLS6a2'3%z*jkAe˖jncf\<=X c c粕8vXo)#0b5)iZYʕHmP.*sR\j2g8*$Jy… Zl?wR|9'32<~ׯNcHy`-;Y2֭ ڶm{*f[|ݻbpxoo/DbJuVAd4H k!,#"c+A{iz8Q5t]L_}CD2a˗Oa=a€u7ox8genb~Qf&[nN,[޽i.#NZsgb-ϖx΄F5E S*!\`Z =CX? 2>aS ft]L_}Q$hIv CK;Fe_*UСG-%ȹs+ub,DXm틅[ cwZi^/sA&K?aJ~LaV3D ӗ_L百o˯PL_7W(y/sƁd+<ӗ9@ 2}t˜q  e:e8}L_~ӧ~(^Xƌ{sƁd>e ˼Ŝq W2{pso,YP9' ?>[ sƁ/NjUEۘ}y~3̸8 aaa(>P Ƅ:ebAB]eLQ&u@ 7t7#Da P*gZmLII7+C:e !) `De BFP!) `D?IsRlIENDB`kea-2.0.2/doc/sphinx/uml/assign-lease4.svg0000644000175000017500000005577714206773363015267 00000000000000DHCPv4 Assign Lease (Kea 1.8.0)INIT-REBOOT stateLease allocatedGet existing lease by client idGet existing lease by hardware addressGet authoritativeupdate DDNSSend ACKCheck Subnetentry pointGet server idGet hintGet hardware address and client idProcess hostnameRequest leaseNo lease allocatedexit pointSend NAKexit pointNo responseexit pointno subnetuse requested address optionuse client addressno hintrequested address and no server idhas a client idno client idfoundnot foundnot authoritative and no owned leaseowned lease with hint mismatchauthoritative and no owned leaseother caseslease allocatedno lease allocatedkea-2.0.2/doc/sphinx/uml/packet4.svg0000644000175000017500000011125314206773363014141 00000000000000DHCPv4 packet processing (Kea 1.8.0)Process Query on its Message TypeProcess DiscoverProcess RequestProcess ReleaseProcess DeclineProcess InformReceive queryinputService EnabledCallout buffer4_receivehookUnpack queryClassify queryCallout pkt4_receivehookCheck DROP classAvoid same client race in multi-threaded modepostpone processing or dropCallout leases4_committedhookParkCallout pkt4_sendSend responsePack responseCallout buffer4_sendoutputDrop packeterrorservice is enabledservice is disabledCONTINUESKIPDROPon errorCONTINUEDROPquery in DROP classqueries from the same client possible raceunknown message typeon erroron erroron erroron erroron errorCONTINUEDROPPARKunparkCONTINUESKIPDROPCONTINUEDROPdashed arrow means asynchronous processing kea-2.0.2/doc/sphinx/uml/main-loop.svg0000644000175000017500000003441314206773363014503 00000000000000DHCP server main loop (Kea 1.8.0)Main LoopEvent LoopWait for next eventCheck ShutdownTimeoutSignalHandle SignalExternal SocketHandle External SocketDHCP QueryProcess QueryI/O ServiceExecute ready handlerget next eventtimeout expiredno ready handlerdashed arrow means prioritykea-2.0.2/doc/sphinx/uml/appendRequestedVendorOptions.svg0000644000175000017500000006720714206773363020502 00000000000000Append vendor requested options algorithm (Kea 1.8.0)get vendor id from query vivso optionget vendor id from response vivso optionreturnnovendor idyesget configured option listget option request list (ORO) from query DOCSIS vendor optionget configured options in vendor id spacepush back option code to OROfor each persistent optionfor each item from configured option listcreate vivso option for vendor idnoresponse vivso optionyesadded = falseget configured options in vendor id spaceadd sub-option to vivso optionadded = truefirstfoundnot found or already foundfor each item from configured option listyessub-option is not set in vivso optionnoadd vivso option in responsenovivso option in responseyesyesaddednofor each code in OROkea-2.0.2/doc/sphinx/uml/select4.svg0000644000175000017500000010423614206773363014154 00000000000000DHCPv4 subnet selection (Kea 1.8.0)RelayedSet address for lookupTry incoming interfaceTry addressRelay address matches subnetRelay address matches shared networkCheck client classSet relay addressSet client addressSet source addressInterface matches subnetInterface matches shared networkCheck client classSet interface addressCheck subnet prefixCheck client classEntry pointTry RAI link selectTry subnet selection optionFound a subnetFound no subnetCallout subnet4_selectReturn a subnetReturn no subnetDrop queryrelayednot relayedno matchmatchno matchmatchcompatiblenot compatibletry the relay addresshas no relay addresshas no client addressno suitable addresshas a relay addresshas a client addresshas source address and use unicastno interfacematchno matchmarchno matchcompatiblenot compatiblehas an interface addressno interface addressmatchno matchcompatiblenot compatibleCONTINUE and subnet setSKIP or subnet not setDROPkea-2.0.2/doc/sphinx/uml/lease-states.png0000644000175000017500000014441614206773363015174 00000000000000PNG  IHDRv;a)tEXtcopyleftGenerated by http://plantuml.com09iTXtplantumlxuKO1MOpXMb)t,Ǫ鲋M7Lu8N[Ŝt)B"мC [N kxBBA)BZ A*pHp/+PrаI{+'k|4*7Dp.L5 d8DQt (dB 8qh 6><:W 9XT{) 궽F3I C#p o Yh7Y6:/U%^ޥ^(OrO)hl8~w+7B0>}:j߿S׮]ḁ5k׉D@"kdd^~=YjLR% {ճgό *®ز+V~a] 'bǎ -Ahbb"JJJ?\\\3X>@d֭(d-[ YAC!8r5Iye]P)YvŞ?̙y7YJ\ ./^F-,,`ҥKP)4x+ݢE j^^پ}; bAabSRRP4 =))ոiӦrrrw3f r8VCC"-[@b d𚕚xA՜ IwWrJh 2+RvF 6F"b {rÇC.K O'''ȑ#!>n݂Qeee6"TbzPԩS0 R111;Hv*l޼F_~M׳;u]N@K+n`k <|P'p(>@/_MA+++j!fo B*'vF *&:͛[do޼ &L af[wV\}I@t$ħmrA֙򟊥T866wX#XȤ'Nؼy}$7>{lii)'۷f<Q?~<{*PH=،^Cڃ80{#FlRmAo-ZĺTU9_$vO@HM4iѢEYY+H)ĉ~Fź]#X9W>p|pɦM„6m" @AIIIEGGKT$mD@"bSSS%*rWX<j;vt۷o7:uC} YseyeD.'}6/a`]=z4 Ϛ5JjFPk׮eElll`pyl֖=8h YQ{{{9s&{*PH=D1ח! j׮< VZU˖-"|fYQ\nM㜤\.qv)))hQ۫W/.TFP.\"NMdy9„D`]ȫW$bzP;`tʕ2 H=|8q"DX/e5vss`;[g\3+'''9r$A㜤.'}B7oް"ī'<==۴i'O@ke0*`]j}DX.T0$iܸ Hҙ3gmihh""lbzPljjjϞ=!2}*֮]xb:9geeeb vyyyYY{{„ P$l93+S'fw;j_Ͽc\'%>$**QF/E|! 0C*¿׈+^^^+fìwDT$>>Qy̙ɓ'SLٳgSNBd…/^ݻ79pܸq۷g A T,ROHBBuDXW1-,,]&MV^K%={?N7uӦMf>zǸNRηK;ɺu력%5J= ÞwޥzhmmMEt5ŋ|az`% :u=q$}vpAHV$~= B*i``~ ,j8;;;((1ff&+rV+w$|Ŀ쀖zg@kTs`)fff>}Y̓t8P`Сرc"lbfwԔݻ|4 B*AGXX USRR(XAP R'bAN@" HEA:  u*AAT,  XAP R'bAN@" HEA:  u*AQK8KQF6]A T,L!1I8KG/XؐkO s  *AA)VV-<%N *A!̊5#,,,%%: *^JrP#̊p憾>X'>>: =y1)a?Euuu###AvA6-.>E 5K6e\ZZZȆ]GAbX} kYT, Ex wu8q` HAbG|`Zu,*Aj",kuZ=[ HAbB²XA *JHؽJK'aIN>緍3^R׿eQTT,ܼ9KWwg\2rd={WZ=a ˓'EEdJ:^ϖDSk+jbb]\6BC{$gW!fUmT{Jg[J=t߽>-+bLӷo5+RR٬ *Ah)v>7W/>&㜐OcAWXAgWtCeOF:fr6ug b`-0PV\B=w8{)-bf4YPB h؅ ?{V'f̘Qqcϒ%C:uj eٲ11녇KhѤ]YEe!.\9+ͮ]9'=gΠ-AGˉhvKqm 47,nn i͛7ӧݗ/;L^"#wϞ=UÆuY|(K\A6߾}ܦM;vlѯ_WSqJKVߩ{,tsߩ=n~jrs5H궗1nv>xESXm[gvWA>i^n߾tݺ{_'NlH^Ν;t?]^q^ Ɗ]|Ӧii-8ה)} XPplܸ`]e׆?YXx,<\qԨn vu9+ΖpΊ$P-X}޽zlĊ}_"ԩ}g_\ݺޱc\D"|&g)lXj+@~,r.s9PoܸXÇ22Asm\>J~8#l.ְnU2Km? 4luɎ?Q1ë>),5>WfS7گW84X$s@ YR%1њbr^]APڶm=hP>q>Ūe6uviͧj5~E!AkoVo|ut\_\ VgV\gKbV\'grN9!ƶk%~ׯd骸"/kiرɼ&g/>>[֏,PՆ}?z$ǫqq\%/pwW9P0+vNg1K4~}m,l+铋{ 6xᘟi2' U^fg3S(Ū-TTLV;i+qAQSlNHNkd;A֭RR̊lB̊$؍R73ULdnb> 6Y{^]epkԤIv'&ӧΝyMN,0^-Q庈_WWa] 3l0 :6v/s5JA5^uD9@CLKsg/x ]]]}}}333-XvoըQ xwY'{~Y+4VQ@ ߹3uf0G*Q1ݽRqe hߴic%I5[B̊$ӧn4j$ѩSK=|'l 5kDڷoqtС]?8p`hӢEYفqqxMN,ݻE`Ahouſ 63wءC *Eڸ}_ Z]Kmg5Mdsk/ݮST}R6`۷޽SSSSWWШBٴiQ&O ?ΫPYrM'~'Yk-NKyt uVVX, ˿$$(%%' ȥ qj|V gx% @:TK0Kn&p]m@ه qn39KWlS<|C MDuX)c*HV֭6m͚53g28ºJZ55/+N.쁖Gn.P4}vKiβ|wc%og} l"X,"_WA'L'$ ZLa!Xj)(p7s_nwRyg m4aa?O3}M"y"Ӿi5M x|]w׀rTT,J6PM@7:yjԺbk^823c9k 価4|w^qjfsIAl?kT+':AޠbеPe;YZU@r`0)))ԫyDNBˊK2'3(/TuJ B#_~ 5IZb7s7S^2TMS}1E.bеPR~0^B)WhX9Q-N 8zzm̬Uw3‹rD@bk *WHXA (TNhXw[qN^F@WCлvXxljV ˃! 2F2={91(X,t-b!YyzݠaYZ)VxȋOI#'-ܠanٖ6::$wLrJ?X,t-bj֫}ZHk5bfOQNAJ:h5ZSLﶟי\뺳A_hXy@fLN 6łԊbY|񰞲M SOyoAt$?)5]c3FT ?R@+5791P, ؕE --,bX[o_![+* ~-X!+4&1ϻNi]^ ]ykUVRJN bddN= W AbRtAyÞ:]ƒjTTQ}EM/l:|_Nˏ~ioB)&WUEObxnٟw^F}j6ϯX:PW#Fd_ >eG~%AzfP kD%~HIiYᏴɨ| Rk.j&Ό=H)6-QqkR" r-T,J }BOs zi7Zra| &A:f4v%"7 ï~T~\֜Nr//+#D'wQg^eRpTf~ yUuYc-KiLLv # A>x%ze6ն-s\?Z q\} }g>Q[9\{)foZT+4_ lި\~^pa*dN3ڪy4wJIn~gpKCwleuτ5s4.R=Pv~ZOުV_d3vLF[juZe:yUZ?^̋*]FJFwmi7xbqQc4*%E ξXMZ; Di 0͞˺ۘzEv`ΪaNzd#6AQ-9`\ߖ:-?|];<-HIO =,C$:[}w|eiPlCJŸT{ 36JS=XD;o^P2#r R053h_3}h^_&.-CnuN#3k ~$i6a%'|C#uMF`d9FzM/s^q"Z{QfWZe%;ն 8m9 9j&o1vݧA> Cn c-)~w VZ}BTI@ A)kEC1ĬF=°m,33ULLF"J+ p$ ˯3a>|VX_=wqЕ.jmh{n@4L`V4"/>%=0N>쁖߉Gi ҴO51vϻRj̨zuY86" (G5jKjk׆]~@h0BT^vN 'nvD ffhZ {͔aefeAH /+9(D]Ew: Nz'Z?2seVaeHՆJ⡤zQt`qOtaoCg@Ira^qQ7\UaTqv%Q!r[3l(|GԀc_B;-=b8Ąc_dSD$ołDaG Vn5iXm[1j)Jb&%ZcenalKC^[Ŧ8c:deqz&/Z㺦(=yE@V ^m8gw!k_?Y@PW͍MrY ? bsw@~?ΫdW24>=aA^7OO+|-}^xY7a"HSV{/xS,P&x{`1f-B\֜zcᄐZVlVHr: Ot% ߉G^{oQI~w3zfHm:# l4wf 8R@?cyN 9 ҔxR pP}!+.w3]4^C_jGEŞ{n]I!4 dA>xdAxrKn"+OBwH5XsT8D,ƭ!D(#@ʀM{ ,a! ~z>bR-o}?)Нoi=yB_Q8;%βzeT=l65RlArوp%|lF@a>gD)/jmz]zm' +05b캀 !].lMTC(T¢Sg*uYo(lHDj*mwOV  )+-sC%Y!L$; NqNCAy^n6WDj*my !uf(QFj{EZ"/!%:n\ХWy_HbC~~ S3 z̥ڶwQm20|75Qp;6UVlWAwy|59"2s=^TVRj_![yFD¢XMk{}F^|?USlqN1 '1]JSHwa$:oX8w]"m]]t#OiQq Ea"W^ M 31 o#"BF@ibϻ^|IFH\˰h?UPm/ (GBiQ'#0 |iPSN<25ce|;=/Ul "2d3ߗDӓ][QwJriQ0ވlj@-+)5*܅@tM52Jmeh /"&#oLp\r4/cR,l;|%# SV\bk~VH4Y KNˏAz@ Z]hJF|y5s2J+N<7"BS ZUBcj1r&YZWa!/!E qsK ʊK^[s8r:,ExdiMFDh(;ȨȗJ<,[W@V =aߢ QPae/"iq?78uN% c2 ';(=)-,2./Aovcֺ;[I։.(|_bx܁0GQ}v|BʊK m3miY'Slf0è"2 >HJ h ]IFDɎ8O_QK|! ]z69&/K &"̔?DK7JX)(kRDGg>Qz2#-Ʈ=rDdo6L;Dd b^'B ,'"SW9D(ji2 %sQ,Γ"Mzcl6bHsb} Eb6|U{  Bnu`2xYYAsx*r.dAIvxQ߅dTTeNYq Y !7&|Ԛ3+$P ^EDzn.QjҖo^dAhEaj{cYMusE$!/+͈XQg7swd=؈'.Q'e]S "'dAhHqNaYZCm2 $;l*2*rW fQ!EٖnxOV Qg={`oQ'y_.+ְܸodA拑/ ɨȑ`b;}EڒlwanL"YA(8'O[,ܐDFE҂BD-9xXvNb}G!BOʊK[O/'+D藫t};T2 $3(t 2*_}@>%9 zˉ'+F>Ǘ!"CdTDO#G-E( /<#BO =/QJu/]#bEYIN_.Oc2 н[b( dAhCӼdTXEsl;Q~u"A~'Q9e%F}*\k//ڝ"=syHm'BHF.ET?!d৞yQWz:~& =Bc-" Ś [MF$2*H2 eAQdTXE$Xa_Ax-E:Ez];xh+? &<<dJѣرcmllXӞ;wn޽]t1bϟ߿߯_Aj-''ײeˁ&ѣGa-ڵk}Ւ"Á2=J\YYy͚5mڴ>s]b6|qj >}IKK?xjs=j W HŁm۶8p5ee'A!b%&!-.)G];3%%%ݺu۵k{edd 9rСNNNǏTm6m*//;i$)))ִM4+AA?JJJ={dM9CѣaZo߾Q-,Kݴ7jhƍ`Ĝ^֋Wn&g,nzCӧࣜ W 擓+ ؘj$2*q|==9y$;޽{O0\j5XΦ"gggpϩg yUQW^t5… 6i7++ _z幺 fþ|fDq9q|ܹT"ՀrL /̾ݸ* W*..z- 0F^B""N-}7ǏþG!C` !^Aˋet{.bYqHƍG JT(VCCc##1! +Fj{_W^ n&gAiۛTfȾRt`ذaX_|GDz 8_ >9.>?~Bݼy mڴ9q/rY{FPG\T *΂ A64G9%W n\'gR>dWSuسX522fU5k(^@!2*q20L~_#600pppnҤIVV ౱)))}n,u.)) Yʕ+۶m lS%CBKKKH|}}cb~:(WV n\'Of (fbO.%%:tby͐PlII +*2*uOJٳaGܾ}+Wnzذa AhbΜ9T5=ZIIZj9sl޽{8(v0K0`0ddd`ݩS'CCCbB 0iӦK|ߟhV,ۍESQ4iiid"PXD)+i7(3]&mIu$Be(T,"8*V3' ̌ B/Cb tM O"" EĂ\V3" "bPXD\sY:fQ e(T,".Ϲ̠((2*\qy(2*#\q#m(2*#|E^B"bDI~~7݊+NjِQ e(T,"^w-GI #"PXD 19J [ODz hXQQ!+bZ2  EĎXm[dTTzcEQ^B"bGiQa_dH}N-52  EO&"]I6dADz #I]2 SZXnFqvY  EM!4'(2*S2=t&;Q= 2*_WEcm(2*_R?1 2./+-2'?;Y " EĚrJQ(=Iu4"hA/Cb曽Е4$K#(2*wlHm'b^B"NUe%Nd S3u;·#" ErY{"_QZa0E^B"HyGQE~Fe2ݏDe(T,0q^y?d&v]FV A/Cb S Ӳ :~2  E|Vdm<H"(B/Cby )]r㾑Mia^gYylLFH rNAjz  \(1=?=&n}FFDPwf6 mXWeNnlY " E|S  (:2*AxH&KV֓;Que(T,#byYYѠ|w 4miYF^B"?@c6R# 3w?Qe(T,TB~7yṱ(k^gY|"PX˯MF'oQe(T,TNiQ QɊz8'Ϡ|vxY  E&_Bi8$kw2  Emk:dEIn>{uѺk-"A/Cbʸm*kH҂B&MYŘiXPReL~oAfDγǵHdAz  !?B|f0c5MdRq82*AIKCQkJ >l#Ռ{ Qce(T,T;v~&DZlQ`=fF"PXD|)JfUHxJ.|7g (""PXD|ͰaIEnz4he(T,"P {뛽FgF*DZ)!wnbB/CbR,([J*)2*_\Wzxx[Az /bgne}||ɭ B E1WWՍ\\\ uD(PUUU҂D6,,:"PXD|AŢbA/CbTVu]r T HmA/CbE|kAfR=7U:͆*ͦS`ev3R|]uOhIn>5^P"DqnLbVX,lXm>;ۗ[7uNwܹ] yofw =/MQq;vlww֣G[fMA3_ -Zf͚6mDEEEGGɵlr6ϟ?|p=ƎkccC9KII=xyP0YYVZ >|C -Y3wk&+ӦMSTTm8Ejz /+vm۶v޳g1jʔ1#Ff`pիgϞqjxÆM4jkߜ10OYvm@{ZB\]߲~{ș3;7n)*c2+54O<}n\K)67l*2ZQ5jqF0(hSIIgϞ6M6՝4ix׮]KզRat;woaVVVcƌƔb{u9ӧWcq7o<99M4166! E۴i =2U۳g6p o4C2ڼy3v6m! j |'$XTHIY3}fRo-/jA&sRî׮]svv/Ģ"z*䝼***0U=zIX/&yGn3庸_Ku `SSuسX522ff͚%&&5Djz /(66ּqǎm6&Mwٛ ׭yYxnV}įb*6>ުQF Lc_.,͛))EY4mDpŘeWTqqq bii oLL цX^!݄ᔔ(59;vvvVQQiӦ KӧO:tby-P,}V\ "u EA{A~,-ڳgcy)ݻwv,՝7+0 8X(6-aCK A\gΜرc|ynZ+ %=`[}s\|jҖ4'GVRRb2 9FtАhs޽֭[iÇSm&Nؽ{w,1:<ȑ#,|`,,Rl9/8 m؃HC/CbEŎ"s vn>JƌC_NYA(RRcak'O tpx /^ٌR($[.$͚5eeҎ{ۼxqej(p|A^ 𲴨̄A_X^RSSiJsΡCQP"b)99. ZԮ^^C:gRPnAQQ&EE9sNzѳZMl<-UYaH09ogr3q#/!%suF}W~%µkyY2Zu`>ii9/2*_P]^Zn!W[qB)Ŀ4ZLh:Y:%{fH6WO K|=pZ~b:62ze!jGnY3e(T,"4b{ƒMP~e/\ d2oTc>X}ws52*_V?i?)A%n&hBn#2e(T,"b\}V{Bs[Jk6dFn&&e(T,"b((eqG12*_PzlI"H]C/Cb[GowB^B"*6=q^éXfe(T,"bQ<}FǙcV]yJnJe(T,"P s ÿ$qk޿WC85/?N~t^gR asm B EDG_+Yدh8-?MӂtrS"РA+Cb053AK0%v2zf `>*VQ0pzv"jYXUTh9Uԇ.m)vm3ua) O tZZZ/T4&Pc:Cs 5j>KVSSdݚi٦*Êo (#UsMa^B"LajǺfABo:l%:sZ4ɜٕGZ]f=fcie[hvCMh[@VW`~ A 7[c^lZu`eaa!$6EI~aؿ;8IV#HC/CbѤ$0\v&;h6ITH"73IXZG͟0KElHR|ݮs -v<~|^v[ǣw‚C Š_ -6{\֜ &PQV\\߰rJnLwUeuu63~VKi4OQ$U)D±;T03~m[}GՠXaPHkg6lĩL.s="n1VdEݐ\[NN]mj$ɦ6궛pYaZ3gERtl.=7P LHZ@ %0Ⱥ:@ȧX-]֜&{%c5+uCvJ6wRjk*RRgPX%ozm\VZJVWmP۝-;3 :&{R֓~w ~W9uy9_2#  8_[5 qNˏ\ނC^B"4$0k.sO=.#y&8hY_ =(ġUE9v饅EhYI]_ڪ!IqJ=_Fz Е!r^y2'&d쳟Aθs^q"Vۖ5rkϰ}M69lp 5llUD|P:,sQ( 1\8!8AWHEqV.(D+lwA#t^B"B OS򠢌OP|Poo2Z%{Mƅ XSH}ܯA(e(T,"teV6Ww5oI3{|mSHv𱝾V,ߗ;M 'GI %D0e(T,"\$Z[}NI~5sov=F>'+ŸdwggQ~oa]. DؠPPWt˨B- HS=J";"d22Z-b-M~[ZBEVX5[.V@ [e(T,"dFXlQF6Y'e%`h Hb $CY{xygm9~CU92*ix"_fdg}w{q-hu)+-4~aE-{Oe(T,Ґ]ן3$/>ϻq}Ѡ|j->rSm6lU=[p Be(T,`]jI^Y'0JV.s gcc.VN2} z 4 Ǩ[U!ƃ9X?0hH0w1=g[w:8a?kOqV.Y lPX)+)9ted]U`B&7&ÿ {m6buQzY!zi1n}~wA~B/Cbz{j_VH4YAClW }lZ[|bsjRz|SͨB_W5e7ޑ"GӲd.?rJ!N@ZZ{dddaiӦ5o޼O>T8'|:dE.]lѢŘ1c]rCЁ!CܼyѣPۮ];}bY\nBj8&&FVVUVÇ_bEeMKK9s WsS8_QF7n*999cǎRϞ=ЦI&k377PAi~!Lu”t^' Ԕ>/_F'.pDZ, ڵi?%*kns88رc䘗dRQuF- "rCWMu^\_i~( q'~(&}jT0`[{ժU F^Uk}-,rYS~ݴ(RU&<^N$ UVBeba׵kWׯ?a„$ -T {?GWVa9:::l&M8ob1wΝԩ;EvxĐ%n޽۰aC_oٲ(-^Dvx7VlŊ$++K^III{AI\n~-kv;e\^L뎐2]QI@8<>fNAeRBo߿σ L%Ek[̵응f|p`UnmHp(R5EXSW,,Vr][յ{ f:EsS2_zOr(J˲n?Y^_Br(XʼlCK0sE^n9tʒE cL\xhb?t=| ]TS&]缍# EYSo~@Ccq57+C*"Ǫ^YJ<ֽ:K t9Z,Rks[0-70_HUu0+s[ˋo@C"b?} L1r@#y4{ /a_{2}3"j ]Hŋ],Y` xd9ecRU5%`]WR, pzM)_4Q{r(Xr` *Ck|xsυTY t9ٯ26gso|B]TBI^+zN/@_~e+Re QlZ|Xȳt9Z,"{._*1шS2D2!U6fiKKD 2_j/|@C"byslyB؁Ie$izN(wΑ*^PhhB^qg GlE eeS|!^Ii18Ob P_Mx -A=fO' KQZmcHM'U}^5[4^RE -!hiWqz(!ꪍb9} |YO˖z9oBr(X3lXAy]g TC# 1&.w*)+[סwdJ {Tu.BE#qU+gd gv&%)yNH4aF8+ّdPhG҃^[(,Tz=' ρѸUJ YaQNxΞE_]hLZ@C"rM<.]gM+d"D_ː%J¢ 'm:N)Z)+FĐPhH"}}eCݴ hDwpH6\l:L r yRE.Btx%k~#|CZŽ' QF%vT)C80oX4OL% ʡˡb58Is\gLo6P>ݪRKU˖z\Vt6h)Xt9ZF pF%{q* mR'69)=Y,r9_M,+)PhK#N`($ d#Ƕ%9rmޯ=+WVo;-,"˔O{^t9Zβ^FpY 0c<-՟,@*A1J?)^sw3G%n7ܦTNEiY"k ACj"ř9=gGr d_+*"_MT:,ܭO+Q)ncV2cG2M j<ޓi̼`iktr`/uYHQz?P.Yœy . ۮ?ub5}W]G0;3,@á\. NaV*PRP)Muޑ$WR {fkx"ZfmaJA@fB9 "R? ?xTjt X'bon?p]oooCHʡˡb@UT2&35RehO|qㆩ)Ld###I9t9ZkL=wbd-=Jl- -mŪ"Pr'o&bٟbZәHU~l=$"2mHl- -mŪQ -&r$;ύO_ː%&bٟbZZ9yë7IU~D=" [~&bٟbZoءݱcǮ]޹s7c|̚5YY__ݟvX{a^܋db'+ˢ,@~J'ÎȂ ;H3rڴiKJJؼyǏ[[[}nDN:zzzC 1b"[D\m۶uL52 B 5k\hTW^JJJY׮]֖&/)-v_|('k;; ޿wnVV'^QGgL~ɵkׂf<8:vM@ƍ֬;{.wL{U;}ŊUV119j&dRS `k$nsHEECmD֭PN [Agڶ*&Ƒy߂/-8(}IU"icSBN2 e˖u!;;W^:i… &l޽{ `էO { Z,$=1Kq70<)_36ļ {ݺu 333F mUdɓŊl Keb,V\{ \[8mٲhSQ>i,> vze ,GK!hҽ{WC)NY$WcwV.%)h]mOG7`X`Vݵk{П>'6lmΤ(;X?Bp+@24 O&n_RvHeؔ&~W\9p@&N e6 {0WxEgΜɠáKbL\HU'Z4-+ > 晛3/ϝ;WC`{e E_!VEZȖץ ݹs'/X@p+ a666P bnݺII_ʪIH5Hc& !č?Zb&cwBj*\DT/)J$ʹXiWBe@[ 6{̿` 6m Ֆ.]*y/A^)סD.:by{{.|-[hkkKD5kfmm]Vڵ_~FFFLkN3J&/eAyf~fV`+]]]h21 aPѣGfNhRe ZF`ଂ"\nsmlN}k7U99^"E>/3zUw̅D5AxT0liʼnq6m|oWwҮkmcR¾~mYP\+tn:}? KX Is,J,c|<8e>}hϟ?d٪U@+""XZ\SƒYb,[J/LQQs0YYY‹#bU2~[uZr7ߐp Re Zl߾'NNff?vl#i^]ne=,-O*n!1b]ayz^{X,xfKN?~Z-@lD4a[K{:`_|шlx'! 0Czyy>L`?N &ͱXTHIIATi/hT-*2=.ȭg$U^ zǐOTԳ\,t hRJrswHE۷419,\$2ycE0(g\\OO0W9U)ܗ]e[, A&S7pb{~wR:ϟ?v96`Re rj={V9hW_5bQp5'_bZ,}Z_sY+ݟ8u&?#U֠*uky 9th_C .uؐT@䂑Lhl->^t6RUa 4:F1)UY,&Z,@ҼYfxK(CgNqf VY.Ų?Ų Xx}ל ,(z(DDq"Uր6bi2Ql~\ Q~KE]!Uր6biJ3qn:*HV~sތTYZ,Z,@|6?d# ؼNajY( '#N?܃Ų?Ų Xj(/I.~^(΅uTYZ,Z,@҂"3B-A뎇NyBؾbٟbZ,iXΉפ(Ɨ^,'8NoJt+,M*b)[\byZ_(Yb:)нWTYc؟bZ,D]#J'7diRe ř9)L:xޞ#0ӕnla]ҍ ޽kccpI9t9Z,r{JyD( e#= UD:|3J`zdii ;SW`q -uvTm9(n>BFp'RIYw!U 8F -x} \\NuI(Wt^aFNvs$u 8F0"Ph&"jREBYS_*+ '0 Uu 8F௅ -<$\^]K(7|HB^ zTڠˡbG-Ebsr3'7J[KqY"RBCŲ k%UER!Ӽ*"2yREh.Be#>p#Ue-.yp4,@KFHeK=REdkΎ[ ]zR<{VSZ¢쿙C|֢."2`Z?/6TˡbUI]"U%gɏ[HQ:Y8_'UiGRE(.BU1X-," HؾwMɎ5kZ|xwgcRE.BUϭ[TNzk?* S3L$UZ.%KRE.BU EV2! NYHQE٦uG*R-l;Oˎ!U~r(X׏*Fֳu*Rur9mqg.BU33"j]lr9fgSڵ@KK+***::zĉ 4ҥK"Gu:uJ[[ٍ=^z۷`D-üɸRETFǧ?HSL:Xe|۹g+ *}YE/sss `ddԦMN۶mݻ;vҖ-[\ḺfffrE \\U`N'H^+Fb ^(A,V_ヸ}||jԨq!///SSSȇX׮]AlK| " }{"Ov6 "]1T]w-!Ů]Q411S֭[O_!b* gϞɔ)SRRR$ $'F>qvnFH$"]1T<&5u%DAe栗/_Jܹ/XoesYjiim߾L| "ywo>RETF$Y4qYq>X呟jbBihXA-))8p LL CBBbbW3f̈#ƍA+MMMm߾aZZIklxOFɴMtZxuZV֯_?#~p85kl֬5UVBeb333v b'L$Z > \s;tZpS RCVVVbbR\\&0ddd@eRՂ0vŘ*:0dıAڳpREb-VIdDvζ{5|H蓅_gɺi+FbD/vCơܬp"O,TAUpZmnRh@X4-J#3*0d!hn*^#h eRU5򧓲k O[%"]1 |~6/[JJ6!OJAW*;%Uhx^~.0ː%)tZbWb ƤQgx,\Q?XŒ:j3*R0Gϣkt#j]1XVyu3F_xƗHQGXbq* W`xCIQ)}uHQGX9 !Ut8 wRET F_5xE@s+FbE^leK=6?Wc$?RET F_5p6Lr%UDM+FbE~%U66W6O5RZPdxlIN>Y)tZx8G&\8Y8"*<z8uB]1%Mgdl±Af;RET F_U {%dYREb-V!> 9" 3sƑ*R0/tZB(?f: 'ǧ*R0ĬQ_XhܪD/RU5Y8/-XI"*Jz=wTA?;)Mu-JEᇮo8IZCWʟdg#* (LIh㽰f_h3q,O`4 *XxǤ5tZy.*;]i>h0 &௠8HVETFJ_/J" A?#WDTAV8ǤuFޯ3<~a&5NVETF9TuA3%9f,` S Mk{X7"0ہk*#hr&XCRe05o2CUETFxNTuA3![φB,ö 8ʐ4{Y>)qt 5z]1+gƬLv'Uupm;O)p5T A'R.kp ,ܯ5̪bDu`II)g*#h$#$gdwTr-'\U'%OglL@Wʓw,X?B1 htZ<_~y3R b_?!UD+FbX׉-D4>.EKRE4 b-V><,IJ-GneO -V2}c+"]1+x<&3I]:ؑ#VÁtip5iΐ35PNR6o^$Zd0$dZT ADSIUjX0ZߺĉFF[nQF ׬4 Z=+n],\z -VF0$}kRE4 b-V$x>TFzׯ;%cߣG'p6r! o+.lG\|-VF$D#*"{ sH0$ Aa}JM,IիB>?pv_6mxJJ:~v-֭}ڵڷoun| & --O  4e[qV͚5Yʺu}SNݵkVTTTttĉ4hХKKˏ5ٳLԩSLnk߾}DD#l:{|T .Bo9TbKKի;~`/]:{U;:` N>u6'y[,oX Lt͚g†>>7"qX1luժ9&& }}YE/sss `ddԦMf۶mݻ;vҖ-[\ḺfffrE@_M,|"]+dT'm:wnۻwć`{7̈Fٳ3S9;_߿ߎp^\ ,-V8O !!׮X׮]A3EZ{.Ogl%UDˡDYl͡W}p4,Y,/psf۶_AkP—DIX~ȰJ~;?}/'Nl淌Ů]Xݺu 3;EZlY62eJJJ"`ሓH< FbFλxUm}|FnÇ@f/fҭ[޽hJXq;;beDb9˗?Wa;wd ,[lY\>[--۷o.zKzMAŚ983T1$9~ G,$`ƌqu 5KHpYɣMA0'Uq+52ϼsʂŖ 8&΅!!!1113fĈrƍp+MMMm߾(0%ݢ% ̓zj_>T13ÿH*Ho]suٳG2O]K`ju?\`T7oǕwYZUq+q7m߮\1˝d>gdd96|pӬY3kkkoV@ՅʌfffvO0!))IB gsFRE4-ֶ돹Q !xnjUbGP%;uj?&KNI$T[΄ =3ܹjժzF +|IWw(Tg,oF&Z/ޠP,DA8--MPaȀʤ*Z4:LQ bX!KBIMVI*HiҤB7ol/xq8›T)l+.~&\_8JCi8 X@WX)l*"^s3CߑjUŲ?JC9e9WRJ  ]1"b{wը .F%? C9[{z#UDS+FDXyTQF'X`>CS*#",sw1"Hv4nVX`>E^IT C*"X8HU-D4%Pkx,,@4bDŖ\TQl9 R"h'5|(D{/w5h0tňZ'*"]1wIŒCo; #-=IuԊ`R"h'5|(Öx*#-型/H]rd].-D4ESox7!j]1"bDTqG~!Z,h0t &mJRE4bDf7s-$jR:ž>q G^)Ȇo-Vt Ů /ig#-bQ($B]BUXJh0jP4nM~GfCW 'qEę?z:c+V̜@&Y<{opmӻ=a9)ݸpݻ666D4R_hvi^Yh6tňXM}"EMi@c*0Q]n\{ׯl Mx kP(_FCWX84r|QN" !O:|Tew㯳&M_!uo&T?>JoTkә87H\֝ ˁ[5pr6:~Rgm›)ȇk,t  ѸU^h~#,.8`߱6>G؁7&U-F*@\]E$z#x<@4-P]Çh`ˤ#"+F*Xo漍#u'?!պ$RU,[ⓑ+)5o2(=,@b2-+{uVfņ$#$iRU:<.ᰥ.Y5|(N&Ub ާ[/>?#ZRU:L5dՈ5|(WovTbrfoNj8y&UIlW> qHA*+Fǩ 0}T://E+7)L bD*\G U atp~!,@hC.?G1#Zlː%8‹ҲYP]Ç"x}n# +Fزl >#OglUΉPԈ"kPOo1q!U]1RMv{Y>WBJ!iM)|ZB!x<pPDtH,b{NXEHUJJ>p# Cd<cRE芑Y,zrîL-ȭ?'UD]k;gMW$U:k HUjbaJ:*$oλxQ>ѷIA+FlY8-  ôHnq *'6T5CXˋM&UϷ]hLDi^FcHU[{:-*& 5Cds Ub:[oyZ @cOTcvJ mQYt s_*|]1R95إ^qK%yHQ;>䋷mIA>{}j|CU-¹Q _M̋{O j]Ç|n7 ꤊ CWTbsDhIξHUaxN RE9Mǩ B#շXncVwl[IU1$=f^!5|ȑwW}~6&Ub\ǯ?t,Pwo;,TȮL4B!G|E#2Y,ުJT!.[/?@ lէ3*5|Ӳ#bHA+FdX CoKA8}vk,t "z+F`?V&U%Ϸ#Uy/*5| {' bD>[k'dRWHU?.7,@ytߛf +FcVU̫#HU\Qm\.ӧ*͛7|ʁzѣ[lƍ=q tSNݵkVTT.hϞ=K,iڴiǎa,r3f4`֬Y W_\]]@5iӦ֭[0Օ(CLLnÆ {5{lXy܌H&O.>>~ܹ-[/| >Xo2Cx͚5-ZݻgN> ߙ]޹S]1"-X%ߪ~+Y.<91ʃg |:dff6jԨqƁ;sݻÈQ~&M/߿rڴiôP͚5-Zdggٿ̡VZ`cǎ!62 b~pω22ciiԩgnn>dȐ#F#" GQiMAj...nnn0vʅ4Knn+))8p`͏?nmm ِo:k׮ F_Ck%x9Sa-tňB,F7n3`FK7$ oԤ8ܜgǎ0c^@/ ///ȇU,M 2P‚~[u/\/a asOrG..._0oGd(*rZC5u}2mqyqM;e-[Cِz*4N}"lQausqV.Y@?zn7P;vlݺunݤX)rOI}a`L`:kWR:l`` ^>xPaq8'OX>HQTf x Z\s71"q0n([GX,?rJ37nŲbD[Vqsw5n+9\{xx4:z3˗/K/^0#tOOOo޼ɼv vUp[)ۗb}򣨴L~ܹhAڳpK"9oZ@W(by\לvIœ:ܴ +jx/3q %F]6a?s=222>gϞ΅!!!11 K^z,-55uѢEa̍@S0->AÆ ޽;b\>,VR~SN{yy]~]KK -Vv>d9W x\D;kߦM8 JNNPܷ-Vm+Fkec{4P +ב+R Ę YBߤI4jo}ժU WΝ;שS9p8ڰU͚55kfmm b~߲eK_k&0ӧF?~q.ĵ/XԩSD[棐w믿޲eK XxzA8."ݛ7ovځ n(['ز`,chT@W(b ^' 0OnqK|(,0v@Nɳ `ďe.dR*ǜs\|AP,((Ȟ wUA[`ه"=< CF֟ !إ":D=+Fa@~b}Y5P _juy{cɥ ~85x e 2q"D%YlYJ.{\I9fy)+n?Y]=o{{{R 5x e Pmd| CW(b*1n}f{dmb^'e"|>d!u@]1T-c\$-I~AU(-˲^vd,Yh*t /f(bD &ąYw Ӄ.2 "UDk!KRŢńT`>MINY"A*Q }l=KЀ\(_qIl>Mϣ R@Wb*p2wA^jUygR8|CQm%RE)+FTi@IncߞZZ@OF.[,.4k=a>r_[lYF5,._k b.*Y_Di5|TҼ/KW~FBWb* iGR/D5|T,TD:VX,C6kIROX'd6|T_|<"t#,X U+'dx4n{@R3Ҿ8>ǣIN> AWb4PYnB\HU 2#umL>Ȭؒ|AaŖU,L `a9yCJDO j8PяFFi+Fh@IvIaXg=S[zje),"jjȖ3 RCWbB(v@ykJdf"kk잒*H ]1^eЪ^-@,=J6 ]GU7WC #lX 3}YAJJ2ިmIU"p!D95|T/_HR ,^)Ƭ,HN#TAoΛyɂ/m7 A*ᣪ$"HU+Frx?tQYt7~} @ܝ#]GUyj\' #XlO~Eb?gQWmHA ]1<1{swx{{s8!Cl:NyGR]XLo7n055ldR05|TJλx w#H5+Fb1'X@Q)Qz&Ub-SyBe t ޷X*]1<ŲJc+" #hZ,KkL^lUk}RE٠+Fb1'X@![^sv*]1{֣G7벧u ͏ ,Lt-%5|Hȳ AWbGXZZ ҥ=դI::C'N>j@:Kۼy.$>KUX@!3_%U b ءC[8h~-jԨaᚕ&Aݳg֭T/I['YŲ 'ZKF]1Rׯ;%cߣG'pr! o+.lG\|Qoۼ$VKʮ2 -%5|H ɏ[HAdb!YY]z.} g7oeӦׯ$SXkײn׮]}V/}n˗X``@S[7S~A/lլYUyhY߾<mA[\$ΟDH0̶[o߾DܾxRwU0Ų :q"#tXlii@zuǏ K/b{t2޳ggb+ [Nmsڿ SS XoRQС}4Ѻuk?ԹzVЙmqd^~">ݓ/)J$X@!”tf:<.,@+F/h?"!8 $vm.|Ia̟8kA?qb3e9b;pvڵ22<"}IU"ŲC8GS6*W#@žxa nÇ@h/fL ޽hJEXq;;E͙a*];Op+;WuED5qDBe ./$UZwՑ[ 2SWRe1XlIIԩjRfɓG;>`@OBgQ,V\Oիkd4y-q*~(<ϠAV >/"|US"}IU"ŲEiYJN*LajeK=Re1,.]ڹ^tt#GOCt⩵kھ} Lmk LjMr!K<%b%?nM>ە+нb!Ka eA/s3Йu8;[xY`Be 'ǧ*Ug9WRJ $vF,;bĀ,S6#\\.Katt!4a0F|2wZjިQ}/_ 1[<,Ѥ˿7h Wö1U`_|шlx'כy0E,a_`Be vg漉#U[G"9*aJ ޼؃k oR$}h6*ʮp}i6xg**%LBe h_t0xIAAz+AHjꔄV~Be ncV<"Upԧ! MېTY ZӞ=+RIBe ws!Uz(5k<[TL <.rifK@XZnhj撚K\sveMGaDMvf"pa8~=0x3g\nN "UQX]/zE+oxvto'#~9v (D@D+Fn:vt$ǭخ!bQʧc4o]CĢ* +i>!f"QкgE%8s\ޝx+`XTE!b(%C\F O]Q\n)"UQXyqZGn?vt$3 خ!bQaf"}k'u2خ!bQ@mwذ]+/,nkP^P Nس]ak޳Q)D@{Ih<_}l@wME'bQ,D^ fv/rՇ.dYL]bM[nہKw:J u1sss\NEY~u[Ui; l׆{pEvt$%lWh$9,Khhhrr2;a/i0UI)0 9mQḧ gsZ3Y0\Ќм(tv@_Gߒ X_.m;*HLe1#i$=Ah.hFh^h NKiǛ߮gv{)Q_Z.[t+N8% kqq1;a/yw{em(8ƌX_)خ m/ctA]Dv"@fh& D,[Yv)jŻ.~, D,gt)w,IbPж0'"@>!PWЅ[v PB  xE-QATN]׾f dQ;/\ "@ fB7vE;GaD \F=vCRvU[Ԏ7&fb.oMbA+ ~.@u7&!bLv=V+S [7m`"u ǯ*{.x hy&r;jnFb|149Vl_B>7nc˽8 P?qg\ߟV w:A?mknc9l0s3v= dD  t%Yy6GdJ>y--8.@>Ƌbb6Fv}OGq.@]O "qsk>;H auu5ʿ"@\ߛvɽv7]:)+p;AoC?Frsچ{'.@UJ&Xq(/(< >h S&] Yk|.vQl?L.@v7$+ D,h$B4'Яӎ).@-u7ΏId$ &m~Wa&jH "@Lcz}*)eorceGR oEٮ$s)خ^mz)d]҅.oNjtN] P+k쀤!bDcY%7 v%K%X(Xٿ4:;; uXQJqwoWd-܄sG ys bٱ& Vs|jqw^bU(6\5b~qz6;4 b*C}Gʿ&g).d8M?AAߘwY݆QeF9 D'UՈeD,ܺk㋲Bv$Z_rx4qaJvAMd @hWԎl@Kfm>fn.+`ǚ$D,$ʺ$$ qghpq ۅ-'ĹV&c"@jA3)hپ~]p MUQJFLa@ "@"{fVo3Ui;MӲ.!b#hEnӨ7F.@Ӕ`{%c4X(̵E?$G;eV|BkT/P3y+1"@RbO:YrJsaO84! ޥ?WM"@ZkUdQ^Tb~ģ)?^ Ool >&K{AHMޝxYWTv6\2j4S*Ťy+(\_sZ$=X |o [scdG߈_ߏ/2dM(+8ANr4IN#\ Ax)k-u1AgP:HJ)p0ijҔ]/R~OjU[uyk3, cn"71t>YC:5nLXvyeE(Y_GnV?p;z]7U +NmCHĥi;;GGm FW R~ʭ ^Cw֙9Wt  e>UCAjc7RybW^TrU d]BS[@@H\>Ƌ.^ fl;[tKR= amQ>&K.{7 {wzYR^lg"VUR4{Y Dzc؍1 b"H5 Dl̸3Nkm:dv 9A@qk퀧g``RLOՋju<֯G~oatmiGZv3D,_X dL Z}orիFr87~#|X(.bϭjnn ؙU߯*bʿ&ߑ`QV+bB@@i"ٳVVVU*sշ(HX/D,_X "0)-I搷n " +j-Iv^cu7fDkȍǓJ2s٭A|!b%PXZ>tyUtI Շm S٭AZ|!b%P M 9tYh׵d] m;}i07tx WTԤ!boݕ QrΥ[{!rӵnMn 4y>4$  ψ:O*(Yy3eh8xذw+kU?rŊ*KۭZ46r#?<}A=j<#afVHtr%M}Zra=Mt  W}"62tTnAM֮LjB֧+mڼ0yQnWrիgR :uرݒ%ߖo\TOb;o0瞭>xOFs[(bvpwz:_E/u'#ԩC:thۭ[' cU>K~uaZߠm۶ GZt qw\9rn/[6")ɕXjݺԚ5FHiI; /<ߣGgW/ԙ5k,޽+(7o~/ܖff(l͛haM߰QkElzא!(4/!W(mOmoC'wxq Z[255-wAcqlh860/mڷoIG}B?bi9~ /) 4m PHo=UC כ+Ӊcc(N(Eh KsNhѢyv/3lye3E_{%_~{ly2'7z1~۝;w3p4QOߚBs̟?dm|h ,-sw^k>TsU"  Z,Wwصi…}&;͛Hde0Cff;ztW܊0Ϝ٬R<>ՙ+]XM_y[Q$)i٥KGZ}>K}"}H*D,"zF,?-3q 4CLv6L͒ݞyѣ?t˻޲sM&&^}Z+bU*'srQ\?`@_I;tOo6ۚP:"}Ñ"bA|޽qTS_f-aeEEf:uмJ1Cu{7nK99ٸ4jƌZhz̈7hsÇرO@;ӬOQL;٪U_/,QN5ljj}?2N9s;9728Iv꠪jG}B3b IVV;N5o\NSʰm[kpD1CicZMly7|+Z͚5"BzԨ>=\پ}>xԏw?\q%'oҤQt\oݺo-P?ӦWQ͟?i6ڷí\]՗__V=RD,"/[Ҭ䒒\jU11v<86։;5W'GϿ\Үqo[RS/:RD,"/=D,  "vz3O.D,"X   +BĂ>!bBJOX(D,"/D   +BĂ>!bBJOX(D,"/.b좧iH zX  4'?[ݶgjcr<..iA~d2=MI MNNfg@GuHOд IT*".詙@rD&&".hCqqqJ!8>Dbvvt          7uA^IENDB`kea-2.0.2/doc/sphinx/uml/appendRequestedVendorOptions.uml0000644000175000017500000000220614206773363020464 00000000000000@startuml Title Append vendor requested options algorithm (Kea 1.8.0) :get vendor id from query vivso option; :get vendor id from response vivso option; if (vendor id) then (no) :return; stop else (yes) endif :get configured option list; :get option request list (ORO) from query DOCSIS vendor option; while (for each item from configured option list) :get configured options in vendor id space; while (for each persistent option) :push back option code to ORO; endwhile endwhile if (response vivso option) then (no) :create vivso option for vendor id; else (yes) endif :added = false; while (for each code in ORO) if (sub-option is not set in vivso option) then (yes) while (for each item from configured option list) :get configured options in vendor id space; if (found) then (first) :add sub-option to vivso option; :added = true; else (not found or already found) endif endwhile else (no) endif if (added) then (yes) if (vivso option in response) then (no) :add vivso option in response; else (yes) endif else (no) endif endwhile ->done; stop @enduml kea-2.0.2/doc/sphinx/Makefile.am0000644000175000017500000001563714206773363013335 00000000000000EXTRA_DIST = sphinxbuilddir = $(builddir)/_build abs_sphinxbuilddir = $(abs_builddir)/_build if GENERATE_DOCS sphinxopts = sphinxopts += -v sphinxopts += -E sphinxopts += -a sphinxopts += -W sphinxopts += -j 2 sphinxopts += -c "${abs_srcdir}" static_sources = include static/static_sources.mk EXTRA_DIST += static/static_sources.mk # ARM rst_arm_sources = rst_arm_sources += index.rst rst_arm_sources += manpages.rst rst_arm_sources += umls.rst include arm/rst_arm_sources.mk EXTRA_DIST += arm/rst_arm_sources.mk main_sources = $(rst_arm_sources) conf.py $(static_sources) # mans rst_man_sources = include man/rst_man_sources.mk EXTRA_DIST += man/rst_man_sources.mk rst_arm_sources += grammar/grammar.rst rst_arm_sources += grammar/grammar-ca-parser.rst rst_arm_sources += grammar/grammar-d2-parser.rst rst_arm_sources += grammar/grammar-dhcp4-parser.rst rst_arm_sources += grammar/grammar-dhcp6-parser.rst rst_arm_sources += grammar/grammar-netconf-parser.rst man8s = include man/man8s.mk EXTRA_DIST += man/man8s.mk man_sources = $(rst_man_sources) conf.py EXTRA_DIST += $(main_sources) $(man_sources) mes2doc.py api2doc.py $(man8s) # list of messages files that are used to generate kea-messages.rst and then kea-messages.pdf mes_files = include $(srcdir)/mes_files.mk EXTRA_DIST += mes_files.mk # list of api files that are used to generate api.rst api_files = include $(top_srcdir)/src/share/api/api_files.mk if HAVE_PDFLATEX all: html mans pdf text else all: html mans text endif # build the list of message files mes-files.txt: mes_files.mk @sed 's;mes_files .*)/;;' $< > $@ # this rule is only used for development purposes and is not used in official # build process as kea-messages.rst is always generated via sphinx's conf.py $(srcdir)/kea-messages.rst: $(mes_files) mes2doc.py $(PYTHON) $(srcdir)/mes2doc.py -o $@ $(mes_files) # build the list of api files api-files.txt: $(top_srcdir)/src/share/api/api_files.mk @sed 's;api_files .*)/;;' $< > $@ # some tools do not use this makefile but still need generate files. EXTRA_DIST += mes-files.txt api-files.txt # this rule is only used for development purposes and is not used in official # build process as api.rst is always generated via sphinx's conf.py $(srcdir)/api.rst: $(api_files) api-files.txt api2doc.py $(PYTHON) $(srcdir)/api2doc.py -o $@ $(api_files) $(srcdir)/arm/platforms.rst: rm -f $(srcdir)/arm/platforms.rst cp $(srcdir)/../../platforms.rst $(srcdir)/arm/platforms.rst # UML files if HAVE_PLANTUML .uml.png: @PLANTUML@ $< .uml.svg: @PLANTUML@ -svg $< endif EXTRA_DIST += uml/appendRequestedOptions.png EXTRA_DIST += uml/appendRequestedOptions.svg EXTRA_DIST += uml/appendRequestedOptions.uml EXTRA_DIST += uml/appendRequestedVendorOptions.png EXTRA_DIST += uml/appendRequestedVendorOptions.svg EXTRA_DIST += uml/appendRequestedVendorOptions.uml EXTRA_DIST += uml/assign-lease4.png EXTRA_DIST += uml/assign-lease4.svg EXTRA_DIST += uml/assign-lease4.uml EXTRA_DIST += uml/buildCfgOptionList.png EXTRA_DIST += uml/buildCfgOptionList.svg EXTRA_DIST += uml/buildCfgOptionList.uml EXTRA_DIST += uml/currentHost4.png EXTRA_DIST += uml/currentHost4.svg EXTRA_DIST += uml/currentHost4.uml EXTRA_DIST += uml/lease-states.png EXTRA_DIST += uml/lease-states.svg EXTRA_DIST += uml/lease-states.uml EXTRA_DIST += uml/main-loop.png EXTRA_DIST += uml/main-loop.svg EXTRA_DIST += uml/main-loop.uml EXTRA_DIST += uml/packet4.png EXTRA_DIST += uml/packet4.svg EXTRA_DIST += uml/packet4.uml EXTRA_DIST += uml/request4-lease.png EXTRA_DIST += uml/request4-lease.svg EXTRA_DIST += uml/request4-lease.uml EXTRA_DIST += uml/request4.png EXTRA_DIST += uml/request4.svg EXTRA_DIST += uml/request4.uml EXTRA_DIST += uml/requestLease4.png EXTRA_DIST += uml/requestLease4.svg EXTRA_DIST += uml/requestLease4.uml EXTRA_DIST += uml/select4.png EXTRA_DIST += uml/select4.svg EXTRA_DIST += uml/select4.uml EXTRA_DIST += uml/tkey.png EXTRA_DIST += uml/tkey.svg EXTRA_DIST += uml/tkey.uml EXTRA_DIST += uml/update.png EXTRA_DIST += uml/update.svg EXTRA_DIST += uml/update.uml PDFLATEX_AND_OPTS=$(PDFLATEX) -interaction nonstopmode pdf: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst $(SPHINXBUILD) -M latex $(srcdir) $(sphinxbuilddir) $(sphinxopts) -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-arm.tex -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-arm.tex -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-arm.tex -cd $(abs_sphinxbuilddir)/latex && makeindex -s python.ist kea-arm.idx -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-arm.tex cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-arm.tex -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-messages.tex -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-messages.tex -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-messages.tex -cd $(abs_sphinxbuilddir)/latex && makeindex -s python.ist kea-messages.idx -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-messages.tex cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-messages.tex html: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst $(SPHINXBUILD) -M html $(srcdir) $(sphinxbuilddir) $(sphinxopts) # This target is not used anywhere, but people who prefer single page docs # can do make -C doc/sphinx singlehtml and then enjoy their docs being # generated in doc/sphinx/_build/singlehtml singlehtml: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst $(SPHINXBUILD) -M singlehtml $(srcdir) $(sphinxbuilddir) $(sphinxopts) text: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst $(SPHINXBUILD) -M text $(srcdir) $(sphinxbuilddir) $(sphinxopts) $(man8s): mans mans: $(man_sources) api-files.txt mes-files.txt $(SPHINXBUILD) -M man $(srcdir) $(sphinxbuilddir) $(sphinxopts) clean-local: rm -rf $(sphinxbuilddir) rm -f $(srcdir)/mes-files.txt $(srcdir)/api-files.txt rm -f $(srcdir)/kea-messages.rst $(srcdir)/api.rst rm -f $(srcdir)/arm/platforms.rst .PHONY: all pdf html mans endif # install and uninstall can occur with GENERATE_DOCS and without it # so we want to install all when GENERATE_DOCS is and # just mans when GENERATE_DOCS is not used, and when man files exists (e.g release tarball) install-data-local: mkdir -p $(DESTDIR)$(docdir) if GENERATE_DOCS cp -r $(sphinxbuilddir)/html $(DESTDIR)$(docdir) if HAVE_PDFLATEX ${INSTALL_DATA} $(sphinxbuilddir)/latex/kea-arm.pdf $(DESTDIR)$(docdir) ${INSTALL_DATA} $(sphinxbuilddir)/latex/kea-messages.pdf $(DESTDIR)$(docdir) endif ${MKDIR_P} ${DESTDIR}${mandir}/man8 ${INSTALL_DATA} $(man8s) ${DESTDIR}${mandir}/man8/ else if INSTALL_MANS ${MKDIR_P} ${DESTDIR}${mandir}/man8 ${INSTALL_DATA} $(sphinxbuilddir)/man/*.8 ${DESTDIR}${mandir}/man8/ endif endif uninstall-local: rm -rf $(DESTDIR)$(docdir) # There are sometimes conflicts when more then one sphinx-build is run at a time. # This target blocks running anything in parallel in this Makefile, # all is run serially. .NOTPARALLEL: kea-2.0.2/doc/sphinx/manpages.rst0000644000175000017500000000121614206773363013612 00000000000000.. Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. .. _manpages: Manual Pages ============ .. toctree:: :maxdepth: 1 man/kea-dhcp4.8 man/kea-dhcp6.8 man/kea-ctrl-agent.8 man/keactrl.8 man/kea-admin.8 man/kea-dhcp-ddns.8 man/kea-lfc.8 man/kea-shell.8 man/kea-netconf.8 man/perfdhcp.8 kea-2.0.2/doc/sphinx/Makefile.in0000644000175000017500000012254514206773377013350 00000000000000# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ # some tools do not use this makefile but still need generate files. @GENERATE_DOCS_TRUE@am__append_1 = static/static_sources.mk \ @GENERATE_DOCS_TRUE@ arm/rst_arm_sources.mk \ @GENERATE_DOCS_TRUE@ man/rst_man_sources.mk man/man8s.mk \ @GENERATE_DOCS_TRUE@ $(main_sources) $(man_sources) mes2doc.py \ @GENERATE_DOCS_TRUE@ api2doc.py $(man8s) mes_files.mk \ @GENERATE_DOCS_TRUE@ mes-files.txt api-files.txt \ @GENERATE_DOCS_TRUE@ uml/appendRequestedOptions.png \ @GENERATE_DOCS_TRUE@ uml/appendRequestedOptions.svg \ @GENERATE_DOCS_TRUE@ uml/appendRequestedOptions.uml \ @GENERATE_DOCS_TRUE@ uml/appendRequestedVendorOptions.png \ @GENERATE_DOCS_TRUE@ uml/appendRequestedVendorOptions.svg \ @GENERATE_DOCS_TRUE@ uml/appendRequestedVendorOptions.uml \ @GENERATE_DOCS_TRUE@ uml/assign-lease4.png \ @GENERATE_DOCS_TRUE@ uml/assign-lease4.svg \ @GENERATE_DOCS_TRUE@ uml/assign-lease4.uml \ @GENERATE_DOCS_TRUE@ uml/buildCfgOptionList.png \ @GENERATE_DOCS_TRUE@ uml/buildCfgOptionList.svg \ @GENERATE_DOCS_TRUE@ uml/buildCfgOptionList.uml \ @GENERATE_DOCS_TRUE@ uml/currentHost4.png uml/currentHost4.svg \ @GENERATE_DOCS_TRUE@ uml/currentHost4.uml uml/lease-states.png \ @GENERATE_DOCS_TRUE@ uml/lease-states.svg uml/lease-states.uml \ @GENERATE_DOCS_TRUE@ uml/main-loop.png uml/main-loop.svg \ @GENERATE_DOCS_TRUE@ uml/main-loop.uml uml/packet4.png \ @GENERATE_DOCS_TRUE@ uml/packet4.svg uml/packet4.uml \ @GENERATE_DOCS_TRUE@ uml/request4-lease.png \ @GENERATE_DOCS_TRUE@ uml/request4-lease.svg \ @GENERATE_DOCS_TRUE@ uml/request4-lease.uml uml/request4.png \ @GENERATE_DOCS_TRUE@ uml/request4.svg uml/request4.uml \ @GENERATE_DOCS_TRUE@ uml/requestLease4.png \ @GENERATE_DOCS_TRUE@ uml/requestLease4.svg \ @GENERATE_DOCS_TRUE@ uml/requestLease4.uml uml/select4.png \ @GENERATE_DOCS_TRUE@ uml/select4.svg uml/select4.uml \ @GENERATE_DOCS_TRUE@ uml/tkey.png uml/tkey.svg uml/tkey.uml \ @GENERATE_DOCS_TRUE@ uml/update.png uml/update.svg \ @GENERATE_DOCS_TRUE@ uml/update.uml subdir = doc/sphinx ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \ $(top_srcdir)/m4macros/ax_cpp11.m4 \ $(top_srcdir)/m4macros/ax_crypto.m4 \ $(top_srcdir)/m4macros/ax_find_library.m4 \ $(top_srcdir)/m4macros/ax_gssapi.m4 \ $(top_srcdir)/m4macros/ax_gtest.m4 \ $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ $(top_srcdir)/m4macros/ax_sysrepo.m4 \ $(top_srcdir)/m4macros/libtool.m4 \ $(top_srcdir)/m4macros/ltoptions.m4 \ $(top_srcdir)/m4macros/ltsugar.m4 \ $(top_srcdir)/m4macros/ltversion.m4 \ $(top_srcdir)/m4macros/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in \ $(srcdir)/arm/rst_arm_sources.mk $(srcdir)/man/man8s.mk \ $(srcdir)/man/rst_man_sources.mk $(srcdir)/mes_files.mk \ $(srcdir)/static/static_sources.mk \ $(top_srcdir)/src/share/api/api_files.mk DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BENCHMARK_CPPFLAGS = @BENCHMARK_CPPFLAGS@ BENCHMARK_INCLUDES = @BENCHMARK_INCLUDES@ BENCHMARK_LDADD = @BENCHMARK_LDADD@ BENCHMARK_LDFLAGS = @BENCHMARK_LDFLAGS@ BENCHMARK_SOURCE = @BENCHMARK_SOURCE@ BOOST_INCLUDES = @BOOST_INCLUDES@ BOOST_LIBS = @BOOST_LIBS@ BOTAN_TOOL = @BOTAN_TOOL@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONTRIB_DIR = @CONTRIB_DIR@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CQL_CPPFLAGS = @CQL_CPPFLAGS@ CQL_LIBS = @CQL_LIBS@ CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ CRYPTO_LIBS = @CRYPTO_LIBS@ CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ CRYPTO_RPATH = @CRYPTO_RPATH@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DISTCHECK_BENCHMARK_CONFIGURE_FLAG = @DISTCHECK_BENCHMARK_CONFIGURE_FLAG@ DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GENHTML = @GENHTML@ GREP = @GREP@ GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ GSSAPI_LIBS = @GSSAPI_LIBS@ GTEST_CONFIG = @GTEST_CONFIG@ GTEST_INCLUDES = @GTEST_INCLUDES@ GTEST_LDADD = @GTEST_LDADD@ GTEST_LDFLAGS = @GTEST_LDFLAGS@ GTEST_SOURCE = @GTEST_SOURCE@ HAVE_SYSREPO = @HAVE_SYSREPO@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KEA_CXXFLAGS = @KEA_CXXFLAGS@ KEA_SRCID = @KEA_SRCID@ KRB5_CONFIG = @KRB5_CONFIG@ LCOV = @LCOV@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LEXLIB = @LEXLIB@ LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ MYSQL_LIBS = @MYSQL_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ PATH_SEPARATOR = @PATH_SEPARATOR@ PDFLATEX = @PDFLATEX@ PERL = @PERL@ PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ PGSQL_LIBS = @PGSQL_LIBS@ PKGPYTHONDIR = @PKGPYTHONDIR@ PKG_CONFIG = @PKG_CONFIG@ PLANTUML = @PLANTUML@ PREMIUM_DIR = @PREMIUM_DIR@ PYTHON = @PYTHON@ PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ PYTHON_PLATFORM = @PYTHON_PLATFORM@ PYTHON_PREFIX = @PYTHON_PREFIX@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SED = @SED@ SEP = @SEP@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SPHINXBUILD = @SPHINXBUILD@ SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ SR_REPO_PATH = @SR_REPO_PATH@ STRIP = @STRIP@ SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ SYSREPO_LIBS = @SYSREPO_LIBS@ SYSREPO_VERSION = @SYSREPO_VERSION@ USE_LCOV = @USE_LCOV@ VALGRIND = @VALGRIND@ VERSION = @VERSION@ WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgpyexecdir = @pkgpyexecdir@ pkgpythondir = @pkgpythondir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ pyexecdir = @pyexecdir@ pythondir = @pythondir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = $(am__append_1) sphinxbuilddir = $(builddir)/_build abs_sphinxbuilddir = $(abs_builddir)/_build @GENERATE_DOCS_TRUE@sphinxopts = -v -E -a -W -j 2 -c "${abs_srcdir}" @GENERATE_DOCS_TRUE@static_sources = static/kea-imageonly-100bw.png \ @GENERATE_DOCS_TRUE@ static/kea-logo-100x70.png \ @GENERATE_DOCS_TRUE@ static/kea-logo-200.png static/kea.css # ARM @GENERATE_DOCS_TRUE@rst_arm_sources = index.rst manpages.rst umls.rst \ @GENERATE_DOCS_TRUE@ arm/acknowledgments.rst arm/admin.rst \ @GENERATE_DOCS_TRUE@ arm/agent.rst arm/classify.rst \ @GENERATE_DOCS_TRUE@ arm/config-backend.rst \ @GENERATE_DOCS_TRUE@ arm/config-templates.rst arm/config.rst \ @GENERATE_DOCS_TRUE@ arm/congestion-handling.rst \ @GENERATE_DOCS_TRUE@ arm/ctrl-channel.rst \ @GENERATE_DOCS_TRUE@ arm/database-connectivity.rst arm/ddns.rst \ @GENERATE_DOCS_TRUE@ arm/dhcp4-srv.rst arm/dhcp6-srv.rst \ @GENERATE_DOCS_TRUE@ arm/ext-gss-tsig.rst arm/ext-netconf.rst \ @GENERATE_DOCS_TRUE@ arm/hammer.rst arm/hooks-bootp.rst \ @GENERATE_DOCS_TRUE@ arm/hooks-cb-cmds.rst \ @GENERATE_DOCS_TRUE@ arm/hooks-class-cmds.rst arm/hooks-ha.rst \ @GENERATE_DOCS_TRUE@ arm/hooks-host-cache.rst \ @GENERATE_DOCS_TRUE@ arm/hooks-lease-cmds.rst \ @GENERATE_DOCS_TRUE@ arm/hooks-lease-query.rst \ @GENERATE_DOCS_TRUE@ arm/hooks-radius.rst \ @GENERATE_DOCS_TRUE@ arm/hooks-run-script.rst \ @GENERATE_DOCS_TRUE@ arm/hooks-stat-cmds.rst arm/hooks.rst \ @GENERATE_DOCS_TRUE@ arm/install.rst arm/integrations.rst \ @GENERATE_DOCS_TRUE@ arm/intro.rst arm/keactrl.rst \ @GENERATE_DOCS_TRUE@ arm/lease-expiration.rst arm/lfc.rst \ @GENERATE_DOCS_TRUE@ arm/logging.rst arm/quickstart.rst \ @GENERATE_DOCS_TRUE@ arm/security.rst arm/shell.rst \ @GENERATE_DOCS_TRUE@ arm/stats.rst arm/stork.rst \ @GENERATE_DOCS_TRUE@ grammar/grammar.rst \ @GENERATE_DOCS_TRUE@ grammar/grammar-ca-parser.rst \ @GENERATE_DOCS_TRUE@ grammar/grammar-d2-parser.rst \ @GENERATE_DOCS_TRUE@ grammar/grammar-dhcp4-parser.rst \ @GENERATE_DOCS_TRUE@ grammar/grammar-dhcp6-parser.rst \ @GENERATE_DOCS_TRUE@ grammar/grammar-netconf-parser.rst @GENERATE_DOCS_TRUE@main_sources = $(rst_arm_sources) conf.py $(static_sources) # mans @GENERATE_DOCS_TRUE@rst_man_sources = man/kea-admin.8.rst \ @GENERATE_DOCS_TRUE@ man/kea-ctrl-agent.8.rst \ @GENERATE_DOCS_TRUE@ man/kea-dhcp4.8.rst man/kea-dhcp6.8.rst \ @GENERATE_DOCS_TRUE@ man/kea-dhcp-ddns.8.rst man/kea-lfc.8.rst \ @GENERATE_DOCS_TRUE@ man/kea-netconf.8.rst man/kea-shell.8.rst \ @GENERATE_DOCS_TRUE@ man/keactrl.8.rst man/perfdhcp.8.rst @GENERATE_DOCS_TRUE@man8s = $(sphinxbuilddir)/man/kea-admin.8 \ @GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/kea-ctrl-agent.8 \ @GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/kea-dhcp4.8 \ @GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/kea-dhcp6.8 \ @GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/kea-dhcp-ddns.8 \ @GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/kea-lfc.8 \ @GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/kea-netconf.8 \ @GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/kea-shell.8 \ @GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/keactrl.8 \ @GENERATE_DOCS_TRUE@ $(sphinxbuilddir)/man/perfdhcp.8 @GENERATE_DOCS_TRUE@man_sources = $(rst_man_sources) conf.py # list of messages files that are used to generate kea-messages.rst and then kea-messages.pdf @GENERATE_DOCS_TRUE@mes_files = $(top_srcdir)/src/hooks/dhcp/flex_option/flex_option_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/hooks/dhcp/bootp/bootp_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/hooks/dhcp/mysql_cb/mysql_cb_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/hooks/dhcp/high_availability/ha_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/hooks/dhcp/stat_cmds/stat_cmds_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/hooks/dhcp/user_chk/user_chk_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/config/config_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/hooks/hooks_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/dhcpsrv/dhcpsrv_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/dhcpsrv/alloc_engine_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/dhcpsrv/hosts_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/http/auth_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/http/http_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/dhcp_ddns/dhcp_ddns_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/database/db_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/log/log_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/log/logimpl_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/log/tests/log_test_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/process/process_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/asiodns/asiodns_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/eval/eval_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/lib/d2srv/d2_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/bin/dhcp4/dhcp4_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/bin/agent/ca_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/bin/dhcp6/dhcp6_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/bin/lfc/lfc_messages.mes \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/bin/netconf/netconf_messages.mes # list of api files that are used to generate api.rst @GENERATE_DOCS_TRUE@api_files = $(top_srcdir)/src/share/api/build-report.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-clear.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-flush.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-get-by-id.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-insert.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-load.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-remove.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-size.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/cache-write.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/class-add.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/class-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/class-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/class-list.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/class-update.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/config-backend-pull.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/config-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/config-reload.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/config-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/config-test.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/config-write.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/dhcp-disable.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/dhcp-enable.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-key-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-key-expire.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-key-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-list.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-purge-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/gss-tsig-purge.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-continue.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-heartbeat.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-maintenance-cancel.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-maintenance-notify.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-maintenance-start.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-reset.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-scopes.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-sync.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/ha-sync-complete-notify.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-add.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-get-by-client-id.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-get-by-hostname.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-get-by-hw-address.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-get-page.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-resend-ddns.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-update.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease4-wipe.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-add.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-bulk-apply.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-get-by-duid.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-get-by-hostname.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-get-page.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-resend-ddns.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-update.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/lease6-wipe.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/leases-reclaim.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/libreload.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/list-commands.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network4-add.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network4-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network4-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network4-list.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network4-subnet-add.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network4-subnet-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network6-add.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network6-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network6-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network6-list.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network6-subnet-add.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/network6-subnet-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class4-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class4-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class4-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class4-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class6-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class6-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class6-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-class6-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter4-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter4-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter4-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter4-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter6-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter6-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter6-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-global-parameter6-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network4-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network4-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network4-list.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network4-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network6-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network6-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network6-list.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-network6-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def4-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def4-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def4-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def4-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def6-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def6-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def6-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option-def6-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-global-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-global-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-global-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-global-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-network-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-network-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-pool-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-pool-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-subnet-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option4-subnet-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-global-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-global-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-global-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-global-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-network-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-network-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-pd-pool-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-pd-pool-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-pool-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-pool-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-subnet-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-option6-subnet-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server4-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server4-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server4-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server4-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server6-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server6-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server6-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-server6-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet4-del-by-id.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet4-del-by-prefix.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet4-get-by-id.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet4-get-by-prefix.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet4-list.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet4-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet6-del-by-id.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet6-del-by-prefix.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet6-get-by-id.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet6-get-by-prefix.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet6-list.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/remote-subnet6-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-add.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-get-by-hostname.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-get-by-id.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-get-page.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/reservation-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/server-tag-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/shutdown.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/stat-lease4-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/stat-lease6-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-get-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-remove-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-remove.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-reset-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-reset.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-sample-age-set-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-sample-age-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-sample-count-set-all.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/statistic-sample-count-set.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/status-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet4-add.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet4-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet4-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet4-list.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet4-update.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet6-add.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet6-del.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet6-get.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet6-list.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/subnet6-update.json \ @GENERATE_DOCS_TRUE@ $(top_srcdir)/src/share/api/version-get.json @GENERATE_DOCS_TRUE@PDFLATEX_AND_OPTS = $(PDFLATEX) -interaction nonstopmode all: all-am .SUFFIXES: .SUFFIXES: .png .svg .uml $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/static/static_sources.mk $(srcdir)/arm/rst_arm_sources.mk $(srcdir)/man/rst_man_sources.mk $(srcdir)/man/man8s.mk $(srcdir)/mes_files.mk $(top_srcdir)/src/share/api/api_files.mk $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/sphinx/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign doc/sphinx/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(srcdir)/static/static_sources.mk $(srcdir)/arm/rst_arm_sources.mk $(srcdir)/man/rst_man_sources.mk $(srcdir)/man/man8s.mk $(srcdir)/mes_files.mk $(top_srcdir)/src/share/api/api_files.mk $(am__empty): $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." @GENERATE_DOCS_FALSE@clean-local: clean: clean-am clean-am: clean-generic clean-libtool clean-local mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: @GENERATE_DOCS_FALSE@html: html-am html-am: info: info-am info-am: install-data-am: install-data-local install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool @GENERATE_DOCS_FALSE@pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-local .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ clean-local cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am \ install-data-local install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am uninstall-local .PRECIOUS: Makefile @GENERATE_DOCS_TRUE@@HAVE_PDFLATEX_TRUE@all: html mans pdf text @GENERATE_DOCS_TRUE@@HAVE_PDFLATEX_FALSE@all: html mans text # build the list of message files @GENERATE_DOCS_TRUE@mes-files.txt: mes_files.mk @GENERATE_DOCS_TRUE@ @sed 's;mes_files .*)/;;' $< > $@ # this rule is only used for development purposes and is not used in official # build process as kea-messages.rst is always generated via sphinx's conf.py @GENERATE_DOCS_TRUE@$(srcdir)/kea-messages.rst: $(mes_files) mes2doc.py @GENERATE_DOCS_TRUE@ $(PYTHON) $(srcdir)/mes2doc.py -o $@ $(mes_files) # build the list of api files @GENERATE_DOCS_TRUE@api-files.txt: $(top_srcdir)/src/share/api/api_files.mk @GENERATE_DOCS_TRUE@ @sed 's;api_files .*)/;;' $< > $@ # this rule is only used for development purposes and is not used in official # build process as api.rst is always generated via sphinx's conf.py @GENERATE_DOCS_TRUE@$(srcdir)/api.rst: $(api_files) api-files.txt api2doc.py @GENERATE_DOCS_TRUE@ $(PYTHON) $(srcdir)/api2doc.py -o $@ $(api_files) @GENERATE_DOCS_TRUE@$(srcdir)/arm/platforms.rst: @GENERATE_DOCS_TRUE@ rm -f $(srcdir)/arm/platforms.rst @GENERATE_DOCS_TRUE@ cp $(srcdir)/../../platforms.rst $(srcdir)/arm/platforms.rst # UML files @GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@.uml.png: @GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@ @PLANTUML@ $< @GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@.uml.svg: @GENERATE_DOCS_TRUE@@HAVE_PLANTUML_TRUE@ @PLANTUML@ -svg $< @GENERATE_DOCS_TRUE@pdf: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst @GENERATE_DOCS_TRUE@ $(SPHINXBUILD) -M latex $(srcdir) $(sphinxbuilddir) $(sphinxopts) @GENERATE_DOCS_TRUE@ -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-arm.tex @GENERATE_DOCS_TRUE@ -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-arm.tex @GENERATE_DOCS_TRUE@ -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-arm.tex @GENERATE_DOCS_TRUE@ -cd $(abs_sphinxbuilddir)/latex && makeindex -s python.ist kea-arm.idx @GENERATE_DOCS_TRUE@ -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-arm.tex @GENERATE_DOCS_TRUE@ cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-arm.tex @GENERATE_DOCS_TRUE@ -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-messages.tex @GENERATE_DOCS_TRUE@ -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-messages.tex @GENERATE_DOCS_TRUE@ -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-messages.tex @GENERATE_DOCS_TRUE@ -cd $(abs_sphinxbuilddir)/latex && makeindex -s python.ist kea-messages.idx @GENERATE_DOCS_TRUE@ -cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-messages.tex @GENERATE_DOCS_TRUE@ cd $(abs_sphinxbuilddir)/latex && $(PDFLATEX_AND_OPTS) kea-messages.tex @GENERATE_DOCS_TRUE@html: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst @GENERATE_DOCS_TRUE@ $(SPHINXBUILD) -M html $(srcdir) $(sphinxbuilddir) $(sphinxopts) # This target is not used anywhere, but people who prefer single page docs # can do make -C doc/sphinx singlehtml and then enjoy their docs being # generated in doc/sphinx/_build/singlehtml @GENERATE_DOCS_TRUE@singlehtml: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst @GENERATE_DOCS_TRUE@ $(SPHINXBUILD) -M singlehtml $(srcdir) $(sphinxbuilddir) $(sphinxopts) @GENERATE_DOCS_TRUE@text: $(main_sources) api-files.txt mes-files.txt $(srcdir)/arm/platforms.rst @GENERATE_DOCS_TRUE@ $(SPHINXBUILD) -M text $(srcdir) $(sphinxbuilddir) $(sphinxopts) @GENERATE_DOCS_TRUE@$(man8s): mans @GENERATE_DOCS_TRUE@mans: $(man_sources) api-files.txt mes-files.txt @GENERATE_DOCS_TRUE@ $(SPHINXBUILD) -M man $(srcdir) $(sphinxbuilddir) $(sphinxopts) @GENERATE_DOCS_TRUE@clean-local: @GENERATE_DOCS_TRUE@ rm -rf $(sphinxbuilddir) @GENERATE_DOCS_TRUE@ rm -f $(srcdir)/mes-files.txt $(srcdir)/api-files.txt @GENERATE_DOCS_TRUE@ rm -f $(srcdir)/kea-messages.rst $(srcdir)/api.rst @GENERATE_DOCS_TRUE@ rm -f $(srcdir)/arm/platforms.rst @GENERATE_DOCS_TRUE@.PHONY: all pdf html mans # install and uninstall can occur with GENERATE_DOCS and without it # so we want to install all when GENERATE_DOCS is and # just mans when GENERATE_DOCS is not used, and when man files exists (e.g release tarball) install-data-local: mkdir -p $(DESTDIR)$(docdir) @GENERATE_DOCS_TRUE@ cp -r $(sphinxbuilddir)/html $(DESTDIR)$(docdir) @GENERATE_DOCS_TRUE@@HAVE_PDFLATEX_TRUE@ ${INSTALL_DATA} $(sphinxbuilddir)/latex/kea-arm.pdf $(DESTDIR)$(docdir) @GENERATE_DOCS_TRUE@@HAVE_PDFLATEX_TRUE@ ${INSTALL_DATA} $(sphinxbuilddir)/latex/kea-messages.pdf $(DESTDIR)$(docdir) @GENERATE_DOCS_TRUE@ ${MKDIR_P} ${DESTDIR}${mandir}/man8 @GENERATE_DOCS_TRUE@ ${INSTALL_DATA} $(man8s) ${DESTDIR}${mandir}/man8/ @GENERATE_DOCS_FALSE@@INSTALL_MANS_TRUE@ ${MKDIR_P} ${DESTDIR}${mandir}/man8 @GENERATE_DOCS_FALSE@@INSTALL_MANS_TRUE@ ${INSTALL_DATA} $(sphinxbuilddir)/man/*.8 ${DESTDIR}${mandir}/man8/ uninstall-local: rm -rf $(DESTDIR)$(docdir) # There are sometimes conflicts when more then one sphinx-build is run at a time. # This target blocks running anything in parallel in this Makefile, # all is run serially. .NOTPARALLEL: # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: kea-2.0.2/doc/sphinx/mes-files.txt0000644000175000017500000000203014206773363013705 00000000000000src/hooks/dhcp/flex_option/flex_option_messages.mes src/hooks/dhcp/bootp/bootp_messages.mes src/hooks/dhcp/mysql_cb/mysql_cb_messages.mes src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes src/hooks/dhcp/high_availability/ha_messages.mes src/hooks/dhcp/stat_cmds/stat_cmds_messages.mes src/hooks/dhcp/user_chk/user_chk_messages.mes src/lib/config/config_messages.mes src/lib/hooks/hooks_messages.mes src/lib/dhcpsrv/dhcpsrv_messages.mes src/lib/dhcpsrv/alloc_engine_messages.mes src/lib/dhcpsrv/hosts_messages.mes src/lib/http/auth_messages.mes src/lib/http/http_messages.mes src/lib/dhcp_ddns/dhcp_ddns_messages.mes src/lib/database/db_messages.mes src/lib/log/log_messages.mes src/lib/log/logimpl_messages.mes src/lib/log/tests/log_test_messages.mes src/lib/process/process_messages.mes src/lib/asiodns/asiodns_messages.mes src/lib/eval/eval_messages.mes src/lib/d2srv/d2_messages.mes src/bin/dhcp4/dhcp4_messages.mes src/bin/agent/ca_messages.mes src/bin/dhcp6/dhcp6_messages.mes src/bin/lfc/lfc_messages.mes src/bin/netconf/netconf_messages.mes kea-2.0.2/doc/devel/0000755000175000017500000000000014206773520011126 500000000000000kea-2.0.2/doc/devel/Doxyfile0000644000175000017500000034133514206773363012572 00000000000000# Doxyfile 1.9.1 # 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 configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = Kea # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = ../images/kea-logo-100x70.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = html # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 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 = YES # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, 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 # The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all generated output in the proper direction. # Possible values are: None, LTR, RTL and Context. # The default value is: None. OUTPUT_TEXT_DIRECTION = None # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = NO # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES # If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # By default Python docstrings are displayed as preformatted text and doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the # doxygen's special commands can be used and the contents of the docstring # documentation blocks is shown as doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\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 (in the resulting output). You can put ^^ in the value part of an # alias to insert a newline as if a physical newline was in the original file. # When you need a literal { or } or , in the value part of an alias you have to # escape them by means of a backslash (\), this can lead to conflicts with the # commands \{ and \} for these it is advised to use the version @{ and @} or use # a double escape (\\{ and \\}) ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 5 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 # The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, # which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If this flag is set to YES, the name of an unnamed parameter in a declaration # will be determined by the corresponding definition. By default unnamed # parameters remain unnamed in the output. # The default value is: YES. RESOLVE_UNNAMED_PARAMS = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # With the correct setting of option CASE_SENSE_NAMES doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that # are not case sensitive the option should be be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On # Windows (including Cygwin) and MacOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. # 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 HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = YES # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = YES # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = YES # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \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 https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as 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. If # EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # at the end of the doxygen process doxygen will return with a non-zero status. # Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # 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. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = ../../src/bin/agent \ ../../src/bin/d2 \ ../../src/bin/dhcp4 \ ../../src/bin/dhcp6 \ ../../src/bin/lfc \ ../../src/bin/netconf \ ../../src/bin/perfdhcp \ ../../src/hooks/dhcp/bootp \ ../../src/hooks/dhcp/flex_option \ ../../src/hooks/dhcp/high_availability \ ../../src/hooks/dhcp/lease_cmds \ ../../src/hooks/dhcp/stat_cmds \ ../../src/hooks/dhcp/user_chk \ ../../src/lib/asiodns \ ../../src/lib/asiolink \ ../../src/lib/cc \ ../../src/lib/cfgrpt \ ../../src/lib/config \ ../../src/lib/config_backend \ ../../src/lib/cql \ ../../src/lib/cryptolink \ ../../src/lib/d2srv \ ../../src/lib/database \ ../../src/lib/dhcp \ ../../src/lib/dhcp_ddns \ ../../src/lib/dhcpsrv \ ../../src/lib/dhcpsrv/parsers \ ../../src/lib/dhcpsrv/benchmarks \ ../../src/lib/dns \ ../../src/lib/eval \ ../../src/lib/exceptions \ ../../src/lib/hooks \ ../../src/lib/http \ ../../src/lib/log \ ../../src/lib/log/compiler \ ../../src/lib/log/interprocess \ ../../src/lib/mysql \ ../../src/lib/pgsql \ ../../src/lib/process \ ../../src/lib/stats \ ../../src/lib/testutils \ ../../src/lib/util \ ../../src/lib/util/encode \ ../../src/lib/util/io \ ../../src/lib/util/unittests \ ../../src/lib/yang \ . # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. # 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. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.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 (to be provided as doxygen C comment), # *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, # *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.cc \ *.h \ *.hpp \ *.dox # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = ../../src/lib/dns/master_lexer.cc \ ../../src/lib/dns/rdataclass.cc \ ../../src/lib/eval/lexer.cc # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = */*-placeholder.* \ */cpp/*.py # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # 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 = ../images \ ../../src/lib/hooks/images \ ../../src/bin/d2/images \ ../../src/lib/dhcpsrv/images # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # 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 = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # 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 # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use 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 YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = YES # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To # create a documentation set, doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag 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: # https://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 main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path # including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 180 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. # Possible values are: png (the default) and svg (looks nicer but requires the # pdf2svg or inkscape tool). # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANSPARENT 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 # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # 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/v2.7-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 https://www.mathjax.org before deployment. # The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # 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/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /

", " }", " ],", " \"remote\": {", " ", " }", " }", "}" ], "hook": "cb_cmds", "name": "remote-option4-pool-set", "resp-syntax": [ "{", " \"result\": 0,", " \"text\": \"DHCPv4 option successfully set.\",", " \"arguments\": {", " \"options\": [", " {", " \"code\":